コーディングガイドラインの車輪を再発明

正直、「好きだね、決めるのが」というところなのだが、コーディングガイドラインについての定義が多いなあと思う。
先日、CodeProjectで、またもやその一つが投稿され、コメントが炎上(?)していた。

Some practices to write better C#/.NET code

この中に何点か、自分的に納得いかないプラクティスがある。

  • Capitalization Conventions – プロパティはともかく、フィールドはプライベートもPascalなのか?自分的にはこうだ:moneyRemains_ – これは、ANSI C/C++において、安全とみなせるシンボル表記だ。但し、アンダースコアが後ろに続く表記はあまり見たことが無いかもしれない。自分のように、C++、C#のどちらも頻繁に書く場合、どちらでも通用する書き方にしたい。但し、それはシンボルが公開インターフェイスでなければ、の話だ。この記事ではないが、進行中のプロジェクトでは、readonlyフィールドが全て大文字という縛りがあって、かなり違和感がある。ここだけ単語の区切りが分かりにくいので、アンダースコアで区切ることになる(DIEING_DICTIONARYのような)。すると、確かに目立つは目立つが、まるでconstかと主張しているようだ(constではなくreadonlyなのだが)。どうも、readonlyインスタンスとconstの決定的な違いを分からず決めたように感じる。
  • DO declare all member variables at the top of a class, with static variables at the very top. – たのむからプロパティをコンストラクタより上に持ってこないでくれ。フィールドとプロパティを同一のように扱うと罠にはまる事があるから、見てすぐにわかるようにしておきたい。
  • DO NOT suffix enum names with Enum – これは同意だが、できれば列挙型名は複数形にして欲しいな。列挙型を定義してTlbExpでタイプライブラリを出力、C++側でインポートした時とか、不自由しなくなる。まれなケースだけど、まれなケースの時だけルールを変えるぐらいなら、複数形にする事に負担があるわけじゃないからやればいい。
  • Avoid Obsolete Comments – これはかなり炎上していたな。前提としては、コメントを「書かなくても済むようにする(シンプルで意味が明瞭な実装を心がける)」ということに賛成だが、不可避な場合がある。結局、突き詰めていくと、コーディングの問題じゃなくて、大枠の設計(ユーセージモデルとか)が問題で複雑化してしまっていたりするので、どうしてもコードが複雑にならざるを得ない事がある。そのような場合に、将来自分でこのコードを見たときに、昔自分が何を考えてこのように実装したかを書いておくためにコメントを使っている。これを書いておかないと(自分でも)理解不能ではないだろうかと思うような場所で、かつ改善できないような痛い場所だ。しかし、出来れば設計からやり直させたいね。
  • Avoid Multiple Exit Points – これは絶対拒否だ。一体これをやりたがるのは何故だ?CPUにはプログラムカウンタ(インストラクションポインタ)とスタックというものがあり、これらの情報で一種の仮想的な「ハードウェアステートマシン」を実現しているのだ。条件分岐のブロック内に居るという「事実」が、この情報だけで表現されているのに、何故手でステートを管理したがるのか。ステート変数を導入すれば、それがいつどこでどのように変更され、どのような場所や場合においても正しくふるまっているかどうかを管理しなければならなくなる。C++でRAIIが当たり前に使われているのは、こうしたステート変数を避けるためだ。これ以上、処理の必要がない分岐ルートでは、速やかにメソッドから退出(return)する。そうすることで、余計な分岐ルートを削減できる。分岐ルートを削減できれば、パターン網羅のためのテストコードも少なくて済む。メソッドが下に落ちていくほどルートはふるいにかけられ、終盤ではそこに居るための「暗黙条件」は絞られるはずだ。結果として、コードはよりシンプルになる。

思うに、こういう事は意外かもしれないが、「ルールを決めて徹底させる」事が諸悪の元凶に思う。たとえば、重厚な開発プロセスを導入する事は、そこから緩慢な死へと向かい、より良い未来に前進することはない。プロセスを定義するという事は、「考えなくても出来る」ことを目指すため、例外なく「思考停止」し、本当は何が問題でどうしようとしていたのかが置き去りにされてしまう。常に最善とは何かを考えるためには、プロセスは邪魔なのだ。

もっとも、多人数で作業を進めるために、何らかの決まり事は必要となる。重要なのは、それが常に陳腐化する可能性があり、いつでもすぐに見直し、正しい方向に修正しなければならないことをメンバー全員が周知していなければならない事だ。加えて、業務を遂行するために、いつでもルールが変更可能でなければならない。何らかのプロセスを導入していると、「こうするのがルールだから仕方ないよね」というところから始まって、なし崩し的に開発効率が落ちていく。終いには、何が悪くてこうなってしまったのかすら、誰もわからないという寒い状況に陥る。

上記に示したルールは、私が業務を進めるにあたって、C++/C#の両刀使いである前提でのルールも含まれている。CodeProjectの元記事や私のルールを鵜呑みにせず、積極的にルールをぶち壊し、再発明してほしいものだ。

Windows Server 2012 HYPER-V の嬉しい機能

ここ一週間、Windows Server 2008R2 のサーバーがディスク障害で逝かれて大変だった。

RAID1+毎日バックアップでハードウェア障害の問題は無いのだが、問題はバックアップの内容だった。HYPER-VのゲストOSに、物理ディスクを直接接続していると、保存状態(バックアップ時にVSSがVMを保存状態にする)のVMを復元できなくなる。

もちろん、完全に同一のハードウェアを用意すれば復元できるのだろうが、障害時には決まってそういう事が期待できないものだ。結局、個々のファイルとしてVMイメージは復元できるのだが、どうやっても新しい環境では認識させる事が出来ず、DCはお亡くなりになった(DCって言っても自分だけで使っているような物だからまだ良いのだが)。

たまたま、業務にクリティカルなVMはそういうしがらみが無かったので復元出来たので、駄目だったVMは完全に放棄し、Windows8の事もあって、HYPER-Vホスト・DC等を2012にアップグレードした。

で、こんなことは二度と御免なので、耐障害性を考えてVMを再編することにしたのだが、調べているうちに、2012のHYPER-VはエクスポートしていないVMイメージもインポート可能になったそうで、感涙やらショックやら。2008R2で出来ていれば!!!

Virtual Server 2005やVirtual PCでは出来てたんだよな。ファイルコピーのバックアップが。

これで、安心してWindows Serverバックアップで「ベアメタル回復」が使えそうだ。これの検証もしておく必要があるな。

LINQは本当に強力だ (8) 何が並列化されるのか

並列化が簡単に出来る事がLINQのアドバンテージの一つなのだが、AsParallelを付ける際に、クエリのどこがどのように並列化されるのかが見えているだろうか?
AsParallelを付ければ並列化出来るというのは簡単すぎるため、意味のない適用や実害が生じる可能性もある。たとえば、以下のコードを考えてみる。

