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メールからは、プラグイン導入で移行出来るようだ。別のルートを探さなければ…

Windowsインストール時に、USB Floppy Driveが認識されない

備忘録。

Windows XP・Windows Server 2003世代で、RAIDドライバなどの追加ドライバを F6 キー押下でインストールするとき、パーティション・フォーマットの操作は出来るようになるものの、Windowsファイルのコピー開始直前に、ドライバの入ったフロッピーが認識されなくなる事がある(ディスクを挿入するように表示されて先に進まない)。

前々から変だなと思いつつも、(レガシーな)FDDを接続してやり過ごしてきたが、もう最近のMBにはFDDインターフェイスが無い。少し検索しても、F6キーを押下して読み込ませる、で終わっている記事が多く、どうもおかしい。

今日、遂に原因がわかった。

Windows XP のインストール処理中に大容量記憶装置ドライバーをインストールする F6 キーを押すと接続されている USB フロッピー ディスク ドライブが動作しません

持っているFDDはバッファローの2倍速FDDで、FD-2USBという型番だ。これは2倍速FDDが「いまさらこの値段で買うのもなぁ」と思っていた頃に、ジャンク屋で安く売っていたので入手したもので、USB接続で普通に使うにはまったく問題が無い。
で、IDは「USBVID_054C&PID_002C」であった。

対処方法はいろいろ考えられるが、これのためだけにDriver DiskをWindowsセットアップに統合するのもばかばかしいので、TXTSETUP.SIFをいじって直すことに。もともと存在するエントリ「USBVID_054C&PID_0023 = “usbstor”」の行を複製して追加し、「PID_0023」を「PID_002C」に変更。

その後、Bootable DVDとして焼いて、インストール完了。

LINQは本当に強力だ (3) 拡張メソッドによる拡張

ToHashSet()とか、ToSortedList()とか、作ってみただろうか? :-)

もう一つ小ネタを行ってみよう。

複数の文字列を連結出来たら良いのにと思う事がある。ハードコードするなら、+演算子で繋げれば良いのだが、配列や列挙子だとこうは行かない。で、レベルの低い現場でよく見るのが、forで回しながら+演算子で連結という、眩暈のするコードだ(しかもStringBuilderも使っていない)。

もちろん、拡張メソッドを定義してみる。
StringBuilderを使ってもよい(その方がありがたみがあるかも?)が、忘れがちだが、System.StringにConcat()というメソッドがあり、簡単に実現出来る。

string[] words = new string[] { "ABC", "DEFGH", "IJK" };
string concatted = string.Concat(words);

これが忘れやすい理由の一つは、やはりStringクラスのスタティックメソッドであることではないだろうか。
このぐらい、えいやっと…

public static string Concat(this IEnumerable<string> words)
{
    return string.Concat(words);
}

もちろん、拡張メソッドのクラスが含まれる名前空間が using されていなければならない。例えば、プロジェクトで共通で使用するクラスライブラリに、LINQ向け拡張メソッドを含む名前空間を決めておくというのはどうだろうか?
他にも、文字群を連結して文字列にするというのも考えられる。

public static string Concat(this IEnumerable<char> chars)
{
    return new string(chars)
}

最初の例で示した文字列の連結は、たとえばカンマ区切りで取得したい場合もあるだろう。

public static string Concat(this IEnumerable<string> words, string separator)
{
    return string.Join(separator, words);
}

小ネタばっかりだが、そろそろまとめておく。

  • 列挙子を受け取って何かをするメソッドなら、拡張メソッドを使って定義しておくと、LINQクエリで使いまわしやすい。
  • しかし、同じ名前のオーバーロードを沢山定義すると、ニアミスが発生しやすい事は押さえておく必要がある。

例で挙げたメソッドは、全て”Concat”メソッドで、引数が異なるのでオーバーロードとして成立する(LINQのConcat含めて)。しかし、これが原因で使う際にどのオーバーロードを使用すべきか迷うことがある。

私は、”Contains”メソッドをいくつか定義してみたことがある。LINQのContainsは、追加引数で指定された値が列挙値内に含まれているかどうかをテストする。ここに、System.String.IndexOf()とよく似たContainsを定義してみた。また、列挙値が追加引数群の何れかに一致するかどうかをテストするContainsも作ってみた。

