Linqで文字のバイト単位でグループ分け

var input = "e4bba5e4b88be381aee69687e5ad97e58897e381af5554462d38e38292e69687e5ad97e382a8e383b3e382b3e383bce38387e382a3e383b3e382b0e5bda2e5bc8fe381a8e38199e3828b3136e980b2e695b0e381aee38390e382a4e38388e58897e381a7e38182e3828be38082";
var count = 0; // 外部にカウント用フラグ変数を置く
var words = Regex.Matches(input, "..")
    .Cast<Match>()
    .Select(m => Convert.ToByte(m.Value, 16))
    .Select(b => Convert.ToString(b, 2).PadLeft(8, '0'))
    .ToLookup(s => (s.StartsWith("10")) ? count : ++count);
Console.WriteLine(words.Count);

前回(たった10時間前ですが)の続き。16進数バイト列で表現されたUTF-8の文章の文字数を数えろ、というお話でした。せっかくなのでビット見てうんたらかんたら、のほうでも。律義に2進数に変換しながら分類。例えばwords[0]は[11100100,10111011,10100101]になる。こんな感じで41個分グルーピングされています。.Keyで何文字目かが取得出来るところが地味に素敵でいて微妙に混乱を招く(配列の何番目、ではない)。 で、まあしかし、場外にフラグ変数を置くことで何でもこなせるけれど、それってアリなの?という疑問はある。Linq連鎖の場外に変数を置くなんてグローバル変数的な害悪です。かどうかは分からないけれど、イマイチだとは思う。

場外に置かないで分類するとしたら、どうすればよいかなー。うーん。思い浮かばない。

var text = words.Select(g => g.Select(s => Convert.ToByte(s, 2)).ToArray())
    .Select(bytes => Encoding.UTF8.GetString(bytes))
    .Aggregate(new StringBuilder(), (sb, s) => sb.Append(s))
    .ToString();
Console.WriteLine(text);

こっちはどうでもいいんですが、文章への復元はこんな感じで。面倒くさー。何個Select使えば気が済むんだ、みたいなみたいな。でもLinq使わないとそれこそもっと面倒くさい気はする。とりあえず=>かわいい。=>のかわいさは異常。=>がかわいいすぎて萌え死ぬ。

Comment (2)

NyaRuRu : (06/26 02:08)

>場外に置かないで分類するとしたら、どうすればよいかなー。

末尾再帰みたいに,先頭から後段に向けて状態を渡していくといいんじゃないですかね.
とりあえずシンプルに
 一文字読む→読み飛ばし数を決める,
の繰り返しで書くとすると,

// 全角スペースでインデントしているので注意
static int GetNumberOfChars(string input)
{
  // テーブル作成部は略
  var numSkip = new Dictionary
  {
    {’0′, 1}, // 0000
    {’1′, 1}, // 0001
    {’2′, 1}, // 0010
    {’3′, 1}, // 0011
    {’4′, 1}, // 0100
    {’5′, 1}, // 0101
    {’6′, 1}, // 0110
    {’7′, 1}, // 0111
    {’c', 3}, // 1100
    {’d', 3}, // 1101
    {’e', 5}, // 1110
  };
  return
  input.Aggregate(new { NumChar = 0, Skip = 0 },
          (state, c) => state.Skip == 0 ?
            new { NumChar = state.NumChar + 1, Skip = numSkip[c] } :
            new { NumChar = state.NumChar, Skip = state.Skip - 1}).NumChar;
}

ちなみに Aggregate の代わりに Unfold を使えば lazy になるっす.

neuecc : (06/30 03:59)

そういえば以前にも前の値が取りたいとかかんとかやった覚えが、進歩のない私……。

Aggregateでカウンタをこねこねしていくのが面白い!
Scanのほうも併せて、色々考えられそう、と思って考えていたのですが
実際問題が出てこないと何も浮かばずorz.

Name
WebSite(option)
Comment

Trackback(0) | http://neue.cc/2009/06/25_172.html/trackback

Search/Archive

Category

Profile


Yoshifumi Kawai
Microsoft MVP for Developer Technologies(C#)

April 2011
|
July 2021

Twitter:@neuecc
GitHub:neuecc
ils@neue.cc