// 重複しない乱数列を生成する
var rand = new Random();
var captured = new ConcurrentDictionary<int, int>();
var results =
    from index in
        Enumerable.Range(0, 100000)
    let value = rand.Next(200000)
    where captured.TryAdd(value, index) // 辞書に値の追加を試み、成功すればtrue
    select value;

少し脱線するが、ConcurrentDictionaryはDictionaryのスレッドセーフバージョンだ。しかも、ロック競合が起きにくく、かつアトミック操作が可能なメソッドが追加されており、上記のようにLINQでも使いやすい(DictionaryクラスはTryGetValueがoutパラメータを使うので、LINQでは使いにくかった)。

さて、このコードは小さいので並列化にはあまり向いていないが、それは横に置いておき、並列化したいのはどこなのかを考える。
やろうとしていることは、10万回分の乱数を取得し、その中から重複する値を避けた乱数列の取得だ。もちろん、Distinctを使えばいいのだが、それも横に置いておく。
生成した乱数(value)を辞書に追加し、成功すれば(つまり辞書にその値が無く、追加された場合)に、その値がselectで射影される。この、乱数の生成と辞書への追加の試行部分を並列化出来れば、大半の処理が並列化出来たことになる。
クエリ構文で書いていることもあり、AsParallelを挿入すべき場所は一か所しかないが、取りあえず挿入する。

var rand = new Random();
var captured = new ConcurrentDictionary<int, int>();
var results =
    from index in
        Enumerable.Range(0, 100000).
        AsParallel()    // 並列化
    let value = rand.Next(200000)
    where captured.TryAdd(value, index)
    select value;

LINQ(とPLINQ)を書き始めて間もないと、このAsParallelによって何が並列化されるのか、誤解する事が多いようだ。上記の例では、10万までの数値を生成する「Range」が並列化され、それ以降の乱数生成や判定が並列化されることも期待しつつも、本当に並列化されるのか自信が持てないらしい。
(いや、自分も最初はそうだったから、そういうものだと思う :-)

最初に書いた通り、良くも悪くもAsParallelを付けるだけでよいというのが、この誤解の主原因なのだろう。
まず、クエリ構文をメソッド構文にする。

var rand = new Random();
var captured = new ConcurrentDictionary<int, int>();
var results =
    Enumerable.Range(0, 100000).
    AsParallel().
    Select(delegate(index)
        {
            return new
            {
                value = rand.Next(200000),
                index = index
            };
        }).
    Where(delegate(entry)
        {
            return captured.TryAdd(entry.value, entry.index)
        }).
    Select(delegate(entry)
        {
            return entry.value;
        });

意味もなくletを使ってしまったので面倒な事になっているが、前回見せたとおりindexとvalueを射影しているだけだ。肝心のAsParallelは、letを射影するSelectの手前に入っている。Range→AsParallel→Select→Where→Selectと、パイプライン結合されているのが分かる。

そう、パイプライン結合されているのだ、AsParallelも。

パイプライン結合を実現しているのは、IEnumerable<T>インターフェイスだ。クエリの末端(最後のSelect)が列挙されるとき、GetEnumeratorが呼び出される連鎖が起きることを述べた。AsParallelもまた、同じようにGetEnumeratorの呼び出しと、列挙の連鎖が発生する。ということは、AsParallelが動作に影響を与えられるのは、自分よりも上位のRangeだけ、と言う事になる。

え?そうなの?
いやいや、実はこれが判りにくい原因ではないかと思う。AsParallelよりも下位のパイプライン結合が並列化されるのだ。つまり、乱数の生成と辞書への追加が、期待通り並列化される。Rangeによるindexの生成は並列化「されない」。
どうしてこのようになるのだろうか?ここにクエリ構文を使いすぎたり、varを使いすぎたりする弊害がある。上記のコードをばらばらにし、.NET2.0的にしてみる。

IEnumerable<int> results0 =
    Enumerable.Range(0, 100000);

ParallelQuery<int> results1 =
    results0.AsParallel();

ParallelQuery<Tuple<int, int>> results2 =
    results1.Select(delegate(index)
        {
            return Tuple.Create(rand.Next(200000), index);
        });

ParallelQuery<Tuple<int, int>> results3 =
    results2.Where(delegate(entry)
        {
            return captured.TryAdd(entry.Item1, entry.Item2);
        });

ParallelQuery results4 =
    results3.Select(delegate(entry)
        {
            return entry.Item1;
        });

匿名クラスは表現できないので、Tupleに置き換えてある。AsParallelの戻り値の型は、実はIEnumerable<T>ではない。ParallelQuery<T>型なのだ。但し、ParallelQuery<T>はIEnumerable<T>を実装しているので、これを直接foreachなどで列挙することは可能だ。つまり、今まで通りIEnumerable<T>が返されると思っていても、表面上の違いは無いということだ。

しかし、このコードを見れば、なんとなく並列化される範囲が見えてくると思う。ParallelQueryによって管理されているクエリが並列化される。少し不思議なのは、IEnumerable<T>に対してSelectやWhereを呼び出した場合はIEnumerable<T>が返されるのに、ParallelQuery<T>に対してSelectやWhereを呼び出した場合は、ParallelQuery<T>が返されることだ。

これもそれほど大げさな仕掛けではない。IEnumerable<T>の場合は、SelectやWhereといった拡張メソッドは「Enumerable」クラスに定義されている。ParallelQuery<T>に対してSelect・Whereした場合は、「ParallelEnumerable」クラスの拡張メソッドが使用されるのだ。C#のコンパイラが、SelectやWhereメソッドの第一引数の型にもっとも一致する拡張メソッドを自動的に選択するため、この仕掛けが機能する。まるで、メソッドのオーバーライドを実装しているかのようだ。

では、いったいどこで並列実行のからくりが実現されるのだろうか? ParallelQuery(ParallelEnumerable)の実装は複雑だが、基本的な考え方は単純だ。ParallelQuery<T>の最下位でGetEnumeratorが呼び出されたときに、並列化が行われる。

IEnumerable<int> results0 =
    Enumerable.Range(0, 100000);

ParallelQuery<int> results1 =
    results0.AsParallel();

ParallelQuery<Tuple<int, int>> results2 =
    results1.Select(delegate(index)
        {
            return Tuple.Create(rand.Next(200000), index);
        });

ParallelQuery<Tuple<int, int>> results3 =
    results2.Where(delegate(entry)
        {
            return captured.TryAdd(entry.Item1, entry.Item2);
        });

ParallelQuery<int> results4 =
    results3.Select(delegate(entry)
        {
            return entry.Item1;
        });