そうすると、当然のことながら、Containsのどの実装が欲しい機能を持っているのか、良く分からなくなってしまう。実際、インテリセンスはこれらのContainsを全て掲示してくれるが、やはりいまいちピンとこない。

問題は、似て異なる機能を持ったメソッドを、同じ名前で定義している事だろう。古典的な問題だが、拡張メソッドについても同じ事が発生するので注意した方が良い。”Concat”も、”ConcatWords”とか、”ConcatToString”のような、具体的な名前を使った方が良いこともある。

この問題はどのように考えれば良いだろうか。
LINQでは、拡張メソッドがあらゆる列挙子で使われることを想定している。例えばConcatの定義、

IEnumerable<T> Concat<T>(this IEnumerable<T> lhs, IEnumerable<T> rhs);

というのは、この拡張メソッドだけでT型列挙子の全てに応用が可能だ。つまり、このConcatだけで様々な用途に使用出来る。それだけ既に抽象化されているわけで、これと似た、しかし異なるメソッドを定義しなければならないとすれば、そもそも元のConcatと用途が異なるかも知れない、という事に注意を払えば良いと思う。


さて、LINQ向けに上記のような「部品」を作っておく事の良さを、もうちょっと別の面で示そうと思う。

今までの例は、拡張メソッド内で、直前の列挙子を評価してしまう。ToSortedDictionaryの場合、メソッド内でforeachで回した時点で列挙を開始する。目的がSortedDictionaryに詰め替えることだからこれで良いのだが、LINQ標準の拡張メソッド(たとえばWhereやOrderBy)のように、実際に列挙されるまで動作を遅延させ、列挙しながら処理を行うためにはどうすれば良いだろうか?

.NET 1.0/1.1では、IEnumerableインターフェイスを実装後、GetEnumerator()メソッドを書かなければならなかった。GetEnumerator()は、列挙子の実体処理を行うクラスのインスタンスを返す。これはIEnumeratorインターフェイスを実装していれば、非公開でも構わない。しかし、IEnumeratorはステートマシンの実装を強要するため、はっきりってこれを書くのは面倒だ。

.NET 2.0になって、画期的な構文「yield」が使えるようになった。詳細は省くが、つまりこれを使って実装すればGetEnumerator()の実装は難しくない。

例えば、何もしない拡張メソッドNop()を実装してみる。

public static IEnumerable<T> Nop<T>(this IEnumerable<T> enumerable)
{
    // 内部的な列挙子を返す
    return new NopEnumerable(enumerable);
}

// T型の何もしない列挙子
private sealed class NopEnumerable<T>
{
    // 元の列挙子
    private readonly IEnumerable<T> enumerable_;

    // コンストラクタ
    public NopEnumerable(IEnumerable<T> enumerable)
    {
        // 保存しておく
        enumerable_ = enumerable;
    }

    // 列挙を実行する
    public IEnumerator<T> GetEnumerator()
    {
        foreach (T value in enumerable_)
        {
            // yield構文を使って、要素を返す(ここでは何もしないでそのまま返す)
            yield return value;
        }
    }

    // 非ジェネリック
    IEnumerator IEnumerable.GetEnumerator()
    {
        // バイパス
        return GetEnumerator();
    }
}

NopEnumerable<T>クラスは、GetEnumerator()が呼び出されるまでは、元の列挙子を一切操作しない。

GetEnumerator()が呼び出されると、最初のforeachで初めて列挙子のGetEnumerator(enumerable.GetEnumerator())が呼び出される。ここで、元の列挙子も動作を開始するわけだ。
その後、foreachで得られた値を、yield return構文で一つずつ返却する。このメソッドのソースコードとバイナリコードはまったく異なり、記述どおりに振る舞うステートマシンとしてコンパイルされる。そのお蔭で、GetEnumeratorの実装は非常に簡単になっている。

このコードをスケルトンとして、GetEnumerator()の実装を肉付けすれば良いだろう。一例として、二重の構造を持った列挙子を、一重の列挙子(つまり普通の列挙子)に変換するメソッドを定義する。

// 二重の列挙子を順番に連結し、一重の列挙子に変換する
public static IEnumerable<T> Unloop<T>(this IEnumerable<IEnumerable<T>> enumerable)
{
    return new UnloopEnumerable(enumerable);
}

private sealed class UnloopEnumerable<T>
{
    private readonly IEnumerable<IEnumerable<T>> enumerable_;

    // コンストラクタ
    public UnloopEnumerable(IEnumerable<IEnumerable<T>> enumerable)
    {
        enumerable_ = enumerable;
    }

    // 列挙を実行する
    public IEnumerator<T> GetEnumerator()
    {
        foreach (IEnumerable<T> outer in enumerable_)
        {
            foreach (T inner in outer)
            {
                yield return inner;
            }
        }
    }

    // 非ジェネリック
    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

#ひょっとして、LINQ to Objectに既にあるかも? 折を見て、MSDNライブラリにも目を通すと良い。
#思いつくようなメソッドは標準で提供されている事がある。
#なお、LINQ to Objectの拡張メソッドは、Enumerableクラスに定義されている。

“IEnumerable<IEnumerable<T>>”という型を見て、「えっ、これどういう構造?」と一瞬考えたことは無いだろうか?このUnloopを使えば、ループ構造を簡略化出来る。しかも、このメソッドは実際に列挙されるまで動作が遅延され、中間バッファは全く必要ない。LINQにはぴったりのメソッドだ。

しかしながら、この例で言えば、実はLINQクエリだけでも解決できる。

IEnumerable<T> results =
    from outer in enumerable
    from inner in outer
    select inner;

#対応する拡張メソッドは「SelectMany」だ。

selectの書き方次第では、outerとinnerを使った演算結果を返すとか、応用性も高い。Unloopは単純な例なので仕方がないが、逆に言えば、まずLINQクエリだけで解決出来るか考えて、それから拡張メソッドを実装する、という手順にした方が良いかもしれない。もちろん、メソッドを作れば文字通り「何でもアリ」となるので、究極的にはメソッド実装すれば、難しい問題も切り抜けられるはず。

この辺り、SQL Serverで、まずクエリだけでどうやって書けるか、クエリの書き方次第でどのように高速化出来るか、それがダメなら部分的にユーザー定義関数に逃がすか、と考えるのと同じ方法論であって、実際頭の中では同じ部位が働いている感じがする。
#横に逸れるけど、上記のクエリにAsParalell()とかやりたくなるよね? 迷っているなら、その事も考えておこう :-)
つづく

LINQは本当に強力だ (2) クエリの連結と拡張メソッド

小ネタだ(しかし、重要)。
書いたクエリはすぐには実行されず、列挙を行った時点(おおよそ、IEnumerable<T>.GetEnumerator()を呼び出した時点)で、実際にクエリが評価される。LINQメソッドの中には、その場で評価が実行されるものもある。例えば、ToList()がそうだ。

// 何の抽出かな?
int[] years = new int[] { ... };
IEnumerable<int> query =
    from year in years
    // letはクエリ内の一時変数のようなもの。
    // SQL Serverのクエリなら、同じ式を多量に記述してもクエリオプティマイザが検出して重複を取り除くが、
    // LINQ(LINQ to Object)はそこまでやらないので、letを使うと良い場合がある。
    // (ここではそのような深い意味はない)
    let mod4 = (year % 4) == 0
    let mod100 = (year % 100) == 0
    let mod400 = (year % 400) == 0
    where mod400 || (!mod100 && mod4)
    select year;

// クエリを実行してList<T>に格納する
List<int> leapYears = query.ToList();

ToList()の部分は、以下のような事をしている。

List<int> leapYears = new List<int>();
foreach (int value in query)
{
    leapYears.Add(value);
}

あるいは、もっとこう。

List<int> leapYears = new List<int>(query);

後者ならかなり短く記述出来るわけだが、ToList()を使う利点は、LINQのメソッド連結式の延長上で書けるからだ。例えば、

// 2000年より前のうるう年を昇順にしてList<int>に入れて返す
List<int> results =
    // メソッド連結式(Where()とOrderBy()はデリゲートで評価結果を得る。最後にToList()する)
    query.Where(delegate(value) { return value < 2000; }).
        OrderBy(delegate(value) { return value; }).
        ToList();

もちろん、ToList()のところを、new List<int>(…)と書いたって良いのだが、Visual Studio上でこれを書けば、何を言っているのか分かると思う。要するに「すらすら書ける」ということだ。

さて、「すらすら書く」ためには、LINQの標準メソッド群でも、やや不足を感じることがある。
例えば、ToDictionary()を使えば、Dictionary<T, U>に変換できるのだが、SortedDictionaryが欲しい場合には、ToSortedDictionary()なるものを期待しても、そういうメソッドはない。
某サイトでは、一旦ToDictionary()で辞書を生成した後、SortedDictionaryのコンストラクタに渡して生成する例が掲載されていた。しかし、それではDictionaryは完全に無駄だ。Dictionaryは内部でハッシュコード取得と分散を行っているはずなので、そのコストはドブに捨てる事になる。そして、やはりすらすらとは書けない。

そこで、無いものは作ってしまえ、と。

// staticクラスでなければならない
public static class CollectionExtensions
{
    // staticメソッドの第一引数にthisを付けることで、「拡張メソッド」となる。
    // Func<V, T>はジェネリックデリゲートで、V型引数を取り、T型を返すメソッドを受け入れる
    public static SortedDictionary<T, U> ToSortedDictionary<T, U, V>(
        this IEnumerable<V> enumerable,
        Func<V, T> keySelector, Func<V, U> valueSelector)
    {
        // 入れ物を作って...
        SortedDictionary<T, U> dictionary = new SortedDictionary<T, U>();

        // 列挙しながらセレクタを通してキーと値を取得して格納
        foreach (V entry in enumerable)
        {
            dictionary.Add(keySelector(entry), valueSelector(entry));
        }
        return dictionary;
    }
}

セレクタとして受け入れるデリゲートの部分は、.NET 2.0世代の知識では少し理解が厳しいかもしれない。まず、ToDictionary()がどのように使われるのかを確認しておく。

// 適当な数列
int[] values = new int[] { ...};
// 数列を文字列化したものとペアで保存する
Dictionary<int, string> results =
    // 条件式がなくても全く問題なし
    (from value in values
    // 匿名クラスを使って、一時的にクラスに値を保存する
     select new { Arg1 = value, Arg2 = value.ToString() }).
    // 匿名クラスから値を取り出してDictionaryにする。
    // "entry"は、式内でのみ有効な引数(上の匿名クラスのインスタンスが渡って来る)
    ToDictionary(delegate(entry) { return entry.Arg1; }, delegate(entry) { return entry.Arg2; });

匿名クラスというのは、名前の無い、初期化だけが出来るクラスの事で、new 文の後ろのブロックで読み取り専用フィールドを定義・生成出来る。上記ではArg1とArg2を宣言して、それぞれ値を代入した。
匿名クラスには名前がないので、コード上で型名を書くことが出来ない。しかし、上の式を見れば、巧妙にクラス名を避けている事が分かる(ここから、何故”var”が必要になったかが分かれば、良い洞察だ :-)
そして、今やToSortedDictionary()という武器が手に入ったので、以下のように書けばよい。

SortedDictionary<int, string> results =
    (from value in values
     select new { Arg1 = value, Arg2 = value.ToString() }).
    ToSortedDictionary(delegate(entry) { return entry.Arg1; }, delegate(entry) { return entry.Arg2; });

晴れて、イメージ通りのToSortedDictionary()が得られた。
しかし、私はToDictionary()にもやや納得が行かない部分があった。一旦匿名クラスに値を入れ、その後セレクタを使って再度辞書に入れ直すという部分だ。どうもスマートじゃない。
無い道具は作ってしまえ、と。

// 初めからKeyValuePairでいいじゃん?
public static SortedDictionary<T, U> ToSortedDictionary<T, U>(
    this IEnumerable<KeyValuePair<T, U>> enumerable)
{
    // 入れ物を作って...
    SortedDictionary<T, U> dictionary = new SortedDictionary<T, U>();

    // 列挙しながらそのまま格納
    foreach (KeyValuePair&lt;T, U&gt;  entry in enumerable)
    {
        dictionary.Add(entry.Key, entry.Value);
    }
    return dictionary;
}

そうすると、クエリ側にはKeyValuePairを書かなければならなくなる。これをどう捉えるかだが、直後に辞書に入れると分かっているなら、KeyValuePairで生成した方がすっきりするのが私流。

SortedDictionary<int, string> results =
    (from value in values
     select new KeyValuePair<int, string>(value, value.ToString())).
    ToSortedDictionary();

言わずもがな、ToDictionary()にも同じものが作れるよね?
し・か・も! この拡張メソッドはIEnumerable<KeyValuePair<T, U>>を実装する、全てのクラスで使えるのだ。それはつまり、Dictionary<T, U>とSortedDictionary<T, U>そのものだ。

Dictionary<int, string> hashed = new Dictionary<int, string>();

// .. 辞書にいろいろ追加

// DictionaryをSortedDictionaryに変換
SortedDictionary<int, string> sorted = hashed.ToSortedDictionary();

// SortedDictionaryをDictionaryに変換
Dictionary<int, string> dictionary = sorted.ToDictionary();

もちろん、辞書じゃなくてもOKだ。

// IEnumerable<KeyValuePair<T, U>>を実装していればいいのだから...
List<KeyValuePair<int, string>> list = new List<KeyValuePair<int, string>>();

// ..

// ListをSortedDictionaryに変換
SortedDictionary<int, string> sorted = list.ToSortedDictionary();

// ListをDictionaryに変換
Dictionary<int, string> dictionary = list.ToDictionary();

なんだか、色々な事に使えそうな気がして来ないだろうか?
つづく

LINQは本当に強力だ (1) データ加工の究極の道具

長い間、.NET2.0から知識をアップグレードしていなかったのだが、先日一気に.NET4.0の知識を詰め込んだ。
LINQに触る必要性から、匿名デリゲート・ラムダ式・匿名クラス・式ツリー・拡張メソッドなどを覚えたのだが、はっきり言って今まで勉強を放置してきた事に、激しく後悔している。
「食わず嫌い」だったのは、矢継ぎ早に追加される新しい構文に対する抵抗だったような気がする。C++のテンプレート拡張がもめたらしいのも、気持ちはよくわかる。

テンプレートもそうだが、LINQも言語思想の革命と言っても言い過ぎではないぐらいのインパクトがあった。

LINQの何が良いかと言えば、比類なき拡張性だろう。これを支えているのはIEnumerable<T>インターフェイスと、拡張メソッド構文なわけだが、LINQに触ったことが無い人向けに、興味が持てそうな事を書いてみる(但し無保証 :-) まぁ、もう既に「枯れた」技術になり始めてる気がするので、今更だが)