// ParallelQuery.GetEnumerator()が呼び出される
foreach (var value in results4)
{
    Console.WriteLine(value);
}

最下位でGetEnumeratorが呼び出されると、スレッドプールからいくらかのスレッドを割り当て、各スレッドがParallelEnumerableの拡張メソッドで定義された演算を実行する。この部分は、従来のGetEnumeratorによる結合では実行できない。何故なら、IEnumeratorインターフェイスはマルチスレッドに対応していないからだ。必然的に、パイプラインで並列化演算が可能なのは、それぞれの演算が専用に設計された、ParallelEnumerableに定義された拡張メソッド群だけ、ということになる。

(もちろん、それらの拡張メソッドから呼び出されるデリゲートの実装は、いくらでも拡張可能だ。SelectやWhereのデリゲートの実装は、使う側が記述するのだから。)


AsParallelの実装とか、ParallelQueryEnumeratorの実装に興味がわくかもしれない。この部分はスレッドの割り当てやデータの分散と集約など、実際にはかなり複雑になっていると思われる。

しかし、注目する点はその部分ではなく :-) 、各ワーカースレッドが並列実行している演算の部分だ。Select→Where→Selectの部分が、スレッド毎に分散されている。パイプラインでAsParallelを適用してから、foreachで列挙されるまでに結合された演算子が並列実行される事が分かる。
そして、(当たり前ではあるが)foreachによるGetEnumeratorの呼び出しの後(foreachループ)は、並列化されていない。Console.WriteLineが並列実行されるわけではない事も分かる。ここが並列実行されたとすると、記述したとおりに実行されないわけだから、C#の構文的にも変だ。
また、AsParallelの上位も並列化されない。こちらも、IEnumeratorインターフェイスの構造に阻まれて、並列化させることはできない。

結局のところ、IEnumerable.GetEnumeratorの呼び出しによって、並列化の「壁」が出来上がるわけだ。私はこれを「ゲート」と勝手に呼んでいる。PLINQクエリが入り混じる場合、このゲートを意図的に作ってやることで、並列化されるクエリの範囲を自由にコントロールできる。

IEnumerable.GetEnumeratorを明示的に呼び出させる事が出来れば、このゲートを作ったことになる。つまり:

IEnumerable<int> results0 =
    Enumerable.Range(0, 100000);

ParallelQuery<int> results1 =
    results0.AsParallel();

ParallelQuery<Tuple<int, int>> results2 =
    results1.Select(delegate(index)
        {
            return Tuple.Create(rand.Next(200000), index);
        });

// results2の直後にゲート生成
IEnumerable<Tuple<int, int>> results3 =
    ((IEnumerable<Tuple<int, int>>)results2).Where(delegate(entry)
        {
            return captured.TryAdd(entry.Item1, entry.Item2);
        });

IEnumerable<int> results4 =
    results3.Select(delegate(entry)
        {
            return entry.Item1;
        });

ということだ。こうすれば、results2だけが並列化の対象となる。

で、キャストは面倒であるため、この目的のための「AsEnumerable」という拡張メソッドがある。内部の実装はキャストしているだけだ。PLINQ向けには「AsSequential」もあるが、これはAsEnumerableと全く変わらない。AsParallelの逆演算子のイメージで導入されたのだと思う。

LINQは本当に強力だ (7) 範囲変数と構文の選択

LINQのクエリ構文には、範囲変数と呼ばれている機能がある。
たとえば、文字列で与えられた半径と円周を対にしてみる。

string[] values = new string[] { "234.56", "456.78", "345.67", "123.45" };
IEnumerable<Tuple<double, double>> results =
    from value in values
    select Tuple.Create(double.Parse(value), double.Parse(value) * 2 * Math.PI);

これで正しい結果が得られるのだが、文字列の数値をdoubleにパースする操作は2回行われる。
CLRがとても賢くなったとしたら、double.Parseに副作用が存在しないことをJITで確認して、1回の呼び出しに変換するかもしれないが、あまり期待できない。
そこで、普通ならパースした結果をローカル変数に入れておき、使いまわしたりするのだが、このような目的のために、LINQには「範囲変数」が存在する。

string[] values = new string[] { "234.56", "456.78", "345.67", "123.45" };
IEnumerable<Tuple<double, double>> results =
    from value in values
    let parsed = double.Parse(value)    // 範囲変数parsedに代入
    select Tuple.Create(parsed, parsed * 2 * Math.PI);

これなら確実に1回だけパースを行う。その結果を、範囲変数”parsed”に代入し、後で何度でも使いまわす事が出来る。
そういう訳で、このletは実に使い出がある。というより、これが無いとかなり困ったことになる。
ここで、新たな「let」という予約語(厳密には予約語ではないが、ここでは詳しく述べない)が登場するのだが、最初に見たときは「何でvarにしなかったんだ?」と思った。

もはやvarについて知らない人はいないと思うが、

// 右辺から、文字列の配列って分かるよね?
var values = new string[] { "234.56", "456.78", "345.67", "123.45" };
// 右辺から、Tuple<double, double>の列挙子って分かるよね?
var results =
    from value in values
    let parsed = double.Parse(value)
    select Tuple.Create(parsed, parsed * 2 * Math.PI);

というように書き直せる。右辺の結果の型が明確であれば、左辺にわざわざ型名を書かなくても良いでしょうというのが一点。もう一点は連載でもちらりと述べたが、匿名クラスをローカル変数に保持するのに、型名が書けないからという理由だ。

さて、やっとvarについて書けた。以後は注目すべき点がない場合はvarで書く :-)
varの意味づけがイメージ出来るように、実際に匿名クラスを使ってみようか。

var values = new string[] { "234.56", "456.78", "345.67", "123.45" };
// 型名が無いから、書きようがない
var results =
    from value in values
    let parsed = double.Parse(value)
    select new  // 匿名クラスだから、型名はない
    {
        R = parsed,
        Length = parsed * 2 * Math.PI
    };

上記の「results」は、varでしか宣言出来ない。故にvarという予約語が必要になった訳だ。
で、範囲変数であるletも事情は同じだ。場合によっては、型名が書けない事がある。それならば、letという予約語を作るよりもvarが使えたほうが、覚える予約語が減って良いのではないか?

var values = new string[] { "234.56", "456.78", "345.67", "123.45" };
var results =
    from value in values
    var parsed = double.Parse(value)    // varでいいじゃん?(もちろんNG)
    select new
    {
        R = parsed,
        Length = parsed * 2 * Math.PI
    };

何故letが導入されたのかは明らかではないが、私が感じた理由を示そうと思う。
まず、上のコードのアウトプットを少し増やす。