IEnumerable<T>インターフェイスは、List<T>ジェネリッククラスや配列が実装している。というよりも、「列挙」可能なクラスには、このインターフェイスが実装されている(ちなみに、非ジェネリックなIEnumerableは列挙可能だが、LINQで使うには難点があるので省略)。

このインターフェイスが実装されていれば、以下のようにforeach出来る。

List<string> nameList = new List<string>();
// ...
foreach (string name in nameList)
{
    // 様で呼んでくれなきゃやだ
    if (name.EndsWith("様") == true)
    {
        Console.WriteLine(name);
    }
}

# varはわざと使ってないよ?(一応、.NET 2.0技術者向け)
で、LINQでforeachの部分を以下のように書ける。

// LINQクエリ(まだ実行されない)
IEnumerable<string> kingNames =
    // nameListの個々の要素をnameとして取り出し、
    from name in nameList
    // nameの終端が"様"の場合に、
    where name.EndsWith("様") == true
    // このクエリの結果要素としてnameを返す。
    select name;

// クエリの結果を列挙
foreach (string name in kingNames)
{
    Console.WriteLine(name);
}

「仮」に、nameListの要素数が非常に多かったとする(100万件とか)。
その場合に、以下のように「AsParallel()メソッドを呼び出す」だけで、”様”付きの判定をマルチスレッド化出来る。