var values = new string[] { "234.56", "456.78", "345.67", "123.45" };
var rand = new Random();
var results =
    from value in values
    let parsed = double.Parse(value)
    let randomValue = rand.Next((int)parsed)    // 乱数を生成する
    select new
    {
        R = parsed,
        Length = parsed * 2 * Math.PI,
        Rand = randomValue
    };

ちょっと強引な例だが、パースした数値の範囲の乱数を加えた。見ての通り、あらかじめ代入した範囲変数parsedを使って、別の範囲変数を生成することも出来る。この辺りは、通常のローカル変数の感覚と全く変わらない。次のようなコードを書くまでは:

var values = new string[] { "234.56", "456.78", "345.67", "123.45" };
var rand = new Random();
var results =
    from value in values
    let parsed = double.Parse(value)
    let randomValue = rand.Next((int)parsed)
    orderby parsed  // ちょっとソートしてみようか。
    select new
    {
        R = parsed,
        Length = parsed * 2 * Math.PI,
        Rand = randomValue
    };

私は、上記のselectまで書いたところで、「凍り付いた」。意味が分かるだろうか?
parsedの値で昇順にソートするために、orderbyを書いた。ここまでは良いのだが、その下のselectが一体どうなってしまうのか、非常に不安になった。もちろん、parsedは昇順に並び替えられた順序でやってくる(select句はSelect拡張メソッドに対応している。OrderByの列挙子で順番に値が渡される事を思い出そう)。
では、randomValueはどうなのか?

上のクエリ構文を見ていると、randomValueはorderbyとは関係がないように見える。実際、その下のselect句でrandomValueをそのまま使っている。と言うことは?ソートされるのはparsedの値だけで、randomValueはソートされずに渡されるのか?すると、組み合わせるべき結果がメチャメチャになってしまうのではないか?
(いやいやいや、GetEnumeratorで渡されるというインフラを無視して、ワープしたかのように別のルートで値を渡すなど不可能、不可能なハズだ…?)

もし、let句が無く、varと「書けた」としたら、益々この罠に掛かったことだろう。

翌々書いておく。「ローカル変数」と「範囲変数」は異なるのだ。異なるのだ。異なるのだ。
上記のクエリ構文をメソッド構文に置き換える事が出来るだろうか? この罠に掛かってからは、メソッド構文のイメージが全く掴めなかった。結局、ILSpyでコードを確認して、以下のように変換されることが分かった。

var values = new string[] { "234.56", "456.78", "345.67", "123.45" };
var rand = new Random();
var results =
    values.
    Select(delegate(value)
        {
            var parsed = double.Parse(value);
            return new  // 不可視の匿名クラス
            {
                parsed = parsed,    // let parsedに対応
                randomValue = rand.Next((int)parsed)    // let randomValueに対応
            };
        }).
    OrderBy(delegate(entry)
        {
            return entry.parsed;
        }).
    Select(delegate(entry)
        {
            return new
            {
                R = entry.parsed,
                Length = entry.parsed * 2 * Math.PI,
                Rand = entry.randomValue
            };
        });

letのあるべき部分に、上記のような不可視の匿名クラスが作られ、Selectで一旦射影される。つまり、parsedとrandomValueはその時点の値がセットとして扱われる。だから、直後のOrderByでソートされても、parsedとrandomValueが誤った組み合わせになる事は無い。最後のSelectまで、この組み合わせは維持される。

letとletの間にwhereを入れたりした場合でも、新たな不可視の匿名クラスが作られ、Selectで射影される。分離されたletが存在するだけ、匿名クラスが増えることになる。これはつまり、分離されたletが沢山あるほど、匿名クラスの種類が膨れ上がると言う事だ。まあ、それはまだいい(コンパイラが勝手にやっていることだから)が、その度にnewされるというのは気持ちの良いことではない。この匿名クラスの生成とnewはかなりパターン化していると思われるので、ひょっとするとJITは何か工夫するかもしれない。

実際、範囲変数を正しく振る舞わせる為には、上記のようにするしかないだろう。で、メソッド構文が最も正確にLINQを表現できるとしても、上記のようなコードを書くのは面倒だ。やはりletが使えるクエリ構文が手放せない。私はいつも直感で書くのであまり注意していなかったが、自分でパターンを考えてみると、基本はクエリ構文で書き、letが不要な場合にはメソッド構文を選択しているようだ。

しかし、クエリ構文で書くデメリットもある。以前に述べた拡張メソッドとの相性の問題もあるが、クエリに問題があった場合に追跡が面倒だ。メソッド構文で書いていると、適当な場所でパイプラインをぶった切って、ToList()をかますことで、そこに流れたデータを直接デバッガで可視化出来る(データ量が多いとこの方法は使えないが、デバッグ時だから与えるデータを工夫すればいい)。

クエリ構文だと「ぶった切る」事が出来ない。letを使っているとクエリが複雑化しやすい上にこの制限があるのが辛い。
将来のVisual Studioでパイプラインの流れを可視化出来る何かが追加されると、非常に強力なのだが…
#使った事は無いが、Resharperはクエリ構文をリファクタリングで分割出来るようだ。さすがに強力。

LINQは本当に強力だ (6) TextFieldContext

抽象的な話ばかり続いたので、今回は、実用的な例を示そう。
.NETでCSVファイルを読み取るとき、まさか自分でパースしたりしていないと思うが、知っていると便利なクラスが「VB.NET」のライブラリに存在する。TextFieldParserクラスだ。VB向けの実装の割には、Streamからの読み取りに対応しているなど、割としっかり作ってある。
今回はこのクラスをLINQで「楽に」使えるようにする。

public static class TextField
{
    // 指定されたCSVファイルへのコンテキストを生成する
    public static IEnumerable<string[]> Context(
        string path, string separator = ",", Encoding encoding = null)
    {
        using (Stream stream =
            new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read))
        {
            using (TextFieldParser parser =
                new TextFieldParser(stream, encoding ?? Encoding.UTF8, true, false))
            {
                parser.TextFieldType = FieldType.Delimited;
                parser.Delimiters = new[] { separator };
                parser.HasFieldsEnclosedInQuotes = true;
                parser.TrimWhiteSpace = true;
                while (parser.EndOfData == false)
                {
                    string[] fields = parser.ReadFields();
                    yield return fields;
                }
            }
        }
    }
}

このクラスのアイデアは、LINQ to Entitiesだ。Contextメソッドで一旦「コンテキスト」を作ってしまえば、面倒な事を一切考えなくても読み取りが可能になる。これでロジックから解放だ。
郵便局 郵便番号データ(全国)を使う。

static void Main(string[] args)
{
    // 郵便番号データのコンテキストを生成
    IEnumerable<string[]> context = TextField.Context(
        @"KEN_ALL.CSV", ",", Encoding.GetEncoding(932));

    // (クエリは遅延実行なので、以下の式ではまだ処理を開始していない)
    IEnumerable<string> results =
        // コンテキストからフィールド群を取得
        from fields in
            context.
            AsParallel() // 並列化
        // 郵便番号データの住所部分に「字」と「条」が含まれている行を抽出し、
        where fields[8].Contains("字") || fields[8].Contains("条")
        // 郵便番号の降順でソートし、
        orderby fields[2] descending
        // 文字列形式にフォーマットする
        select string.Format("〒{0} {1}{2}{3}", fields[2], fields[6], fields[7], fields[8]);

    // 取りあえず、結果をコンソールに出してみた(ここでGetEnumeratorが呼ばれて実行が開始される)
    foreach (string result in results)
    {
        Console.WriteLine(result);
    }
}

データ量が少ないので、並列化の恩恵はあまりないが、AsParallelしてみた。AsParallelを付けたり外したりしてみて、タスクマネージャのCPUリソースの具合を確認して見ると良い。

前回、LINQのパイプライン実行について説明したが、上記のコードの良い点は、コンテキストからデータが流れるようにやってきて、クエリで加工されて結果が出力される(コンソールに表示される)というところだ。実は、orderby句(OrderByDescending拡張メソッド)は、内部でバッファリングを行っているので、件数に応じてメモリ使用量が増加してしまうが、orderbyが無ければゴミをGCが回収するため、メモリ使用量が増え続けてしまう事はない。
(orderbyはソートを行うのだが、ソートを行うためにはすべてのデータが揃わなければならない。いくらパイプライン実行でも、不可能なことはある。その代わり、AsParallelしていれば、スレッド数に応じてソートが高速化される。)

さあ、仕上げだ。目の前にCSVファイルの山があったとしよう。

static void Main(string[] args)
{
    IEnumerable<string> results =
        from path in
            Directory.GetFiles("CSV_FILES", "*.csv", SearchOption.AllDirectories).
            AsParallel()
        from fields in
            TextField.Context(path, ",", Encoding.GetEncoding(932))
        where fields[8].Contains("字") || fields[8].Contains("条")
        orderby fields[2] descending
        select string.Format("〒{0} {1}{2}{3}", fields[2], fields[6], fields[7], fields[8]);

    foreach (string result in results)
    {
        Console.WriteLine(result);
    }
}

涙が出るほど簡単で、しかも速い。鳥肌が立つね :-)

LINQは本当に強力だ (5) クエリ構文とメソッド構文

アルゴリズムを記述する拡張メソッドは色々書いてみただろうか?

拡張メソッドを書きたくなる場合を考えると、結局のところは、いかにループロジックを書かなくて済むように出来るか?と言う所から始まるように思う。

他の例として:

  • 連続する隣り合う要素をペアにして列挙
    → [A, B, C, D, E] を [{A, B}, {B, C}, {C, D}, {D, E}] で列挙できるようにする。
    ループロジックで書くと、最後の要素を覚えておいて、次回に利用できるようにするとか、ループ回数を-1するだとか、簡単な事なのに泥臭いコードを書く必要がある。
  • STLのsort/uniqueでDistinctのような事をするのと同様の操作
    →LINQにはもちろんDistinct拡張メソッドが存在する。しかし、stableであろうとするからだろうか、異様に遅い。そこで、OrderByでソートしておいて(もちろん、PLINQで)、Unique拡張メソッドを用意し、連続する同じ要素を無視したシーケンスを返すことで、高速に一意化する事が出来る。この、連続する要素を無視するというアルゴリズムが、ループロジック的にやはり汚くなる。だから、拡張メソッド内に押し込めて、そういう事を考えなくても済むようにするわけだ。

ここまでで見てきたように、LINQには「クエリ構文」と「メソッド構文」があり、クエリ構文で記述出来ることは全てメソッド構文で記述可能で、結局のところ、LINQを支えているのは大量の拡張メソッド群となっている。

int[] datas = new int[] { 1, 2, 5, 3, 9, 7, 4 };

// クエリ構文 #1
IEnumerable<string> resultByQuery1 =
    from data in datas
    select data.ToString();

// メソッド構文 #1
IEnumerable<string> resultByMethod1 =
    datas.
    Select(delegate(int data) { return data.ToString(); });

// クエリ構文 #2
IEnumerable<string> resultByQuery2 =
    from data in datas
    orderby data descending
    select data.ToString();

// メソッド構文 #2
IEnumerable<string> resultByMethod2 =
    datas.
    OrderByDescending(delegate(int data) { return data; }).
    Select(delegate(int data) { return data.ToString(); });

// クエリ構文 #3
string[] resultByQuery3 =
    (from data in datas
     orderby data descending
     select data.ToString()).
    ToArray();

// メソッド構文 #3
string[] resultByMethod3 =
    datas.
    OrderByDescending(delegate(int data) { return data; }).
    Select(delegate(int data) { return data.ToString(); }).
    ToArray();

LINQ入門のような記事は、大方クエリ構文で紹介される。つまるところ、SQL構文がネイティブにC#でサポートされたかのように使えますよ、と。しかし、LINQのクエリ構文は、いわゆる「構文糖」なので、上記の例で見せたように、コンパイラはメソッド構文に置き換えた状態でコンパイルを行う。このメソッド構文こそが、LINQの真の姿ということだ。

最後の例では、ToArray拡張メソッドの違和感がよく表れている。メソッド構文では綺麗につながって記述されているが、クエリ構文ではfrom句からselect句まで括弧でくくり、その結果をToArrayしなければならない。括弧を使わないと、select句の一部と見なされ、違ったコードとなってしまう。
そして、メソッド構文で考えると、LINQのパイプライン実行がどのようなものかも見えてくる。

// 一行で記述
string[] resultByOne =
    datas.
    OrderByDescending(delegate(int data) { return data; }).
    Select(delegate(int data) { return data.ToString(); }).
    ToArray();

// 分割して記述
IEnumerable<int> resultStep1 =
    datas.
    OrderByDescending(delegate(int data) { return data; });

IEnumerable<string> resultStep2 =
    resultStep1.
    Select(delegate(int data) { return data.ToString(); });

string[] resultStep3 =
    resultStep2.
    ToArray();

一行で全て記述した場合と、分割して記述した場合で、(バイナリは多少変わるが)コードの効率は全く変わらない(理由は後述)。その上で、分割したそれぞれの式の結果型を見ると、IEnumerable<T>となっていることが分かる。LINQの拡張メソッドの戻り値は、殆どが列挙子で返されるようになっている。ちなみに、ToArrayで返される配列も、IEnumerable<T>を実装しているため、配列に対して更にLINQの拡張メソッドを使用する事が出来る(元々、datasは配列だ)。