IEnumerable kingNames =
    from name in nameList.AsParallel()
    where name.EndsWith("様") == true
    select name;

大量の要素の、しかしながら個々の要素の判定にはそれほどの判定コストを必要としない場合でも、この「PLINQ(パラレルLINQ)」は効率的に動作するはずだ。

通常、マルチスレッドで同じことを行う場合、以下のようなコードとなる。

private readonly List<T> kingNames = new List<T>();
private int count = 1;
private readonly ManualResetEvent final = new ManualResetEvent(false);

// ワークアイテム実行のエントリポイント
private void WorkItemEntry(object argument)
{
    string name = (string)argument;
    if (name.EndsWith("様") == true)
    {
        // 追加が競合しないように、コレクションをロックする
        lock (kingNames)
        {
            kingNames.Add(name);
        }
    }

    // 自分が最後なら通知
    if (Interlocked.Decrement(ref count) == 0)
    {
        final.Set();
    }
}

public IEnumerable<T> GetKingNames()
{
    List<string> nameList = new List<string>();

    // ...

    // ワークアイテムをキューに入れる
    foreach (string name in nameList)
    {
        Interlocked.Increment(ref count);
        ThreadPool.QueueUserWorkItem(WorkItemEntry, name);
    }

    // (キューに入れている間に終わってしまわないように、カウンタの初期値を1にしてあるので減算)
    if (Interlocked.Decrement(ref count) == 0)
    {
        final.Set();
    }

    // 全てのスレッドが終わるのを待つ
    final.WaitOne();
    return kingNames;
}