この、「列挙子」がパイプライン実行を担っていて、クエリの遅延実行を可能にする。遅延実行とは何だろうか?
唐突だが、ToArrayの中身について考えてみる。ToArrayは列挙子の「遅延実行」が行われず、その場で評価される。

// ToArrayの疑似コード
public static T[] ToArray<T>(this IEnumerable<T> enumerable)
{
    List<T> results = new List<T>();
    foreach (T value in enumerable)
    {
        results.Add(value);
    }
    return results.ToArray();
}

引数で与えられた列挙子は、要素数が分からないため、List<T>を使って要素値を移し替えた(コピー)あと、配列に変換している(Listの使い方としては例が悪いが、趣旨とはずれるので勘弁)。
foreachは、実際には以下のように変換出来る。

public static T[] ToArray<T>(this IEnumerable<T> enumerable)
{
    List<T> results = new List<T>();

    // foreach (T value in enumerable)
    IEnumerator<T> enumerator = enumerable.GetEnumerator();
    while (enumerator.MoveNext() == true)
    {
        T value = enumerator.Current;
        results.Add(value);
    }
    return results.ToArray();
}

例外に対処するコードは省いた。

IEnumerable<T>から、GetEnumeratorメソッドで列挙子本体(IEnumerator<T>)を取得し、これで列挙を実行するループを形成している。resultStep2.ToArray()とした場合、resultStep2.GetEnumerator()を呼び出している事になる。resultStep2はSelectが返しているのだが、Select内部の実装はどうなっているだろうか?

// Selectの疑似コード
public static IEnumerable<U> Select<T, U>(this IEnumerable<T> enumerable, Func<T, U> predict)
{
    // foreach (T value in enumerable)
    IEnumerator<T> enumerator = enumerable.GetEnumerator();
    while (enumerator.MoveNext() == true)
    {
        T value = enumerator.Current;
        U result = predict(value);
        yield return result;
    }
}

ををを、こりゃいかん。yield returnを説明で使ってしまった。これは以下のように展開される(うぅ、面倒だ)。

public static IEnumerable<U> Select<T, U>(this IEnumerable<T> enumerable, Func<T, U> predict)
{
    // ※1
    return new SelectEnumerable<T, U>(enumerable, predict);
}

private sealed class SelectEnumerable<T, U> : IEnumerable<U>
{
    private readonly IEnumerable<T> enumerable_;
    private readonly Func<T, U> predict_;

    public SelectEnumerable(IEnumerable<T> enumerable, Func<T, U> predict)
    {
        enumerble_ = enumerable;
        predict_ = predict;
    }

    public IEnumerator<U> GetEnumerator()
    {
        // ※2
        return new SelectEnumerator<T, U>(enumerator_.GetEnumerator(), predict_);
    }
}

private sealed class SelectEnumerator<T, U> : IEnumerator<U>
{
    private readonly IEnumerator<T> enumerator_;
    private readonly Func<T, U> predict_;

    public SelectEnumerator(IEnumerator<T> enumerator, Func<T, U> predict)
    {
        enumerator_ = enumerator;
        predict_ = predict;
    }

    public U Current
    {
        get
        {
            return predict_(enumerator_.Current);
        }
    }

    public bool MoveNext()
    {
        return enumerator_.MoveNext();
    }
}

説明に不要なコードは省略している。Selectを呼び出すと、結果としてSelectEnumerableのインスタンスが返される(※1)。この時点ではまだ元の列挙子(Selectの引数。resultStep1)は操作していない。

resultStep2にはSelectEnumerableのインスタンスが入っているので、resultStep2.GetEnumerator()を呼び出すということは、SelectEnumerableのGetEnumerator()を呼び出すことだ(※2)。ここで初めてresultStep1のGetEnumeratorが呼び出され、SelectEnumeratorのインスタンスが生成されて返される。

既に忘れているかもしれないが :-) ToArrayの中身のforeach(そしてそれを展開したwhile文)は、IEnumerator<T>を取得した後、MoveNextを呼び出している。上記のMoveNextを見ると、resultStep1のMoveNextに転送しているだけだ。その後、Currentプロパティを参照する。Currentプロパティの中身は、predictデリゲートを使ってresultStep1のCurrentプロパティの値を変換して返している。変換の内容は?LINQクエリを書いた本人が定義するわけだ。
そして、その結果をList<T>に追加し、whileの先頭に戻り、またMoveNextを呼び出す。後は、上記の動作が繰り返されるわけだ。

ここでは複雑すぎるので示していないが、resultStep1へのMoveNext呼び出しも、Currentプロパティの参照も、同じような流れに従っている。

全体を通して、以下のような構造となる。

これを俯瞰して見ると、ToArrayのforeachによるループ1回で、LINQクエリのパイプラインで接続されたメソッド群を「行ったり来たり」していることが分かると思う。この動作が「パイプライン実行」の正体だ。そして、GetEnumeratorが呼び出されるまでは、接続されたクエリが全く動作していないこともわかる。これが「遅延実行」と呼ばれる動作だ。

一行で書いても分割して書いても効率は変わらないと書いた理由が分かるだろうか?遅延実行をサポートするLINQの拡張メソッドは、結局SelectEnumerableのような「クエリ条件だけ保持して何もしない」クラスのインスタンスを返しているだけだからだ。それを一時変数に入れたところで、動作は全く変わらない。

LINQは本当に強力だ (4) アルゴリズムヘルパーメソッド

列挙子を列挙するときに、現在の位置が知りたい場合がある。頭の中にまだLINQの武器が少ないときは、諦めてfor文に逃げたりする。

int[] data = new int[] { 123, 456, 789 };
List<int> results = new List<int>();
for (int index = 0; index < data.Length; index++)
{
    results.Add(data[index] * (index + 1));
}

LINQを使うなら、こういったループ制御構文を出来るだけ使わないようにする事だ。これが最終的に、ロジック重視の設計からデータドリブンの設計に移行するカギとなる。
インデックスが必要なら、インデックスを列挙させれば良い。

int[] data = new int[] { 123, 456, 789 };
List<int> results =
    (from index in Enumerable.Range(0, data.Length)
     select data[index] * (index + 1)).
     ToList();

for文やforeach文で(ロジック的に)記述してしまうと、その後の応用性が失われてしまう。Enumerable.Rangeでインデックスの列挙子を生成すれば、結果は列挙子となる。上の例では早々にToListしてしまったが、もちろん、ここから続けて別のクエリを書く事が出来るし、そうすることでパイプライン実行が行われる。AsParallelすれば並列実行も出来る。応用性が全く違うというわけだ。

ところで、上の例は元ネタとなるdataが配列であるので、要素数が分かる。だからEnumerable.Rangeでインデックスを生成できるのだが、配列である事を想定しているので詰めが甘い。ここは一つ、任意の列挙子で実現してみよう。
ただ置き換えるのであれば、以下のようになる。

IEnumerable<int> data = new int[] { 123, 456, 789 };
List<int> results =
    (from index in Enumerable.Range(0, data.Count())
     select data.ElementAt(index) * (index + 1)).
     ToList();

配列はIEnumerableを実装しているので、Count拡張メソッドを使って要素数を取得するのは間違っていない。また、Count拡張メソッドの実装は、対象がICollectionを実装していることを検知して、実際に列挙することなく要素数を得る事が出来るので、効率も良い。

しかし、列挙子が配列ではない、任意の列挙子であった場合、しかもICollectionを実装していない場合は、一旦列挙を行って要素数を数え上げる。要素数を取得するだけならこれでも良いが、その直後、LINQクエリ内のElementAtで、また列挙を開始するため、二重に列挙を行っていることになる。

コストについてピンと来ないかもしれないが、仮に列挙子がデータベースクエリを抽象化していると、データベースに対して2回のクエリ実行を行うことになる。ほとんどの場合において、これは許容出来ない(LINQ to SQLやLINQ to Entitiesでこのような事をしないように)。

では、どうやって改善するか。

public static IEnumerable<Tuple<int, T>> Indexing<T>(this IEnumerable<T> enumerable)
{
    int index = 0;
    return from value in enumerable select Tuple.Create(index++, value);
}

またしても、ヘルパー拡張メソッドだ。これを使えば、タプルクラスで作った「インデックス詰め合わせ」に変換できる。

IEnumerable<int> data = new int[] { 123, 456, 789 };
List<int> results =
    (from entry in data.Indexing()
     select entry.Item2 * (entry.Item1 + 1)).
     ToList();

危ういロジック記述に頼ることなく、インデックスを保持する外部変数に頼ることなく、2回列挙することなく、クエリを簡便に記述可能になった。しかも、Indexingメソッドの実装もまたLINQクエリなので、返された列挙子は遅延実行される。つまり、パイプライン実行が可能ということだ。


Windows Formsで、コントロールの階層を親方向に辿りたいと思ったことがあるかも知れない。

List<Control> list = new List<Control>();
Control current = innerMostControl;
while (current != null)
{
    list.Add(current);  // 実際にはリストに溜めるのではなく、ロジックを書いちゃったりするよね
    current = current.Parent;
}

この「while」を止めたいのだ。LINQに慣れてくると、「ループ終了条件」が正しいかどうかを、演算式だけで担保したくなる。whileやforやifによる制御構文があると、ロジックによる分岐が一々大丈夫かどうか確認するのが煩わしいし、ミスも生じやすくなる。「インデックスって0からだっけ、1だっけ?最後は”<"か"<="か?」とか、ifでこっちに行って、elseであっちに行ってとか、非常にうっとおしい。思考の邪魔だ。

このようなループ制御を行っている場合は、うまく考えればクエリ化出来る。もうかなりのLINQクエリを書いているが、どうしてもループ制御構文を使わなければ実現できなかったコードは、ほんの僅かだった。
この親コントロールをたどるwhileループは、要するに親までの一方向リンクをリスト化するということだ。やってみよう。

public static IEnumerable<T> TraverseLink<T>(this T begin, Func<T, T> predict)
{
    T current = begin;
    while (current != null)
    {
        yield return current;
        current = predict(current);
    }
}

このyield構文は、前回のyield構文と書き方は同じだが、メソッドが直接IEnumerableを返してしまう点で、更に強力だ。と、同時に、使い慣れていないと、メソッドが返却しているモノが一体何であるのか、混乱するかもしれない。

さて、この武器を使えば、リンクリストの追跡など、安全でたやすく、シンプルだ。

List<Control> list = innerMostControl.TraverseLink(delegate(control) { return control.Parent; }).ToList();

ええ、と? あ、一行か :-)

もちろん、型がControlである必要はない。リンク先がParentである必要もない。これらを担保するのは、ジェネリック引数とFuncデリゲートだ。つまり、任意のリンクリストをこのTraverseLink拡張メソッドでたどる事が出来る。

Outlook 2010 から Outlook.com への移行 (3)

あれから、個人・会社のメールをすべてOutlook.comに移行した。と入っても、ドメイン3個分だが。
自分のメールアドレスをそのまま使えるのは、大変便利だ。

ところで、移行後に新規にメールアドレスを増やしたくなったらどうすれば良いのだろうか?
前の記事でも書いたが、現在のOutlook.comの新規アカウント作成画面では、自分のメールアドレスを指定出来ない(ほにゃらら@outlook.comにしか出来ない)。
以前は自由に指定できた気がしたのだが… と思っていたら、Outlook.comから新規アカウント作成を行うとこうなるようだ。以下のリンクから登録すれば、自分のメールアドレスで登録出来る。

新規登録 – マイクロソフトアカウント

しかし、だ。この登録画面から登録を行った場合、メールアドレスの有効性を確認するために、メールアドレスに対して確認メールが送信される。通常の使い方であれば、これは当然の処理であり、送られてきたメールのリンクをクリックして認証が完了する。

だが、Outlook.comで独自ドメインをハンドリングしていると、これで「鶏と卵」が出来上がる。これからメールボックスを作ろうとしているのに、そのメールアドレスに対して確認のメールが送られてしまう(当然、読めない)。だから、事前にすべてのメールアカウントを移行しておく必要がある、と書いた。

実は、前回のWindows Liveアドミンセンターの管理画面に、新しくメールアドレスを追加する画面がある(これは、独自ドメイン委譲の手続きが完了するまで表示されない)。

つまり、独自ドメインをOutlook.comに移行した後は、この画面からアカウントを追加すればいい。簡単だ!
管理アカウントに紐づけられたユーザーだけが、アカウントの追加・削除を行えるので、管理者にも優しい。

判っていれば、「何をいまさら」なネタではあるし、移行開始前までは、どこかにこのような管理画面があることを期待していたのだが、現在のOutlook.comとWindows Liveのシステム統合が(特にユーザーインターフェイスが)、中途半端で非常にわかりにくいために、まったく気が付かなかった。時間の経過とともに、ここも改善されていくだろう。


ところで、元のネタはOutlook 2010からの移行なので、その件をメモっておく。

Outlook ExpressなどからOutlook.comへの移行には、専用の移行ツールがあるが、Outlook 2010からの移行ツールは無い。では、どのようにすれば良いかだが、Outlook 2010にはHotmail connectorというアドインが存在する。

Microsoft Office Outlook Hotmail Connector の概要

これをインストールしてサインインすると、Outlook.comのメールボックスを直接Outlook 2010内にマウント出来る。