まず、第一にわかることは、「マルチスレッド対応」にするだけで面倒で不安な同期処理を大量に記述しなければならない事だ。この処理の肝は「EndsWith(“様”)」だけであるのに、付随コードの何とも多い事か。そして、これだけでもLINQで書く事がいかに強力であるかが分かる。

LINQで書く場合、「AsParallel()」で列挙子を修飾するだけだ。AsParallelメソッドは、指定された列挙子(nameList)をParallelQuery<T>クラスでラップする。LINQの他のメソッド同様、これだけではまだクエリは実行されていない。最後にkingNamesをforeachで列挙するまで、全く処理は行われていない。つまり、中間バッファは一切不要という点も重要だ。

元のデータが100万件、EndsWithで絞り込んでも50万件のデータがあるとすれば、判定しては新たなコレクション(中間バッファ)にデータを格納するのはためらわれる。また、列挙した時点で一気に処理を実行することで、CPUのキャッシュにコードが維持される可能性も高まる。おまけに.NETはJITコンパイラで動いているので尚更だ。

次に、レガシーコードで示した方法には、マルチスレッドを効率的に実行できない罠がある。それは、kingNamesに対してロックを行っている部分だ。この実装は、一回のワークアイテム実行に占めるロック時間が、相対的に長すぎる。そのため、複数のスレッドで同時実行されると、ロック競合が多量に発生する。結局その間は「シングルスレッド」と変わらないのだ。おまけにスレッドコンテキストの遷移に時間がかかってしまうので、シングルスレッド性能より落ちてしまう。

「既存コードのマルチスレッド化でパフォーマンス向上」なんて、生易しい事ではないのだ。

それがもっとよく分かるように、このレガシーコードを改良してみる。

// スレッドのエントリポイント
private void ThreadEntry(object argument)
{
    KeyValuePair<Queue<string>, List<string>> pair = (KeyValuePair<Queue<string>, List<string>>)argument;
    Queue<string> queue = pair.Key;
    List<string> localKingNames = pair.Value;

    while (true)
    {
        // キューから文字列を取り出す。最後なら抜けてスレッド終了
        string name = queue.Dequeue();
        if (name == null)
        {
            return;
        }
        if (name.EndsWith("様") == true)
        {
            // コレクションはスレッド固有なのでロック不要
            localKingNames.Add(name);
        }
    }
}