Hotmail専用ではないのか?と思うが、実はOutlook.comのバックエンドはHotmailそのままか、あるいはインターフェイスが同じであるようだ。実際、HotmailアカウントをそのままOutlook.comに移行できるし(最近は、「Outlook.comを試す」という表示が出る)、Outlook.comのアカウントをHotmailにダウングレード(?)も出来る。

さらに、iPhoneからHotmailコネクタで直接Outlook.comのアカウントに接続できることも確認した。どうもプッシュ通信にも対応しているようで、メール着信が瞬時に通知される。これは良い。

Outlook 2010に話を戻すと、これでOutlook.comのメールボックスと、従来のメールが行き来できるようになったので、後は従来のメールのうち、必要なものをOutlook.comのメールボックスにドラッグ&ドロップで移すだけ。
(忘れず、溜りに溜まった迷惑メールも移行する。SPAM学習ネタになるだろうか?)

ただし、操作は簡単だが、移動には死ぬほど時間がかかる(結局一日がかりだった orz)。あまりに多いメールを一度に移動しようとすると刺さる、など、やや不安定な部分もあるので注意。この際、必要なメールだけ移動して、後はPSTファイルでバックアップするか削除するなど、整理しても良いのではないだろうか。

さて、これでとうとうメールサーバのNetBSDを止める時が来たようだ。Windows 8への準備はまだ一つ大きな壁(移動ユーザープロファイルの廃止)があるのだが、一つ重荷が減った思いだ。

Outlook 2010 から Outlook.com への移行 (2)

のらりくらりやろうと思っていたら、MXレコード更新の時点で「こいつはすばやくやらないと!!」って状態になってしまったので、本腰を入れることに :-)
とりあえず、結論としては「できました」。


手順1:
あらかじめ、Outlook.comのアカウント(正確にはマイクロソフトアカウント)を取っておく。

Outlook.com サインイン

現在は、自分のメールアドレスで直接アカウントを生成できないようだ。一旦適当なアカウント名で取得し、「メールアドレスの更新」で、ターゲットとなるメールアドレスに変更する。その際、自分のメールアドレスに確認メールが送信されるので、現行のメールサーバからメールを確認する必要がある。


変更が完了したら、念のためサインインして確認する。
メッセンジャーなどのLiveクライアントを使用している場合は、それらも再度サインインし直しておくこと。アカウントは”Windows Live ID Sign-in Assistant”サービスで管理されており、移行前と後の混在状態だと、思わぬ問題が発生する可能性がある。

※重要
以降の手順を実施する前に、必ずドメインに含まれるすべての現存メールアドレスのアカウントで、Outlook.comのアカウントを取得すること。上記のように、直接自分のメールアドレスでアカウントを取得出来なくなっているので、後からメールアドレスを変更しようとしても「鶏と卵」状態になってハマる。


手順2:
Live.comのドメイン管理画面にアクセスし、メールドメイン管理委譲の手続きを行う。

Windows Live アドミン センター

「www」とか先頭に書いてあるが、気にするな :-)

ここで割り当てるドメイン管理者は、以下の設定を管理するためのアカウントであって、メールアドレスとは直接関係はない。

これでOutlook.com側は準備出来たので、ドメイン管理側のレコードを修正する。うちでは、Dyn.incを使っているので、以下のような管理画面でMXレコードとTXTレコードを指示通りに変更する(TTLはわざと600にしてある)。

この後、ドメイン管理画面の「更新」をクリックし、Outlook.com側でも変更が認識出来たことを確認する。
これで、すでにメールはOutlook.com側に配信されているはずだ。

Outlook.comにメールアドレスのアカウントでサインインし、メールの送受信が出来ることを確認する。
ここまでで、もっとも懸念していた技術的な課題はクリア。後は既存のメールデータの移行だ。

つづく。

Outlook 2010 から Outlook.com への移行 (1)

重いネタだが、いつかは取り掛からなければならないと思っていた。

現在はOutlook 2010を個人メールアドレス(IMAP4/NetBSD)と外部プロバイダ(POP3)で使っている。
もともとはExchangeでやっていたのだが、管理が大変過ぎて諦めた(SIPがTCPベースでオワタ感があったのも理由だが)。

で、最近ThinkPad X201sを購入した。LAN接続している時やWifi経由でVPNしている場合は良いのだが、こういう場合にWindowsドメイン参加がネックになってしまう。おまけに(非推奨を承知で)Outlook.pstをユーザーフォルダに置き、これをADのフォルダリダイレクトで共有フォルダに入れているため、ネットワーク接続が切れていたり不安定だったリすると、使い物にならない(壊れてしまう恐怖もある)。

そこで、メールについては、フロントエンドをOutlook 2010で使うにしても、何かクラウドソリューションに移行する必要があると、常々考えていた。VPSのようなサービスでIMAP4でもいいわけだが、自由な時間がどんどん減っているので、(金を払っても)SaaSだろうかとも考えていた。

一番候補に挙がったのはOffice360だが、実際、何も下調べはしていないので、満足できるかどうかはわからない。
そうこうしているうちに、件のOutlook.comが立ち上がった。とりあえず、アカウント名で辛酸を舐めるので、サインアップだけはした。

Gmailのおかげか、昔なら「超ニッチ」と思われていた技術的な課題は、問題なくいけそうだ。

・ネイティブクライアントからの接続。IMAP4やPOP3でアカウントにアクセス。今や当たり前の機能。
 IMAP4に入れ込んでいるわけではないので、もっとタイトに接続できる方法があるなら、なおの事よい。
 Outlook 2010向けにコネクタがあるかな?
・外部プロバイダからのメール取り込み機能。昔風に言うなら、fetchmailもどきか。
 これで、度々認証に失敗するぐうたらな某GM○に直接つながなくて済みそうだ。
・自前ドメインのネームサーバーにMXを指定してOutlook.comに取り込ませる。
 これはまだよくわからない。現状のIMAP4は自前ドメインで、自前サーバなので、これが出来ないとネタとしてはボツになる。Gmailでは出来る?

Gmailでやったら?と言われそうだが、個人的に情報の一極集中に良い思いがないのと、Googleが欲しいのはメールデータであることははっきりしているので、避けようと思う。

#加工するにしてもしないにしても、Googleの収入源はほとんど「情報」であり、それをネタに上場しているから自明(行動はほとんど確信犯だし)。
#MSももちろん上場企業だが、MSは今のところは情報だけが収入源ではない。情報収集が0とは思わないが、プロジェクトの(情報吸い上げの)本気度も、自ずと違ってくるだろう。
#どうしても嫌になったらOffice 365に移行すればいい。どのみち、絶対的な無償なんて存在しない。

さっそく判明した問題が一点。Office Outlookラインからは、今のところ直接メールデータを移行する手段がない。Outlook Express・Windowsメール・Liveメールからは、プラグイン導入で移行出来るようだ。別のルートを探さなければ…