public IEnumerable<T> GetKingNames()
{
    List<string> nameList = new List<string>();

    // ...

    // スレッドに供給する文字列と、結果文字列を格納するコレクションをスレッド数分用意する
    List<KeyValuePair<Queue<string>, List<string>>> pairs = new List<KeyValuePair<Queue<string>, List<string>>>();
    for (int i = 0; i < Environment.ProcessorCount; i++)
    {
        pairs.Add(KeyValuePair<Queue<string>, List<string>>(
            new Queue<string>(), new List<string>());
    }

    // 事前にキューに入れる(スレッド毎に均等に)
    int index = 0;
    foreach (string name in nameList)
    {
        pairs[index].Key.Enqueue(name);
        index = (index + 1) % Environment.ProcessorCount;
    }

    // スレッドを生成して実行を開始する
    List<Thread> threads = new List<Thread>();
    for (int i = 0; i &lt; Environment.ProcessorCount; i++)
    {
        Thread thread = new Thread(ThreadEntry);
        threads.Add(thread);
        thread.Start(pairs[i]);
    }

    // スレッド群が終わるのを待つ
    List<string> kingNames = new List<string>();
    for (int i = 0; i < Environment.ProcessorCount; i++)
    {
        threads[i].Join();
        // 終わったスレッドから、結果を収集する
        kingNames.AddRange(pairs[i].Value);
    }
    return kingNames;
}

あーもう、書いている矢先から面倒で、記事自体無かったことにしようかと5回ぐらい思った :-) (そんな訳で、コンパイルして検証はしていない)

要するにこのコードは、ロックを行わなくて済むように、事前にスレッド毎にデータを分散し、スレッド毎に結果を格納し、その結果はメインスレッド側で収集する、という事をしている。これはマルチスレッドでパフォーマンスを向上させる定石のようなものだ(ロックを不要にする)。キューにデータを入れ直している時点でメモリを余分に使っているため、さらなる改良が必要だが、「もういいだろう」。それにこれ以上書きたくない。

つまり、こういう面倒なことを、ParallelQuery<T>クラスの内部でやってくれるという事だ。そして、現在のCPU事情と言えば、シングルスレッド性能は頭打ちで、マルチコア・SMTを推進している。コードを高速化させるためには、マルチスレッドに対応させるコーディングを行う必要があるが、同期処理は面倒かつバグを生みやすい。上記のような短いコードでさえ、書いただけでは正しいかどうか分からない。

この最適化手法を知っている人なら、これがAsParallelするだけで実現される、なおかつそれはコンパイラが何か怪しげなことをやっているのではなく、全てライブラリだけで実現されていると聞けば、カルチャーショックを受けるはずだ(受けなきゃおかしい)。

#LINQのクエリ構文はコンパイラが解釈する。しかしParallelQuery<T>クラスは種も仕掛けもない、普通のライブラリだ。
#その気になれば、だれでも同じものが作れる。もちろん、安全に実行出来るようにするには高度な技術が必要なので、「俺ParallelQuery<T>」は作らない方がいい。

さて、どのようなコードでも、LINQで書かなければならない訳ではない。例えば、LINQで列挙しながら、列挙中のコレクションを更新するという操作はNGだ。しかし、それはLINQを使わない場合でも同じ事だ(LINQで記述すると、あまりに簡便になるため、出来ない事があると欠点に思えてくるが、それは多分違うよ?)。

また、LINQは「常に構造が変化する要素」を扱うのが難しい。これは当たり前だ。列挙する要素がintになったりstringになったりする状況では、クエリが簡便に記述出来ない(そういう状況が、IEnumerable非ジェネリックインターフェイスを使った場合だ。もっとも、LINQのライブラリはIEnumerableに対応していない。擬似的にIEnumerable<object>で試せば分かる)。

これを以って、やはりLINQは中途半端と思うなら、実用的なコードをLINQで書いてみるべきだ。また、LINQを使うのに、SQL Serverにアクセスする必要などない。IEnumerable<T>インターフェイスを実装していれば、あらゆる要素に応用が可能だ。

「LINQが適している分野ではLINQで書け」

これに尽きる。そしてLINQの「後」でPLINQが生み出されたように、LINQの拡張性にも目を見張るものがある。次回に続く。