XboxInfoTwit - ver.2.3.0.3

Xbox.comの認証周りが変わってログイン出来なくなってしまってたのですが、それを修正しました。朝っぱらに急ぎで対応したので、あまりテストしてません。マズいところあったら後で直します。

Chaining Assertion for MSTest

MSTest用の拡張メソッド集をCodePlexと、そしてNuGet(idはChainingAssertionです)にリリースしました。ライブラリといってもたった数百行(うち300行が自動生成)の、csファイル一つです。NuGet経由でも.csが一個配置されるだけという軽量軽快さ。中心となるのはIsというTへの拡張メソッドで、これはAssert.EqualとAssert.IsTrue(pred)とCollectionAsert.AreEqualを、一つのメソッドだけで表現します。単純なものですが、それだけなのに驚くほどテストが書きやすくなります。まずは例を。

// 全てのオブジェクトに.Isという拡張メソッドが追加されていて、3つのオーバーロードがあります

// Assert.AreEqualと等しい
Math.Pow(5, 2).Is(25);

// 条件式によるAssert.IsTrueをラムダ式で
"foobar".Is(s => s.StartsWith("foo") && s.EndsWith("bar"));

// コレクションの等値比較は可変長引数でダイレクトに書ける
Enumerable.Range(1, 5).Is(1, 2, 3, 4, 5);

以前どこかで見たような?はい。ベースはテストを簡単にするほんの少しの拡張メソッドで書いたものです。それを元に若干ブラッシュアップしています。ラムダ式によるAssert時のエラーメッセージが、非常に分かりやすくなりました。

前はFuncだったのですが、Expressionで受けるようにしたので、情報が取れるようになったらからです。それと渡ってくる値に関しても表示。これで、情報が足りなくてイライラってのがなくなって、かなり書きやすくなりました。

ところでnullに関してはIsNullとIsNotNullというメソッドに分けました。あまり増やしたくはないので、入れるか悩んだんですけどね。x => x == null でもいいのだし。

TestCase/Run

以前書いた時もNUnit風のTestCaseの再現を試みていたのですが、今回は抜本的に作り替えてリベンジしました。再現度(それっぽい度)は相当上がっています。といっても、目的は再現度を上げることじゃあないですね、大丈夫です、これにより、遥かに使いやすく実用的になりました。

[TestClass]
public class UnitTest
{
    public TestContext TestContext { get; set; }

    [TestMethod]
    [TestCase(1, 2, 3)]
    [TestCase(10, 20, 30)]
    [TestCase(100, 200, 300)]
    public void TestMethod2()
    {
        TestContext.Run((int x, int y, int z) =>
        {
            (x + y).Is(z);
            (x + y + z).Is(i => i < 1000);
        });
    }
}

TestContextへの拡張メソッドにRunというのが仕込んであって、それとTestCase属性が連動して、パラメータ指定のテストが行えるようになっています。TestCaseSource属性もありますよ?

[TestMethod]
[TestCaseSource("toaruSource")]
public void TestTestCaseSource()
{
    TestContext.Run((int x, int y, string z) =>
    {
        string.Concat(x, y).Is(z);
    });
}

public static object[] toaruSource = new[]
{
    new object[] {1, 1, "11"},
    new object[] {5, 3, "53"},
    new object[] {9, 4, "94"}
};

何でTestContextへの拡張メソッドという形をとっているかというと、TestContextから実行中クラス名とメソッド名が簡単に取れるから、です。この二つさえ分かれば、あとはリフレクション+LINQでやり放題ですからねー。クラス名が取れるTestContext.FullyQualifiedTestClassNameは、どうやらVS2010からの追加なようで、ナイス追加。VS2008からTest周りは何も進化しなくてNUnitと比べるとアレもコレも、な感じなわけですが、地味に改良はされていたんですね。いや、誰が嬉しいんだよって話ですが、私は大変嬉しかったです。

テストケースとして分けられないので、非常にオマケ的ではあるんですが、大変お手軽なので無いよりは嬉しいかな、と。

まとめ

Assert.HogeHogeの楽しくなさは異常。本当にダルい。かといって、英語的表現を目指した流れるようなインターフェイスは爆笑ものの滑稽さ。ああいうのをDSLと言うのは止めましょうぜってぐらいの。まあ、そんなわけで、これを使うと結構快適に書けます。Isの一つ一つは馬鹿みたいに単純なたった一行のメソッドなんですが、それだけなのに、全然違うんですよね。

というわけでまあ、楽しいテスト生活を!あと、最近は自動化テストなPexや、それと絡むCodeContractsを触っているので、それらについて近いうちに書けたらと思っています。

Rx FromEvent再訪(と、コードスニペット)

最近の私ときたらFromAsyncでキャッキャウフフしすぎだが、Asyncの前にEventを忘れているのではないか、と。というわけで、FromEventについて、「また」見直してみましょう!延々と最初の一歩からひたすら足踏みで前進していないせいな気はその通りで、いい加減飽きた次に進めということだけれど、まあそれはそのうち。私的にはFromEventはとうに既知の話だと思い込んで進めているのであまり話に出していなかっただけなのですが、初期に出したっきりで、特にここ数カ月で(WP7出たりAsync CTP出たり昇格したり)でRxが注目を浴びるのが多くなってはじめましてな場合は、そんな昔の記事なんて知らないよねですよねー(Blogの形式は過去記事へのポインタの無さが辛い)。なわけなので定期再び。

「Rxの原理再解説」や「時間軸という抽象で見るRx」、というネタをやりたいのですが、長くなるのと絵書いたり動画撮ったり色々準備がという感じで中々書き進められていないので、先にFromEvent再訪を。

4オーバーロード

FromEventはイベントをReactive Sequenceに変換するもの。これはオーバーロードが4つあります。

// ただのEventHandlerを登録する場合はハンドラの+-を書くだけ
// 例はWPFのWindowクラスのActivatedイベント
Observable.FromEvent(h => this.Activated += h, h => this.Activated -= h);

使う機会は少ないかな? 実際はEventHandler/EventArgsだけのものなどは少ないわけで。

// WPFのButtonなどり
Observable.FromEvent<RoutedEventArgs>(button1, "Click");

これはサンプルなどで最も目にすることが多いかもで、文字列でイベントを登録するもの。記述は短くなるのですが、動作的にはイベント登録時にリフレクションで取ってくることになるので、あまり推奨はしない。じゃあどうすればいいか、というと

// 第一引数conversionはRoutedEventHandlerに変換するためのもの、とにかく記述量大すぎ!
Observable.FromEvent<RoutedEventHandler, RoutedEventArgs>(
    h => h.Invoke, h => button1.Click += h, h => button1.Click -= h);

ハンドラの+-を自前で書くわけですが、EventArgsと一対一の俺々EventHandlerへの変換関数も必要になっています。これはnew RoutedEventHandler() などとしなくても、 Invoke と書くだけで良いようです。最後のオーバーロードは

// EventHandler<T>利用のものって本当に少ないんですよね、こちらを標準にして欲しかった
Observable.FromEvent<TouchEventArgs>(h => button1.TouchDown += h, h => button1.TouchDown -= h);

EventHandler<T>のものはスッキリ書けます。

コードスニペット

conversionが必要なFromEvent面倒くさい。それにしても面倒くさい。WPFのINotifyPropertyChangedほどじゃないけれど、やはり面倒くさい。ジェネリックじゃない俺々EventHandlerどもは爆発しろ!デリゲートはEventHandler<T>とFuncとActionがあれば他は原則不要(ref付きが必要とか、そういう特殊なのが欲しい時に初めて自前定義すればよろし)。と、嘆いてもしょうがない。何とかしなければ。以前はT4でガガガガッと自動生成してしまう方法を紹介しましたが、少し大仰な感があります。もう少しライトウェイトに、今度は、コードスニペットでいきましょう。

// 普通に使うもの
Observable.FromEvent<$EventHandler$, $EventArgs$>(h => h.Invoke, h => $event$ += h, h => $event$ -= h)

// 拡張メソッドとして定義する場合のもの
public static IObservable<IEvent<$EventArgs$>> $eventName$AsObservable(this $TargetType$ target)
{
    return Observable.FromEvent<$EventHandler$, $EventArgs$>(
        h => h.Invoke, h => target.$eventName$ += h, h => target.$eventName$ -= h);
}

この二つです。二つ目の拡張メソッドのものは、ええと、大体の場合は長ったらしくて面倒なので拡張メソッドに退避させるわけですが、それを書きやすくするためのものです。利用時はこんな形。

class Program
{
    static void Main(string[] args)
    {
        var c = new ObservableCollection<int>();
        var obs1 = Observable.FromEvent<NotifyCollectionChangedEventHandler, NotifyCollectionChangedEventArgs>(h => h.Invoke, h => c.CollectionChanged += h, h => c.CollectionChanged -= h);
        var obs2 = c.CollectionChangedAsObservable();
    }
}

public static class EventExtensions
{
    public static IObservable<IEvent<NotifyCollectionChangedEventArgs>> CollectionChangedAsObservable<T>(this ObservableCollection<T> target)
    {
        return Observable.FromEvent<NotifyCollectionChangedEventHandler, NotifyCollectionChangedEventArgs>(
            h => h.Invoke, h => target.CollectionChanged += h, h => target.CollectionChanged -= h);
    }
}

rxevent -> TabTab か、 rxeventdef -> TabTab というだけで、この面倒くさい(長い!)定義が簡単に書けます。おっと、スニペットファイルはXMLなのでこれだけじゃ動きませんね。ベタ張りだと長いので、ダウンロードはneuecc / RxSnippet / source – Bitbucketの二つからどうぞ。そうしたら、ツール->コードスニペットマネージャーで追加してやってください。

今回、スニペットはSnippet Designerで作成しました。Visual Studioと統合されているので非常に書きやすくてGood。コンパイルがしっかり通る、万全な雛形をコード上で作ったら右クリックしてExport As Snippet。スニペットエディタ上に移ったら、置換用変数を選択してMake Replacement。それだけで出来上がり。あとはプロパティのDescriptionとShortcutを書くだけ。楽すぎる。もうスニペットを手書きとか馬鹿らしくてやってられません。これだけ楽だと、ちょっとした面倒事を片っ端からスニペット化していけるというものですねん。

アタッチ、デタッチのタイミングを考えよう

Rxで意外と困るのが、いつアタッチされているのか、デタッチされているのか、よくわからなかったりします。慣れると分かってくるのですが、最初は存外厳しい。そういう時は悩まずに、デバッガを使おう!

こういったように、アタッチのラムダ式、デタッチのラムダ式にそれぞれ縄を張れば、いつ呼ばれるのか一目瞭然です。悩むよりも手を動かしたほうがずっと早い。

ところでRxのいいところは、イベントのデタッチが恐ろしく簡単になったことです。そのお陰で今まであまりやらなかった、そもそも考えすらしなかった、アタッチとデタッチを繰り返すようなイベント処理の書き方が発想として自然に浮かび上がるようになった。少し簡単に書けるようになった、という程度じゃあそんな意味がない。Rxのように極限まで簡単に書けるようになると、スタイルが一変して突然変異が発生する。ある意味これもまた、パラダイムシフトです。オブジェクト指向から関数型へ?いえいえ、C#から、Linqへ。

まとめ

2/11にRx v1.0.2856.0 releaseとしてアップデートが来てました。大量更新ですよ大量更新!正式入りしたから更新ペースがゆったりになるかと思いきや、その逆で加速しやがった!ちなみに破壊的変更も例によって平然とかけてきて、Drainというメソッドが消滅しました(笑) 何の躊躇いもないですね、すげー。

代わりに、Christmas Releaseの時に消滅してWP7と互換がなくなった!と騒いだPruneとReplayは復活しました(なんだってー)。というわけで、再びWP7との互換は十分保たれたという形ですね、ヨカッタヨカッタ。そんなわけで、常に見張ってないと分からないエキサイティングさが魅力のRx、是非是非使っていきましょう。

実践 F# 関数型プログラミング入門

共著者の一人であるいげ太さんから献本のお誘いを受け、実践F#を献本頂きました。発売前に頂いたのですが、もう発売日をとっくに過ぎている事実!ど、同時期に書評が並ぶよりもずらしたほうがいいから、分散したんだよ(違います単純に遅れただけです、げふんげふん)

NuGetの辺りでも出しましたが、F#スクリプトは活用し始めています。いいですね、F#。普通に実用に投下できてしまいます、今すぐで、C#とかち合わない領域で。勿論、それだけで留めておくのはモッタイナイところですが、とりあえずの一歩として。実用で使いながら徐々に適用領域を増やせるという、なだらかな学習曲線を描けるって最高ぢゃないですか。

F#を学ぶ動機

最近流行りだから教養的に覚えておきたいとか、イベントがファーストクラスとか非同期ワークフローが良さそうなので知っておきたいとか、私はそんな動機ドリブンのつもりでしたが、そういう動機だと実に弱いんですね!そんな動機から発したもので完走出来たものは今まで一つもありません(おっと、積み本の山が……)。もっと具体的に甘受できる現金なメリットがないと駄目なんだ。そんな情けない人間は私だけではない、はず、はず。

というわけで、実際、動機付けが一番難しいのではないかと思います、「実践」するには。F#の場合「それC#で」という誘惑から逃れるのは難しく、正面から向かわないとでしょう。この図式と対比させられるJava-Scala間では、「それJavaで」とは口が裂けても言えなくて(Java……ダメな子)、学ぶことがそのままJVMの資産を活かしてアプリケーションを書けるというモチベーションに繋がりますが、C#は割とよく出来る子だから。そんなわけかないかですが、本書では、冒頭1章でF#手厚く説明されています。言語の歴史を振り返って、パラダイムを見て、F#とはどういう流れから生まれてきた言語なのか。丁寧だとは思います。

並列計算。マルチパラダイム。うーん、それだけだと請求力に欠けるよね、何故ならF#が関数型ベースのマルチパラダイム言語であるように、C#はオブジェクト指向型ベースのマルチパラダイム言語だから。

などとやらしくgdgdとしててもまあ何も始まらない。いいからコード書こうぜ!といった流れで2章で環境導入の解説(この解説は非常に役立ちでした、F# Interactiveのディレクティブ一覧や、__SOURCE_DIRECTORY__でパスが取れるとか、F#のソースコードの所在とか)で、あとは書く!と。なんとも雑念に満ちたまま読み始めたわけですが、読み始めるとグイグイ引きこまれました。なんというか、学ぶのに楽しい言語なんですよね、F#。

それと、Visual Studioに統合されたF# Interactiveがとんでもなく便利で。こいつは凄い。私、今までREPLって別にどうでもいいものと思っていたのですよ。コマンドプロンプトみたいな画面で一行一行打っていくの。REPLは動作が確認しやすくてイイとかいう話を耳にしては、なにそれ、そもそもメチャクチャ打ちづらいぢゃん、イラネーヨって。でもVSに統合されたF# Interactiveは、IDEのエディタで書くこと(シンタックスハイライト, 補完, リアルタイムエラー通知)とREPLの軽快さが合体していて、最強すぎる。しかもその軽快さで書いたコードはスクリプトとして単独ファイルで実行可能、だと……!F#スクリプト(fsx)素晴らしい。C#で心の底から欲しいと思っていたものが、ここにあったんだ……!

と、読み始めてたら、普通に楽しい言語だし、並列や言語解析といった大変なところに入らなくても実用的だしで、かなりはまってます。F#いいよF#。始める前に考えてた動機だとかなんて幻でしかなく、始めたら自然に心の奥から沸き上がってくるものこそ継続されるものだと、何だか感じ入ってしまったり。

パイプライン演算子

F#といったらパイプライン演算子。パイプラインは文化。と、いうわけかで、実際、私がよく目にしているF#のコードというのは基本|>で繋ぐ、という形であり、それが実にイイなー。などという憧憬はあるのでF#を書くとなったらとりあえずまずはパイプライン演算子の学習から入ったりなどしたりする。

このパイプライン演算子、書くだけならスッと頭に入るけれど、どうしてそう動くのかが今一つしっくりこなかった、こともありました。関数の定義自体は物凄くシンプルでたった一行で。

// |>は中置換なのとinlineなので正確には一緒ではないですが、その辺は本を参照ください!
let pipe x f = f x

おー、すんごくシンプル。シンプルすぎて逆にさっぱり分からない。型も良くわからない。困ったときはじっくり人間型推論に取り組んでみますと、まず、変数名はpipeであり、二引数を持つから関数。

pipe : x? -> f? -> return?

まだ型は分からないので?としておきます。右辺を見るとf x。つまりfは一引数を持つので関数。

x? -> (f_arg? -> f_ret?) -> return?

fの第一引数はxの型であり、fの戻り値が関数全体の戻り値の型となるので

x? -> (x? -> return?) -> return?

これ以上は型を当てはめることが出来ず、また、特に矛盾なくジェネリクスで構成できそうなので

'a -> ('a -> 'b) -> 'b

となる('aがC#でいう<T>みたいなものということで)。なるるほど、あまりに短いスパッとした定義なので面食らいますが、分かってしまえばその短さ故に、これしかないかしらん、という当たり前のものとして頭に入る、といいんですがそこまではいきませんが、まあ使うときは感覚的にこう書けるー、程度でいいので大丈夫だ問題ない。

このパイプライン演算子をC#で定義すると

public static TR Pipe<T, TR>(this T obj, Func<T, TR> func)
{
    return func(obj);
}

比較するとちょっと冗長すぎはします。とはいえ、この拡張メソッドは、これはこれでかなり有益で、例えばEncodingのGetBytesなどを流しこんだりがスムーズに出来ます。例えばbyte[]の辺りは変換後に別の関数を実行して更に別の、という形で入れ子になりがちで、かといって変数名を付ける必要性も薄くて今一つ綺麗に書けなくて困るところなのですが、パイプライン演算子(モドき)さえあれば、

// ハッシュ値計算
var md5 = "hogehogehugahuga"
    .Pipe(Encoding.UTF8.GetBytes)
    .Pipe(MD5.Create().ComputeHash)
    .Pipe(BitConverter.ToString);

// B6-06-FC-CF-DC-99-6D-55-95-B8-B6-75-DB-EE-C8-AE
Console.WriteLine(md5); // Pipe(Console.WriteLine)でもいいですね

気持ちよく、また入れ子がないため分かりやすく書けます。そのためC#でも最近は結構使ってます。ただ、Tへの拡張メソッドという影響範囲の大きさは、相当な背徳を背負います。というかまあ、共同作業だと、使えないですね、やり過ぎ度が高くなりすぎてしまって。パイプはC#のカルチャーでは、ない。うぐぐ。F#なら

"hogehogehugahuga"
|> Encoding.UTF8.GetBytes
|> MD5.Create().ComputeHash
|> BitConverter.ToString
|> printfn "%s"

このようになりますね。「|>」という記号選びが実に絶妙。ちゃんと視覚的に意味の通じる記号となっているし、縦に並べた際の見た目が美しいのが素敵。美しいは分かりやすいに繋がる。

"hogehogehugahuga"
|> (Encoding.UTF8.GetBytes >> MD5.Create().ComputeHash >> BitConverter.ToString)
|> printfn "%s"

翌々眺めると、関数が並んでるなら合成(>>演算子)も有りですね!びゅーてぃほー。

LINQ

関数型言語といったら高階関数でもりもりコレクション処理であり、そしてそれはLinq to Objectsであり。F#ではSeq関数群をパイプライン演算子を使って組み上げていきます。

[10; 15; 30; 45;]
|> Seq.filter (fun x -> x % 2 = 0)
|> Seq.map (fun x -> x * x)
|> Seq.iter (printfn "%i")

filterはWhere、mapはSelect、iterは(Linq標準演算子にないけど)ForEachといったところでしょうか。

new[] { 10, 15, 30, 45 }
    .Where(x => x % 2 == 0)
    .Select(x => x * x)
    .ForEach(Console.WriteLine); // 自前で拡張メソッド定義して用意する

比べると、|>はドットのような、メソッドチェーンのような位置付けで対比させられようです。違いは、パイプライン演算子のほうが自由。例えば、拡張メソッドとして事前にかっきりと定義しなくてもチェーン出来る。だから、F#にSeq.iterなどがもしなかったとしても、

[10; 15; 30; 45;]
|> (fun xs -> seq {for x in xs do if x % 2 = 0 then yield x * x })
|> (fun xs -> for x in xs do printfn "%i" x)

その場でサクサクッと書いたものを繋げられたり、yieldもその場で書けたり(まあこれは内包表記に近くC#だと別にクエリ式でもいいし、といった感じで意味はあまりないですが)実に素敵。しかし、自由には代償が必要で。何かといえば、補完に若干弱い。シーケンスの連鎖では、基本的には次に繰り出したいメソッドはSeqなわけで、一々Seq.などと打つまでもなくドットだけでIntelliSenseをポップアップさせて繋げていくほうが楽だし、ラムダ式の記法に関してもfunキーワードが不要な分スッキリする。

この辺は良し悪しではなくカルチャーの違いというか立ち位置の差というか。C#のほうがオブジェクト指向ライクなシンタックスになるし、F#のほうが関数型ライクなシンタックスだという。同じ処理を同じようにマルチパラダイムとして消化していても、優劣じゃない差異ってのがある。これは、私は面白いところだなーと思っていて。どちらのやり方も味わい深い。

最後に

C#単独で見た時よりも.NETの世界が更に広く見えるようになったと思います。あ、こんな世界は広かったんだって。今後は、ぽちぽちとF# Scriptを書きつつ、FParsecにも手を出したいなあといったところですね。

大して書けはしませんが、それなりに書き始めて使い出せている、歩き始められているのは間違いなく本書のお陰です。「こう書くと良いんだよ」という、誘導がうまい感じなのでするっと入れました。どうしてもC#風に考えてしまって、それがF#にそぐわなくてうまくいかなくて躓いたりするわけですが、そこで、ここはこうだよ、って教えてくれるので。ちょっとした疑問、何でオーバーロードで作らないの?とかの答えは、載っています。

それと最後にクドいけれど、Visual Studio統合のF# Interactiveは本当に凄い。C# 5.0のCompiler as a ServiceはこれをC#にも持ってきてくれることになる、のかなあ。

Linq to ObjectsとLinq to Xmlを.NET 2.0環境で使う方法

LinqのないC#なんて信じられない。カレールゥのないカレーライスみたいなものです。しかし.NET Framework 2.0ときたら……。幸いなことに、開発はVisual Studio 2008以降で、ターゲットフレームワークを2.0に、とすることでvarやラムダ式は使うことが可能です。拡張メソッドも小細工をすることで利用可能になります。といったことは、C# 3.0 による .NET 2.0 アプリケーション開発 - XNA で LINQ を使おう - NyaRuRuの日記に書いてありますねん。あと足りないのはLinqを実現するEnumerableクラス。その辺のことはNyaRuRuさんの日記の追記のほうにもありますが、LINQBridgeを使うことで拡張メソッドのための小細工なども含めて、全部面倒見てくれます。つまり、Linq to Objectsを.NET 2.0で使いたければLINQBridge使えばいい。以上。

というだけで終わるのもアレなので、Linq to Objectsが使えるなら、Linq to Xmlも使いたいよね?Linq to Xmlの強力さを一度味わったら二度とXmlDocumentも使いたくないし生のXmlReader/Writerも使いたくないし。でも残念なことにLINQBridgeはto Objectsだけ。となれば自前再実装、は無理なので、そこは.NET Frameworkのオープンソース実装のMonoからソースコードをお借りすればいいんじゃなイカ?

Monoのソースコードはmono/mono - GitHubで管理されています。私はGitじゃなくてMercurial派なのでGitは入れてないのでPullは出来ない、ので、普通にDownloadsからzipで落としました。クラスライブラリはmcs\class以下にあります。まずはEnumerableから行きましょう。

Linq to Objects

新規にクラスライブラリプロジェクトを立てて(プロジェクト名は何が良いでしょうかねえ、私はMono.Linqとしました)、ターゲットフレームワークを2.0に変更。そしてSystem.Core/System.Linqをフォルダごとドラッグアンドドロップでソリューションエクスプローラに突っ込む。そしてコンパイル!

ふむ。華麗に673件のエラーが出てますね。どうやらExpressionsがないそうで。ふーむ、つまりQueryable関連ですねえ。Enumerableだけだと関係ないので削除してもいいんですが、せっかくなのでIQueryableも使えるようにしましょう!System.Core/System.Linq.ExpressionsもEnumerableと同じようにフォルダごとコピー。更にコンパイル!

するとまだまだ346件エラー。FuncがないとかExtensionAttributeがないとか。.NET 3.0で追加された拡張メソッド用の属性とかFuncがないわけですね。というわけで、それらも持ってきてやります。ExtensionAttributeはmcs/class/System.Core/System.Runtime.CompilerServicesにあります。Enumerableだけの場合はExtensionAttributeだけでいいのですが、Queryableも使う場合は他のクラスも必要になるので、ここもフォルダごとコピーしましょう。

もう一つの、FuncとActionはSystem.Core/SystemにFuncs.csとActions.csとして定義されているので、これらも持ってきます。なお、FuncとActionは#ifディレクティブにより.NET4以下の場合は4引数までのものしか使えないようになっていますが、.NET4からの16引数までのものも使いたければ、#ifディレクティブを削除すればOK。

これでコンパイルするとエラーはたった4つになります!ってまだエラーあるんですか、あるんですねえ。HashSetがないとか。HashSetで、ああ、集合演算系が使ってるもんねえ、とティンと来たら話は早い。こいつはSystem.Core/System.Collections.Generic/HashSet.csにあります。みんなSystem.Core下にあるので捜すの楽でいいですね。

コンパイルしたらエラーが増えた!HashSet.csのエラーですね。CollectionDebuggerViewとMonoTODOという属性が無いそうだ。よくは分かりませんが、名前からして大したことはなさそうだしたったの5つなので、削除してしまっても問題なく動きます。ので削除してしまいましょう。と言いたいんですが、せっかくなのでこの二つの属性も拾ってきます。この二つはSystem.Coreにはないし、正直見たこともない属性なので何処にあるのか検討付きません。というわけで、まあ検索すれば一発です。

System.Data.Linq/src/DbLinq/MonoTODOAttribute.cs、って随分変なとこにありますね、とにかくこれと、corlib/System.Collections.Generic/CollectionDebuggerView.csを持ってくる。

これで完成。コンパイル通る。動く。ターゲットフレームワーク2.0でもLinq!何も問題もなくLinq!ラムダもvarも拡張メソッドもある!うー、わっほい!C# 3.0で.NET Framework 2.0という奇妙な感覚が非常に素敵です。

Linq to Xml

ではLinq to Xmlも用意しましょう。といっても、やることは同じようにmonoのコードから拝借するだけです。mcs/class/System.Xml.Linq下にあるSystem.Xml.Linq, System.Xml.Schema, System.Xml.XPathをフォルダごとコピー。そしてコンパイルすると!例によってエラー。

XNameが見つからないそうで。んー、あれ、XNameは普通にLinq to Xmlの一部では?と、いうわけでSystem.Xml.Linq/XName.csを見に行くと、あー、#if NET_2_0で.NET2.0環境下では全部消えてる!しょうがないので、ここではソースコードを直に編集して#ifディレクトブを除去しちゃいます。

コンパイルは通りましたが警告ががが。CLSCompliantがついてないってさ。というわけで、Properties/AssemblyInfo.csにCLSCompliantを付けてやります。

[assembly: System.CLSCompliant(true)]

これで完成。Linq to Xmlが使えるようになりました!マジで!マジで。

ライセンス

ライセンスは大事。FAQ: Licensing - Monoで確認するところ、クラスライブラリはMIT X11 Licenseになるようです。かなり緩めのライセンスなので比較的自由に扱えるのではないかと思いますが、詳細はFAQならびにMIT X11 Licenseの条項を個々人でご確認ください。

まとめ

Linqがあれば.NET 2.0でも大丈夫。もう何も怖くない。まあ、実際.NET 2.0のプロジェクトを今からやるかといえば、これは最終手段であって、まずやることは全力で反対して.NET 4を採用させることでしょう。既存のプロジェクトに対する改修でLinqを突っ込むのは、うーん、そんなこと許されるんですか!許されるなら平気でやります!大抵は許されない気がしますが!

さて、.NET 4の人でもこれを用意する利点はあります。学習用に。シームレスにLinqの中へデバッグ実行で突入出来ます。挙動の理解にこれより最適なものはないでしょう。ソースコードを眺めるもよし、ですしね。それと、これを機にMonoに触れる、機会はWindowsな私だとあまりないのですが、ソースコードに触れてみるのも結構幸せ感です。mono独自のクラス(Mono.Xxx)も色々あって面白そう。

余談ですが、Windows Phone 7やSilverlightであのクラスがない!という状況もMonoの手を借りることで何とかなるケースも。(何とかならないケースは、依存がいっぱいで沢山ソースを持ってこなければならない場合。さすがにそう大量となるとどうかな、と)

.NETコードへデバッグ実行でステップインする方法

デバッグ実行といえば、Microsoftもソースコードを公開しています。.NET Framework Librariesで公開されてます。.NET 4をDownloadすれば、その中にあります。やたら階層が深くて迷ってしまいますが、EnumerableとQueryableは Source.Net\4.0\DEVDIV_TFS\Dev10\Releases\RTMRel\ndp\fx\src\Core\System\Linq にあります。Symbolをモニョッとすれば、Visual Studio内でもステップインでデバッグ出来ますねえ。というわけで、その解説もついでなので。

まず、オプション->デバッグ->全般で「マイコードのみ設定を有効にする」のチェックを外します。そして、「ソースサーバーサポートを有効にする」のチェックを入れます。この二つだけ(多分)。ちなみに、「.NET Frameworkソースのステッピングを有効にする」というなんともそれっぽいオプションは罠なので無視しておきましょう。

あとはデバッグ->シンボルでダウンロードした先のフォルダを指定すればOK。私はZ:\RefSrc4\Symbolsになってます。これで、F11でめくるめく.NET Frameworkの無限世界にステップインで帰ってこれなくなります!やり過ぎると普通に鬱陶しくなるので、その他のオプション類とかで適度に抑制しながらやりませう。

Reactive Extensionsを学習するためのリソースまとめ

1年半ほどDevLabsプロジェクトとして動いていたReactive Extensionsですが、ついにDevLabsを卒業し、Data Developer Center入りを果たしました。まずは、おめでとう!そして、これで安心してプロダクトに突っ込む事ができます。どれだけ有望そうに見えても、DevLabsのままではいつ消滅するか分からない。そういう先例(STM.NETがね、この死骸ページの虚しさ)もあった。また、Rxチームの前身はMicrosoft Live LabsでのVoltaというプロジェクトなわけですが、これは打ち切りでした!その魂はRxの可愛い鰻のアイコンで引き継がれ(Voltaからの継続使用)、ついに復活を遂げたという感動ストーリーがあるかないかは、特にない。それはともかくとして、私はこのアイコン好きです。

なお、Data Developer Centerはen-usja-jpの格差が激しいので、日本語情報が充実するかは不透明というか多分しないというか、せめてja-jpからだと辿れないテクノロジが幾つかあるのだけは何とかして欲しい、RxもそうだしStreamInsightなんかも……。

学習リソースまとめ

Data Developer Centerのページが何だかごちゃごちゃしているので、少し情報を整理します。

ここのTutorials & ArticlesにあるCuring the asynchronous blues with the Reactive Extensions。これがハンズオンラボになっていて、基礎からチュートリアル式に触りながら学べるようになっています。まずは、これをこなすとRxではどのようにプログラミングするのか、どのような問題を解決できるのか、というのが見えるはずです。for .NETとfor JavaScriptがありますが、内容は同じです。両方を見ることで、Rxという、Linqという層を設けることで言語を超えた統一的な思考・統一的な記述が可能になっているという世界を垣間見ることができます。

続いて同ページの左下、Documentation HighlightsにあるDesign Guidelines。このドキュメントは非常に重要で、Rxにおける原理原則注意点実装の詳細が書かれているので、最初から読む必要はないのですが、ある程度Rxに慣れ親しんだら絶対に読むべき代物です。日本マイクロソフトは是非これを和訳してください!

RxチームのBart De Smetによるプレゼンテーション。Rxチームもあちこちでプレゼンやっていて、色々なビデオが残っているのですが、これが一番お薦め。導入から深いところまで過不足なく説明されていて大変分かりやすいし、グッとくるかと思われます。また、Channel 9のRxタグには動画がいっぱいあります。新機能を足すたびに動画で説明していたので英語圏のほうでは分かりやすいのだろうけれど、それ以外にとってはむしろ分かりづらいんだよこんちくしょう!を繰り広げてましたので良く分からない機能があったらとりあえず動画見に行くといいのではないかと思われます。

これは、お薦めしません。ほんと最初期に立てられたWikiで、皆が試行錯誤な段階で例が書かれた感じで、どうもわかってない感漂う今一つな例ばかり。いや、そりゃ初期なのでしょうがないのですが、如何せんそれから以後誰も追加も更新もしない寂れた廃墟なので、これ見て学ぼうとするのは少しシンドイ。(私も含めて)お前ら編集しろよって感じなのですが、どうしてこうなった……。

Rxについての情報交換は公式フォーラムで行われています。Rxチームの人も出てきますし、常連みたいな人が何人か張り付いてコードぺたぺた貼ってくれているので、サンプル集的な意味でもお薦め(前述のWikiよりも遥かに!)。何か使い方の分からないメソッドがあれば、検索に入れてみれば、きっと解説とコードが出てくるでしょう。

Windows Phone 7にはRxが標準搭載されていますので、当然MSDNにもリファレンスがあります。WP7の開発環境が日本語化してくれれば、念願のRxでの日本語IntelliSenseが!なのですが、まだなのですよね、残念。ちなみに、このWP7版は少し前のものがベースになっているので、必ずしも現在の最新版と一致するとは限りません。WP7版が出てから追加されたものは入っていないし、中には削られたものも……。なお、リファレンス自体はインストールディレクトリ Progam Files\Microsoft Cloud Programmability\Reactive Extensions にchmで転がってます。

あまりお薦めしません(笑) 初期は「メソッド探訪XX回」というフォーマットでやろうとしていましたが今はそれは放棄して完全に好き放題書いてます。壊滅的に整理されておらず、非常に分かりづらい。日本語でのちょっと突っ込んだ情報はここしかないというのは悲しいことです。一応、幾つか並べてみれば

Reactive Extensions入門 + メソッド早見解説表
linq.js & Reactive Extensions for JavaScript(RxJS)入門
RxとAsync CTPでの非同期のキャンセル・プログレス処理
Reactive Extensionsとエラーハンドリング
Rxを使って非同期プログラミングを簡単に
Reactive Extensionsの非同期周りの解説と自前実装
メソッド探訪第7回:IEnumerable vs IObservable
Rx(Reactive Extensions)を自前簡易再実装する

といったところでしょうか(全然絞れてないですね、あうあう)。ちょっと非同期にお熱だったので、非同期系に偏りがちな、特に近頃は。重要な○○の解説を出してないので早く書きたい!と思っている事項が、まだかなり沢山残っているので、今年も積極的に更新を続けたいと思っています。あとは私の Twitter:@neuecc で小さいコード書いて貼りつけたり、Rx関連な話題が目についたら反応したりはしてます。たまに。答えられることは答えられますが答えられないことは勿論答えられないので、私がダンマリとしてたら、こいつ分かってねーな、ということで、ぐぬぬぬ。もしくは風呂で寝てます。

その他のリソース

Reactive programmingというパラダイムで見ることが出来るので、他の言語での動きから分かることというのも、いっぱいあります。

F#から。この論文の著者のTomas Petricekは非常に有名な方で、そもそもSupervisor: Don Syme, Microsoft Research Cambridgeですしね。146ページとボリューム十分。ですが、私は「読んでません」よ。俺、F#を学んだらこの論文読むんだ……。とか思ってはやンヶ月。ようやく重い腰が上がってF#はぢめました。やってみるとF#は非常に面白く、更に教養として身につけておく、的なわけじゃなく今すぐ普通に実用的なので、実践 F#読んで一緒に学びましょうー。F#お薦め。

F#はファーストクラスイベントとして、デフォルトでフィルタリング程度なら可能になっているしで、むしろネイティブ対応だぜ的な勢いもありますね。少し触った感じだとmapとfilterぐらいなので、あくまで軽く、程度ではあるようですが。あと非同期ワークフローが実に興味深く有益な機能。

非同期ワークフローといったら、こちらも。C# 5.0に入るかも、なAsync機能のCTP。残念ながら英語版VSにしか入らないので簡単に触れはしないのですが……。Rx自体にもこのAsync CTP対応(GetAwaiterの実装)や、System.Linq.AsyncとしてAsyncEnumerableの実装などをしていて、Async CTPとは、切っても切れない密接さを見せているのですが、機能的にやはり被りつつあるので、どう上手く切り分けるのか、というのが難しいところです。Async CTPはもう少し突っつきたいのですが中々時間取れずな今現在。

ScalaでのReactiveの実装になるようです。Wiki -> Design Guidelines in Japanese は実にためになります。作者はC++でのLinq(酷い形容ですがC#erの戯言なので許して!) であるOvenのかた。Enumerable-Observableみたいなことを感じつつそこはしかしC++もScalaも分からないのでもごもご。

概要編のほかEvent編、Behavior編が。Haskellはよくわからなくても雰囲気は分かる(ぉ

こうして俯瞰してみても、Rxは実用に踏み出しているという点で、一歩抜けてるのではないかと思います。

Rxの入手方法・パッケージ・DLL内容について

対応プラットフォームはいっぱいありますが、Get Reactive Extensions for .NETからRx for All Platformsを選択すれば全部インストールされますんで、それでいいと思われます。又はNuGetに対応しているので、それを使うのも良いでしょう。

画像はNuGetに登録されているRxのもの。多すぎである。NuGetでも多すぎて困るのですが、普通にインストールした場合は、やたら小分けされた大量のDLLをインストールしてくるので、何をどう選べばいいのかさっぱり分かりません。というわけで、それの解説を少し。

System.CoreEx // Scheduler,Disposableなどの必須クラス群
System.Observable // Silverlightのみ

NuGetだとRx-Core。System.ObservableはIObserver/IObservableのインターフェイス定義で、.NET4なら標準入りしているので不要ですがSilverlightでは、こちらの参照も必要になります。

System.Reactive // Observable拡張メソッド 

NuGetだとRx-Main。Observableの拡張メソッド群が入っているので、CoreExとReactiveがRxを使う際に必須となる参照と考えるといいです。

System.Reactive.ClientProfile // Stream周りをRxで非同期Readする補助拡張メソッド群
System.Reactive.ExtendedProfile // IHttpHandler使ってモニョモニョなサーバー利用での補助拡張メソッド群
System.Reactive.Testing // テストでのモックを作ったりする時に使いたいクラス群

この3つはオプション。ClientProfileはstreamにAsyncRead/AsyncReadLineというのが入っていて、そのまんまな挙動です。ちなみにAsyncReadLineはbyteをそのまんま切り出しているだけ(つまり日本語で使うとぶっ壊れる)のでまるっきり実用になってません。ふざけんな。というわけで使わないの推奨。

ただ、標準のRxだけだと、この辺のReadは自前で書かなければならなくて少々面倒くさいので、こういったあると便利系のものはあったほうがいい。恐らく、今後拡充されてここに追加されていくのではないかと思われます。私もReactive Extensions用のWebRequest拡張メソッドとか書いちゃってますが、標準でそういうの入ってくれれば手間なくて素敵。ExtendedProfileはよくわからないけどClientProfileと同じく作りは適当な気がする。Testingはあとで調べると思って放置中。でも概念は多分有益だと思うので、そのうちしっかり調べておきたい。

System.Interactive // Enumerable拡張メソッド(EnumerableEx)

こちらはRxとは独立していて、IEnumerableへの拡張メソッド群になっています。気の利いたのが色々入っていて便利。linq.jsにもここからパクッた、じゃなくて名前を統一させて入れたのが幾つかあります。あと、みんなが待望していたForEachがRunという名前で入っていますよ!それだけでもはや必須コンポーネントですね!

なお、突然Ixという表記を見たら、こちらのこと(もしくはIEnumerable全体)を指します。Interactive Extensions。用語としては、Pull-Enumerable-Interactive-IxとPush-Observable-Reactive-Rx となっています。紛らわしくよくわからなくなっても泣かない。

System.Linq.Async // AsyncCTP - Task連携

これもRxとは微妙に独立していて、中身はIAsyncEnumerable。AsyncCTPのTaskをLinq的に扱おうとするものです。Taskだと1個しか返せないので複数返せるように、という。私はイマイチこれの必要性が分からなかったりします。ぶっちゃけIObservableで良くて、で、IObservableのほうが色々融通が利くので。なんかもう出来るから作ったし入れたよ、といったRxチームのノリがここから伺えます。フットワーク軽くて、その姿勢は大好きだけど、混乱します。

最後に、Windows Phone 7では標準搭載されていて、System.ObservableとMicrosoft.Phone.Reactiveを参照することで使えるようになります。また、標準搭載とは別に、上記のような最新版も提供されています。標準搭載と最新版の違いですが、安定度は断然標準搭載版です。MSDNにドキュメントがあるのも良い。そして勿論、標準搭載なので配信する際の容量が嵩まない。では最新版のメリットはというと、勿論、機能面では豊富。また、デスクトップ版などとも完全にAPIの互換が取れます。ただ、バグの混入率は残念ながら結構高いので安定性は若干欠けます。

Rxの使える局面って?

ハンズオンラボやセッションのタイトルがCuring the asynchronous bluesであるように、やはり非同期に対しての適用に強い。クラウド時代のデータプログラミングに非同期は避けられない、それを解決するソリューションとしてのRx。しかしC# 5.0には組み込みAsync入っちゃうしRxJSだって、jQueryに1.5から組み込みでDeferredが入るので、将来的には強い特徴にはならないのですが、未来の前に現実。特にC# 5.0なんていつだよっていう。まあ、jQueryのDeferredよりもずっとセンス良いし(よく知らないで言ってる)、C#5.0 async/awaitよりも遥かに柔軟で強力だという印象を私は持っているので、直接競合するからってoutにはならないと思ってます。

非同期じゃなくイベントのほうは、様々な側面を見せるので一概に言うのは難しい。とりあえず時間を扱うテクノロジとしてのRxは、ベストな選択です。もはや生Timerなんて使ってられません。一例を出すと

// FileSystemWatcherのChangedイベントは一度の変更で複数のイベントが発行される面倒な仕様
var watcher = new FileSystemWatcher("C:\\", "test.txt") { EnableRaisingEvents = true };

// 1秒以内に連続して発生してきたイベントは無視して通すのは最後一つだけにする
Observable.FromEvent<FileSystemEventHandler, FileSystemEventArgs>(
        h => h.Invoke, h => watcher.Changed += h, h => watcher.Changed -= h)
    .Throttle(TimeSpan.FromSeconds(1)) // Throttleは指定時間、値の通過がなかった場合に最後の一つを通す
    .Subscribe(e => Console.WriteLine(e.EventArgs.FullPath));

FileSystemWatcherはファイルの変更を監視し、変更があった場合にイベントを飛ばしてくれる便利なクラスですが、例えば一度ファイルを保存する(変更がある)だけで、2,3個のイベントが同時に飛んできたりするなど処理が少し面倒だったりします。ググルとみんな困ってるよ。そういう場合にDateTimeを外部変数に保存しておいて比較を取るとかTimerで発火を調整するとか面倒くさいことをしなければなりません、が、RxならThrottleメソッドで一撃。

Rxではイベントの発生が時間軸上に並んでいるので、イベントに対して時間系魔法をかけることが出来ます。Throttle!Sample!Delay!TimeInterval!その他色々。未だかつてないほど時間を扱うのが容易になりました。面倒くさい印象のあったポーリングなども楽勝です。

他にイベントといったらセンサーの感知なども上げられます。Windows Phone 7に標準搭載された理由にもなるかもですが、センサーで飛んでくる大量のイベントのフィルタリング・加工にRxは実に都合が良い。また、物理デバイスがないとテストできない、あってもそもそもテストしにくいセンサー系APIですが、Rxを使うことでイベントのモック生成・差し替えが容易になります。

最後に挙げるイベント系といったら、GUI。ボタンクリックとか。これは、割とダメ。いや、ダメではないのだけど、GUIの基盤であるWPF/Silverlightががっちりとデータバインド中心に組まれているわけで、ここに無理やり割り込んでも、お互いの良さを消してしまう。フレームワークの根幹でサポートしている仕組みにRxは乗り入れられるほどのメリットを提供出来るか?というと、それは苦しいのではないかとも。Rxが標準入りすることで、WPFのフレームワーク根源からのサポートが入れば、また変わってきそう。これは未来の話。素敵な融合を夢みたい。

その他にデータの分配配信とか実行コンテキストの選択とか、メリット・特徴は色々あります。というか、なんでもかんでもが突っ込める基盤になってます。それらが渾然一体となって融合出来るというのがRxの本当の強さなのではないかと思っています。あらゆるソースをOrchestrate and Coordinateする。美しい……。

まとめ

正式になったから、それで何が変わったか、何が変わるかというとまだ不透明ではある。けれど、なにも変わらない気がします、とりあえず当面は。リリースパッケージが分かれるとかいうこともなく、なんか普通にDevLabsから引っ越ししてきました、というだけな雰囲気だし。そして、今まで通りに一月ぐらいの単位で新しいのリリースするサイクルを続けるのではないかなあ。まだ足したりなさそうだし。破壊的変更も、普通にたまにやってくるのではないかなあ。例えばIQbservableなんて、いつ名前が変わってもオカシクない。これは、QueryableObservableの略なのだけど、Observableと対比させるため同程度の長さの名前である必要があって、それでいてQbservableの本質をついた良い代案ってのは、出てこないよねえ。名前は難しい。

正式なテクノロジとして認められた、として、じゃあ今後どうなる?予想でしかないですが、まず.NET 4 SP1に入るか。これは、入らないんじゃないかなあ……。もし入ったとしても、変わらずData Developer Centerのページで最新版の開発と提供が続いていくでしょう。と、現時点でGetAwaiterが入ってたりするなど、C# 5.0 Asyncとの関係性も避けられない話なので、少なくとも.NET 4 SP1に入ったから開発終了には絶対にならないのではない。はず。.NET 5には入るでしょうが。確実に。

個人的にはWP7に搭載されている程度のものは.NET 4 SP1に入って欲しい(WP7のものは結構前のもの、リリース時期考えると当然ですが)ところなのですけれど、WP7のものとシグネチャ合わないのが出てきちゃってるのが、少々難しいかもなー、と、思うところ。一度フレームワーク本体に入れると変更が効かなくなるので、Rxチーム的にはもう少し手元に置いて弄りたいと思ってるような気がします。見ててそう思うというだけで、的外れな可能性はありますよ、あくまで私の予想なので悪しからず。

私としては、これで日本マイクロソフトにも動きが出て翻訳とか出たりしてくれると嬉しいのだけど。Rx Design Guidelinesなどは非常に重要な資料なので……。

ともあれ、使用に当たっての最大のリスク(テクノロジそのものが消滅する)というのがなくなったので、実プロジェクトに突っ込むことも十分検討できる範囲に入ってきました。実際に使ってみた、の記事が読めるようになってくと嬉しいですねえ。私は、ええと、機会があれば……。もうすぐ実際に突っ込める機会があるかもなので、なにか出せればいいか、な。サイの転がり方次第では分からないけれど。

ReactiveOAuth ver.0.2.0.0

ReactiveOAuthを更新しました。今回よりNuGetに対応したのでReactiveOAuth、もしくはReactiveOAuth-WP7で入れられます。あとSilverlightもサポートしました。そして、盛大にバグってたのを修正しました。UrlEncodeをそのまんまの使ったのでRFC2396でRFC3986じゃないから「!」とかが含まれた文章を投稿すると死ぬ、という。あまりにも限りなく初歩ミスで、死ぬほど反省します……。

おまけに、この辺りがマズいのは出した当初から薄々感づいていたのですが、「あとで直す」とか思って延々と今まで放置していたので、もう本当に本当にごめんなさい。リリース用にzip固めるスクリプトも書いた(fsxで)し、ディレクトリ周りも整理したしで、負担なくリリースしてける態勢を整えたので、もう放置しません、今後他のプロジェクトでも。本当に、今回はあまりにも酷かったのでなんともかんともです。

一応、通信部分を以前書いたReactive Extensions用のWebRequest拡張メソッドに載せ替えたりしたりなど、中身も変えたんですが、うーん。解説する気力が折れたぽ。

とりあえずドッグフードな体制を整えるためにXboxInfoほげほげにとっとと積んでしまうかな……。

NuGetパッケージの作り方、或いはXmlエディタとしてのVisual Studio

linq.js 2.2.0.2をリリースし、今回からNuGetでも配信されるようになりました!linq.js、もしくはlinq.js-jQuery、linq.js-Bindingsで入りますので、是非お試しを。ちなみに更新事項はちょっとBugFixとOrderByの微高速化だけです(本格的な変更は次回リリースで)。

さて、そんなわけでNuGetに対応したので、今回はNuGetのパッケージの作り方、公開のしかたについて解説します。やってみると意外と簡単で、かつNuGetいいよNuGet、と実感出来るので、特に公開するようなライブラリなんてないぜ!という場合でも試してみるのがお薦め(参照先としてローカルフォルダも加えられる)。普通に小さなことでも使いたくなります。そういえばでどうでもいいんですが、私は「ぬげっと」と呼んでます。GeForceを「げふぉーす」と呼ぶようなノリで。ぬげっと!

NuGetを使う

NuGetとは何ぞやか、大体のとこで言うと、オンラインからDLLとかライブラリをサクッと検索出来て、依存関係(これのインストールにはアレとソレが必要、など)を解決してくれた上で参照に加えてくれて、ついでにアップデートまで管理してくれるものです。Visual Studioの拡張として提供されているので、インストールはCodePlexからでもいいですが、VSの拡張機能マネージャからNuGetで検索しても出てきます。

インストールすると参照設定の右クリックに「Add Library Package Reference」というのが追加されていて、これを選択すると、トップの画像のようなNuGetの参照ダイアログが出てきます。最初NuGetが喧伝されていたときはPowerShellでのConsoleでしたが、ご覧のようにGUIダイアログもあるので安心。Consoleのほうが柔軟でパワフルな操作が可能なのですが(PowerShellを活かしたパイプやフィルタで一括ダウンロードとか)、普通に参照してーアップデートしてー、程度ならば別にGUIでも全然構いませんし。

.nupkg

NuGetを通してインストール/参照を行うと、プロジェクトのフォルダにpackages.configが生成されています。しかしこれはどうでもいいのでスルー。.slnのあるディレクトリにpackagesというフォルダも生成されていて、実体はこちらにあります。そこにはパッケージ名のフォルダが並んでいて、中には.nupkgという見慣れないものと、libもしくはContentというフォルダがあるのではないでしょうか……?

nupkgが最終的に作らなければならないもので、実態はただのzip。nupkgと同フォルダにあるlib/Contentはnupkgが展開された結果というだけです。というわけで、適当なパッケージをダウンロードして(linq.jsとかどうでしょう!)zipにリネームして解凍するとそこには……!

_relsとか[Content_Types].xmlとか、わけわからないものが転がってます。これらはノイズです。ようするに、System.IO.ZipPackageを使って圧縮してるというだけの話ですねー、恐らくこれらがある必然性はないです。ただたんに、.NET Framework標準ライブラリだけでZipの圧縮展開をしようとすると、こうしかやりようがなかったという、ただそれだけです。だから早くZipライブラリ入れてください(次辺りに入るらしい)。

大事なのは、.nuspecです。

.nuspec

.nuspec(中身はXml)に、バージョン情報やID、依存関係などが記載されています。といったわけで、自分で作らなければならないのは.nuspecです。これにパッケージしたいファイルや配置場所などの定義を記述し、それをNuGet.exeというものに通すと.nupkgが出来上がる、といった流れになっています。

nuspecの記述には、既存のnuspecを見るのが参考になるかもでしょう。但し、既存のnupkgを落として展開した結果のnuspecはNuGet.exeを通された時点で再加工されているものなので(パッケージ用のファイルの場所などの情報は消滅してる←まあ、絶対パスで記述可能だったりするので消滅してないと逆に困るわけですが)、100%そのまんま使える、というわけではないことには少し注意。

XmlエディタとしてのVisual Studio

では、nuspecを書いていく、つまりXmlを書いていくわけですがエディタ何使います?勿論Visual Studioですよね!Visual Studioは最強のXmlエディタ。異論はない。えー、マジXmlを補完無しで書くなんてシンジラレナーイ!小学生までだよねキャハハ。というわけで、補完全開で書きます。補完さえあればリファレンスなくても書けるし!IntelliSense最強説。

そのためにはスキーマが必要なわけですが、ちゃんと用意されています。NuGet Documentationの下の方のReferenceの.nuspec File Schemaにスキーマがリンクされています。CodePlexのソースリポジトリに直リンクというのが色々潔いですな。

さて、適当に新規項目でXmlを作ったら、メニューのXML->スキーマのダイアログを開き、nuspec.xsdを追加してやりましょう。

そして、とりあえずは<とでも打ってやると補完に!--とか!DOCTYPEなどなどに並んでpackageというものが。これを選択すると、一気にxmlns="http..."と名前空間まで補完してくれて!更に更に書き進めれば……。入力補完は効くし、必須要素が足りなければ警告出してくれるしでリファレンスとか何も見なくても書ける。

これなら打ち間違えでエラーなども出ないし、完璧。Xmlなんて普通のテキストエディタで気合で書く、とか思っていた時もありました。もうそんなの無理げ。VSバンザイ。なお、FirefoxのアドオンのGUI定義などに使うXULのSchemaなども当然適用出来る - XUL Schema ので、まあ、補完のないテキストエディタなんて使ってたら死んでしまうです。

なお、毎回毎回、スキーマの追加参照するのは面倒くさいという場合は、VSの標準スキーマ参照ディレクトリにxsdを直に突っ込んでおくと、楽になれます。オプション->テキストエディター->XMLでスキーマ、で場所が設定出来ます(デフォルトは %VsInstallDir%\xml\Schemas のようで)

パッケージング

nuspecのリファレンスは.nuspec File Formatに。IDとかVersionとかしか書かないし、項目も少ないしネストもないので書き方というほど書き方はないです。参考までにlinq.js-jqueryのnuspecは

<?xml version="1.0" encoding="utf-8" ?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
    <metadata>
        <id>linq.js-jQuery</id>
        <version>2.2.0.2</version>
        <title>linq.js - jQuery Plugin Version</title>
        <authors>neuecc</authors>
        <owners>neuecc</owners>
        <requireLicenseAcceptance>false</requireLicenseAcceptance>
        <description>Linq to Objects for JavaScript. This version is plugin integrated with jQuery.</description>
        <language>en-US</language>
        <licenseUrl>http://linqjs.codeplex.com/license</licenseUrl>
        <projectUrl>http://linqjs.codeplex.com/</projectUrl>
        <tags>linq javascript jquery</tags>
        <dependencies>
            <dependency id="jQuery" version="[1.3.1,]"></dependency>
        </dependencies>
    </metadata>
    <files>
        <file src="../../jquery.*" target="Content\Scripts" />
    </files>
</package>

といった感じ。tagsはスペース区切りで入れておくと検索の時にそのワードで引っかかる。dependenciesは依存関係がある場合に記載。対象バージョンの書き方に関してはSpecifying Version Ranges in .nuspec Filesを見て書くべし。

filesは後述するNuGet.exe(コマンドラインツール)でのパッケージング時に参照するファイルを設定。何も記載しない場合はNuGet.exeの実行時引数で解決されるので、どちらでもお好みで、という感じですが、普通はこちらに書いておいたほうが楽な気はします。

ファイル指定のsrcではワイルドカードとして*が使えます。targetのほうは、nupkgにパッケージングされた時の階層の指定。この階層の指定は非常に重要です。「Content」の下に記載すると、プロジェクト直下に対象を配置します。この例では Scripts 下に「jquery.linq.js, jquery.linq.min.js, jquery.linq-vsdoc.js」が展開されることになっています。Scriptsというフォルダ名はjQueryに合わせてあります。勿論、対象は.csでも.txtでも何でも可。

では、普通のC#でのdllのように直下には.dllとか置いて欲しくないし参照設定にも加えて欲しい、という場合はどうするかというとtargetを「lib」にします。すると自動で参照設定に加えてくれます。この「Content」とか「lib」とかってのは名前で決め打ちされてますので、そーいうものだと思うことにしませう。

残るはパッケージ化。まずNuGetのトップからDownloadsタブ(Downloadボタンじゃなく)を選び、NuGet Command Line Toolをダウンロード。このNuGet.exeに対して引数「p ファイル名」でnuspecを指定してやればnupkgが出来上がります。私はnuspecと同じ階層にexeを置いて、ついでにbatファイルに

nuget p linq.js.nuspec
nuget p linq.js-jquery.nuspec
nuget p linq.js-bindings.nuspec

とか書いたのを置いて3個のパッケージを作ってます。この辺は好き好きで。

以上が基本的な感じです。ただたんに参照設定に加える、ファイルを配置する、以上のことをやりたい場合はインストール時にPowerShellスクリプトを実行、なども出来るので色々柔軟に手を加えられそうです。また、.NET Frameworkのバージョンによって参照させるファイルを変える、といったことはフォルダの構成を変えるだけで対応で可能です。例えば.Net4の場合は lib/Net4 に、Silverlightへは lib/SL4 に、といったような感じ。

といったルールなどはNuGet Creating a Packageを見るといいでしょう。また、バージョンのフォルダ分けがワケワカランという場合は既存のnupkgを展開してフォルダ構成を見てしまうのが手っ取り早いかも。Rx-AllやNewtonSoft.Jsonなどなど。

ローカル参照としてのNuGet

nupkgは別にオフィシャルのサーバーだけではなく、個人で立てたサーバーも参照出来ます。また、それだけでなく、ただたんにフォルダを指定するだけでもOKです。

作ったnupkgはこれでテスト可能です。また、頻繁に参照に加えるものはわざわざOnlineに繋げて取ってくるの重い!という場合では一度落としたnupkgをローカルに配置してしまうのも悪くないかもです。テストというだけじゃなく、これは普通に使えますね?今まで参照設定の共通化というとテンプレート作って、程度しかありませんでしたが、これならばいい具合に自由度の効いたものが出来そうです。社内/俺々フレームワーク置き場として活用できそう。

なお、現在は、ローカル参照のパッケージは、GUIのパッケージマネージャだとバージョンが上がってもUpdatesに現れなくてアップデート出来ません。Consoleならば現れるので、ふつーにバグのよう。で、報告されていましたし修正もされていた(今リリースされているのには反映されてないもよう)ので、次のリリースでは直ってるんじゃないかと思われます。

NuGet gallery

せっかく作ったパッケージはOnlineに乗せたいよね!NuGet galleryでパッケージの閲覧・登録・管理が出来ます。よーし、じゃあパパSign Inしちゃうぞー、Registerして、と。やってもいつまでたってもInvalid Passwordと言われてしまいます。あれれ……。

現在は管理者の承認が必要なようで David Ebbo: Introducing the NuGet gallery Registerしたら、Twitterの@davidebbo宛てにapproveして!と言わないとダメぽ。私は「Hi. I registered nuget.org, id is "neuecc" . plaease approve my account.」と、スペルミスしてる適当不躾な@を飛ばしたところ数時間後にSign In出来るようになりました。いつまで認証制なのかは不明ですが、いまんところそんな感じなようです。

まとめ

オンラインで簡単にDLLをインストール出来て便利!というのは勿論ありますが、ローカルで使ってみても存外便利なものです。ぬげっといいよぬげっと。NuPackからNuGetに名前が変わったときは、事情は分かる(NuPackは名前が被ってたらしい)けど、NuGetはないだろ、いくらなんでも。と、思ってたんですが、今は何かもうすっかり馴染んだ気がします。ぬげっと。ぬぱっけーじ。ぬすぺっく。

とりあえず私は今後作るのは勿論、今まで出してきたものも、順次対応させてNuGet galleryに登録していくのでよろしくお願いしま。勿論linq.jsもよろしくお願いしま。今回の2.2.0.1は表には何も更新されてない感じですが、裏側の体制を整えてました。

F#スクリプト(fsx)により、linq.jsからAjaxMinのdllを通し圧縮化と、ついでにjQueryプラグインを生成したり、これまたF#スクリプトでリリース用のZip圧縮をワンクリックで一発で出来るようにしたり。今まで手動でやっていた(そしてミスしまくってた!リリースから10分で撤回して上げなおしとか今まで何度やってきたことか)部分を完全自動化したので、もうミスはありません。そして、自動化されたことによりリリースはミス出すし面倒なので、もう少し色々やってからにするかー、とズルズル後回しにする心理がなくなりました。多分。きっと。NuGet対応したことだしで、当分はアクティブにアップデートしていきます!

そんなこんなでF#スクリプトはぢめました。素晴らしすぎる。あとVS2010とシームレスに完全統合されたF# Interactiveがヤバい。超凄い。こんなイイものがあったなんて……。というわけでF#書きたい欲とF#について色々書きたい欲が、ので次回は実践F#書評です、多分。いや、次々回かも。とりあえず近日中には。とにかくF#は絶対触るべきですね!

おまけ

と、いうわけで、生成を自動化します。F#スクリプトでdllのアセンブリ情報を読み込んでnuspecとnupkgを生成するものを書きました。

#r "System.Xml.Linq"

open System
open System.IO
open System.Diagnostics
open System.Reflection
open System.Xml.Linq

// 同ディレクトリにNuGet.exeを置いておくこと
// mainにはnuspecへの情報登録に利用するdllを、othersにはその他のものを;区切りで
// パスはこのスクリプトからの相対パス
let main = "bin/Release/ClassLibrary4.dll"
let others = ["bin/Release/System.CoreEx.dll"; "bin/Release/System.Interactive.dll"]

let pass p = Path.Combine(__SOURCE_DIRECTORY__, p)
let xn s = XName.Get(s)

// Load Assembly
type AssemblyInfo =
    { Id:string; Version:string; Description:string; Company:string }
    
let getAttr<'a> (asm:Assembly) = 
    asm.GetCustomAttributes(typeof<'a>, true) |> Seq.head :?> 'a

let info =
    let asm = Assembly.LoadFrom(pass main)
    let name = asm.GetName()
    { Id = name.Name;
      Version = name.Version.ToString();
      Description = (getAttr<AssemblyDescriptionAttribute> asm).Description;
      Company = (getAttr<AssemblyCompanyAttribute> asm).Company }

let filename = info.Id + "." + info.Version + ".nuspec"

// Build .nuspec
let nuspec =
    let file src = XElement(xn "file", XAttribute(xn "src", src), XAttribute(xn "target", "lib"))
    let delBlank = function "" -> "_" | x -> x
    XElement(xn "package",
        XElement(xn "metadata",
            XElement(xn "id", info.Id),
            XElement(xn "version", info.Version),
            XElement(xn "authors", delBlank info.Company),
            XElement(xn "description", delBlank info.Description)),
        XElement(xn "files",
            file main,
            others |> Seq.map file))

nuspec.Save(pass filename)

// output .nupkg
new ProcessStartInfo(
    FileName = pass "NuGet.exe",
    Arguments = "p " + filename,
    RedirectStandardOutput = true,
    UseShellExecute = false,
    WorkingDirectory = __SOURCE_DIRECTORY__)
|> Process.Start
|> fun p -> Console.WriteLine(p.StandardOutput.ReadToEnd())

DLLからVersionとかDescriptionとか取れてしまう、.NETのアセンブリがサクッと読み込めるF#いいわー。これだけだと情報は最低限なので、tagとかも入れたければ下の方のXElementを生成している部分に直書きで挟んでやればヨシ。スクリプトの軽快さは良いですね。なので設定というか読み込むファイルも先頭のほうで普通に直書きで指定しちゃっております。

そのまま使ってもいいんですが、ビルド後に実行するコマンドラインに指定してやれば一切の手間暇なく常にフレッシュ。おお、素敵。

XboxInfoTwit - ver.2.3.0.2

電源OFFなのにONということになって投稿し続けるという酷い暴走していました、ごめんなさい……。というわけで直しました。

解説(言い訳とも言う)

XboxInfoTwitはスクレイピングという、普通にブラウザで表示する際のページのHTMLを解析してデータを取り出しています。今回は、HTML側でほんのちょびっと変更があって、その影響をモロに被って暴走しました。Ouch!こういう、うまくデータが取れなかった場合を想定してちゃんとエラーにしないとダメなのですね。私は今回はその辺まったく対策取ってなかったので、……といった結果になってしまいました。ほんとすみません。

一応、次からは今回のようなのが原因の暴走はなくなりました。別の原因がもとに、ってのは、うむむ……。

l2o.js 世界最速分解解説

l2o.js、でググっても何も出てきませんね!じゃあl2o.jsって何?って話なのですが、その前に、Reactive Extensionsが12/24にChristmas Releaseと称してリリースされたわけで、RxJSにも若干更新が入っていました。

· Fix for scheduling in the Concat, Catch, and OnError operators.
· If operator can now be used without supplying an “else” case.

特に大したこともないわけでしたが。しかし、インストールディレクトリである Microsoft Cloud Programmability\Reactive Extensions\v1.0.2838.0\RX_JS を見ると、l2o.jsという見慣れないものが……?

l2o.js、つまり、Linq 2 Objects.js。ええ、ええ……。linq.jsとモロにガチにかちあいそうな匂いがします。RxJS全然更新しないなあ、やる気あんのかよお、JVGoghも辞めちゃったしー、とか思っていたのですが、その裏でこっそりとんでもないものを仕込んでいたようです。いざ出てみると、むしろやらなかったのが不思議なぐらいな。

では、使ってみましょう。ついでにlinq.jsと比較しましょう。

var array = [12, 21, 4, 5, 36, 3, 10];

// l2o.js
L2O.Enumerable.FromArray(array)
    .Where(function (x) { return x % 2 == 0 })
    .Select(function (x) { return x * x })
    .ForEach(function (x) { alert(x) }); // 144, 16, 1296, 100

// linq.js
Enumerable.From(array)
    .Where(function (x) { return x % 2 == 0 })
    .Select(function (x) { return x * x })
    .ForEach(function (x) { alert(x) }); // 144, 16, 1296, 100

名前空間はL2Oから。そこにRange, Repeat, FromArrayなどなどの生成子があり、あとはメソッドチェーンでクエリ演算子があり。linq.jsと完全に一致。……これはlinq.jsオワタ。

いや待て。私がlinq.jsを作ったときには既に3つぐらいLinq to Objectsライブラリはあったけれど、それのどれにも不満があったから、自分で作ったわけで、同じように書けるからといって内部のクオリティが保証されているわけではない。Rxチームが作ってるからといって生半可なものだったら許さんぞ、と、いうわけで中身を覗いてみました。

Minifyされてますが、改行/インデント整形を施すだけで十分読めます。変数難読化が入っても、構造がシンプルなので全然読める。と、いうか、そうして普通に読めるのは、linq.jsと構造がまるっきり一緒だからですが。……。一緒ですが。一緒ですねこれ。そりゃC#の忠実移植を目指して作ったlinq.jsなわけなので、l2o.jsも同じ目標に向かってるだろうから一緒になるのは当然なのですが当然すぎてlinq.jsの存在意義ががが。

中身をチラッと見てみましょう。Selectを、Minifyされていたので変数名は私の方で付け直しました。

// l2o.js
L2O.Enumerable.prototype.Select = function (selector)
{
    var source = this;

    return L2O.Enumerable.Create(function ()
    {
        var current, count = 0, enumerator;

        return L2O.Enumerator.Create(
            function () // MoveNext
            {
                if (enumerator === void 0) // initialize
                {
                    enumerator = source.GetEnumerator()
                }
                if (!enumerator.MoveNext())
                {
                    return false
                }
                current = selector(enumerator.GetCurrent(), count++);
                return true
            },
            function () { return current }, // GetCurrent
            function () { enumerator.Dispose() }) // Dispose
    })
}

// linq.js
Enumerable.prototype.Select = function (selector)
{
    var source = this;
    selector = Utils.CreateLambda(selector);

    return new Enumerable(function ()
    {
        var enumerator;
        var index = 0;

        return new IEnumerator(
            function () { enumerator = source.GetEnumerator(); }, // Initialize
            function () // MoveNext & Current
            {
                return (enumerator.MoveNext())
                    ? this.Yield(selector(enumerator.Current(), index++))
                    : false;
            },
            function () { Utils.Dispose(enumerator); }) // Dispose
    });
}

完全に一致。linq.jsのほうでは、MoveNextのほうに定型句のようにif(enumerator === undefined){初期化処理}を書くのが嫌だったので、そもそも別関数として隔離、Currentはどうせキャッシュを返すだけなのだから省略してMoveNextと統合させてしまえ(this.Yieldというメソッドがその辺を受け持ってる、yield returnっぽく)とか、細々としたのを入れていますが、実質的には一緒です。

なお、この辺のLinq to Objectsの仕組みは、先日紹介しましたが、JSでも一緒です。Selectなど拡張メソッは、以前の物(this = source)を内包したうえで、新しいオブジェクト(new Enumerable)を返し、GetEnumeratorによりEnumeratorを生成し、最初のMoveNextが呼ばれた時に初めて動作が始まる。

アルファ

現在l2o.jsはアルファ版というか、それ以前の状態と思われるので、今実践に投げ込むのはダメです。メソッド全然足りないしバグいっぱいあるし。具体的に挙げると、Rangeはこれだとオーバーフローしない?とかReverseが即時評価ですよー、とかDisposeが不徹底で機能してない場合がある、とか、いっぱい。

メソッドは基本的なのしかありません。OrderByや、それと集合系がごっそり抜けているので、普通に使う分にも困るぐらいなので。まあ、集合系は(Dictionaryがないので)ちょっと実装が面倒ではある……。そのために私はDictionaryを導入しています。neue cc - linq.js ver 2.1.0.0 - ToDictionary, Share, Let, MemoizeAll というのはver2.1と、つい最近からで、それまではDictionary導入してないが故にバグ持ちだったんですよね、恐ろしや……。

まあ、Script#からの生成だろうから、Dictionaryを持ってくるぐらいはお茶の子さいさいかもしれません。

まとめ

l2o.jsは、今はまだこんな風にやるよ、という骨組みを見せているだけに過ぎませんが、すぐに標準クエリ演算子は実装されるでしょう。勿論、歓迎すべきことです!私の心中が穏やかでないのもしょうがない話です!linq.jsはJSでのLinqライブラリでは最後発ですが、現在CodePlex調べでは、同種のライブラリで月々のDL数が最も多いところまで行きました。地道に続けていれば良いものはいつか認められる、という甘い幻想を少しだけ見させてもらったのですが(3/dayとか微々たるものな世界なのですけどね、そう考えるとLinqでJSというものは、今は需要が……。でも、ASP.NET MVCの普及と共に.NETerにJSでのでのLINQは、まだまだ需要が発生する余地はありますな)、公式で十分なクオリティのものが出てしまった以上は、ただある俺々ライブラリの一つとなる。

とはいっても、公式では出来ないこと、出来ない付加価値は、まだまだ幾らでも足せるはずです!現時点でも無名関数の文字列ショートカット、WSH対応、vsdoc、大量の拡張メソッド、jQueryプラグイン化とやってきたし、これらは公式では出せないはず。で、まだネタはあります。今思っているのはClosure CompilerのAdvanced Optimizations対応、任意で配列のprototype拡張の追加、ですねえ。特に後者は、ちょっとしたことには大分便利になるはずだと思っています。

あとは、もしかしたらlinq.jsがRxJSやl2o.jsに影響を与えたのではないか?と考えると、嬉しい話ですかねー。ふむふむ。ま、その辺も含めて今年のRx周りは激しく加速しそうですね。

LINQの仕組みと遅延評価の基礎知識

新年明けましておめでとうございます。その第一弾の記事は実践 F# 関数型プログラミング入門の書評にしようと思っていたのですが、もう少し時間がかかりそうなので、せっかくの年始は基礎から考えようということで、LINQと遅延評価について最初から解説します。まあ、何をもって最初だとか基礎だとか言うのも難しいので私的な適当な基準で。つまり役に立たな(ry。なお、ここではLinq to Objects、IEnumerable<T>の連鎖についてのみ扱いますので、IQueryableについてはまた後日というか実のところ私はQueryableは全然分かってなくてやるやる詐欺が今も続いているといううがががが。

メソッドチェーン != return this

例によって単純なコードで。

var query = Enumerable.Range(1, 10).Select(i => i * i).Take(5);
foreach (var item in query)
{
    Console.WriteLine(item); // 1, 4, 9, 16, 25
}

1から10を二乗したうちの先頭5つを出力という、それだけのコードです。foreachする場合のinの右側が長くなるのは個人的に好きじゃないので、わざわざ変数に置いたりするのをよくやるのですが、これは好みですかねえ。なのでリスト内包表記とかあんま好きじゃなかったりはする、記法的に。

それはともかく、ドットで繋げていると実体が隠れてしまいがちなので、分解します。

var rangeEnumerable = Enumerable.Range(1, 10);
var selectEnumerable = rangeEnumerable.Select(i => i * i);
var takeEnumerable = selectEnumerable.Take(5);
foreach (var item in takeEnumerable)
{
    Console.WriteLine(item);
}

変数だらけでゴチャゴチャして余計に分からない。良く分からないものは、とりま図で。

こうなってます。中に、一つ前のものを内包している新しいオブジェクトを返しています。メソッドチェーンというと所謂ビルダー的な、もしくはjQueryなんかを想像してしまってチェーン毎に内部の状態が変化して return this するか、もしくは完全に新しいものを生成して返す(array.filter.mapしたら.filterで完全に新しい配列が生成され返って、.mapでも、的な。DeepCopyも同じようなものですか)みたいなのを想像してしまう感もあるのですが、そのどちらでもない。中に仕舞い込んで新しい包を返す。実に副作用レスでピュアい。

このことはデバッガで確認出来ます。

面白いのはSelectの戻り値の型で、WhereSelectEnumerableIteratorとなっていて、名前のとおりWhereとSelectが統合されていたりします。これは、Where->Selectが頻出パターンのためパフォーマンス向上のためでしょうねえ。面白いですがユーザー的にはあまり気にすることではないので深追いしないで次へ。

Takeの戻り値であるTakeIteratorはsourceとして中にSelectの戻り値であるWhereSelectEnumerableIteratorを抱えていて、Selectの戻り値はRangeの戻り値であるRangeIteratorを、中に抱えています。という連鎖が成り立っていることがしっかり確認できました。Visual Studioのデバッガは大変見やすくてよろしい。

遅延評価と実行

hogeEnumerableに包まれている状態では、まだ何も実行は開始されていません。そう、遅延評価!このままWhereやSkipを繋いでも、新たなhogeEnumerableで包んで返されるだけで実行はされません。ではいつ実行されるかといえば、IEnumerable<T>以外の、何らかの結果を要求した時です。それはToArrayであったり、Maxであったり、foreachであったり。

foreachを実行した時の動きを、図(但し致命的に分かりづらい)で見ると……

まず最初は最外周のtakeEnumerableに対しGetEnumeratorを実行し、IEnumerator<T>を取り出します。そして取り出したIEnumerator<T>に対しMoveNextの実行をすると、その先ではまた中に抱えたIEnumerable<T>に対しGetEnumeratorでIEnumerator<T>を取り出し、の連鎖が大元(この場合はrangeEnumerable)に届くまで続きます。

大元まで届いたら、いよいよMoveNextの結果が返されます。trueか、falseか。trueの場合は、通常は即座に現在値(Current)の取得も行うので、Currentが根本から下まで降りていくイメージとなります。あとは、どこかのMoveNextがfalseを返してくるまで、その繰り返し。今回はRangeが10個出力、Takeが5個出力なので、Rangeが5回分余りますがTakeで列挙は途中打ち切り。falseを流して終了させます。SumやCountなど値を返すものは、falseが届いたら結果を返しますが今回はforeachなのでvoid、何もなしで終了。

イテレータの実装

ついでなので、動作の実態であるイテレータも実装します。単純な、0から10までを返すだけのものを例として。

public class ZeroToTenIterator : IEnumerator<int>
{
    private int current = -1;

    public int Current
    {
        get { return current; }
    }

    public bool MoveNext()
    {
        return ++current <= 10;
    }

    // 必要でなければ空でもいいや、という感じ
    public void Dispose() { }

    // TじゃないほうはTのほうを返すようにするだけでおk
    object System.Collections.IEnumerator.Current { get { return Current; } }

    // Resetは産廃なのでスルー、実装しなくていいです、Interfaceからも削られて欲しいぐらい
    public void Reset() { throw new NotImplementedException(); }
}

// 使うときはこんな感じでしょーか
// IEnumerator<T>利用時はusingも忘れないように……
using (var e = new ZeroToTenIterator())
{
    while (e.MoveNext())
    {
        Console.WriteLine(e.Current);
    }
}

IEnumerator<T>ですが、見てきたとおり、中核となるのはMoveNextとCurrentです、といってもCurrentはキャッシュした値を中継するだけなので、実質実装しなければならないのはMoveNextだけ(場合によりDisposeも)。

見たとおりに一行の超単純な、10超えるまでインクリメントでtrue、超えたらfalse。なんかとってもいい加減な感じで、falseだろうとMoveNext()を呼んだらCurrentの値がどんどん増加していっちゃって大丈夫か?というと、全然問題ない。と、いうのも、そういうのは利用側の問題であって実装側が気にする必要はないから。

MoveNextする前のCurrentの値は保証されていないので使うな、であり、MoveNextがfalseを返した後のCurrentの値は保証されてないので使うな、です。大事なお約束です。お約束を守れない人は生イテレータを使うべからず。Linqのクエリ演算子やforeachは、そんな他所事を考えないで済むようになっているので、それらを使いましょう。生イテレータを取得したら負けです(拡張メソッド定義時は除く、つまりライブラリ的な局面以外では避けましょう)

ちなみにStringのイテレータは列挙前/列挙後のCurrentへのアクセスで例外が飛び、List<T>は列挙前は0、列挙後も0にリセットされ、Enumerable.Rangeでは列挙後は最後の値が返る、といったように、実際に挙動はバラバラです。

実装側が守らなければならないルールは、MoveNextが一度falseを返したら、以後はずっとfalseを返し続けること。で、その観点で、このZeroToTenIteratorを見ると、実のところ全然ダメです。MoveNextがint.MaxValue回呼び出されるとcurrentがオーバーフローしてint.MinValueになって、つまりはMoveNextの結果もfalseからtrueに変わってしまいます。腐ってますね。殺害されるべき。誰がそんなに呼ぶんだよ、という感じに普段はあんま気にしないゆとりな私ですが、いえいえ、こういう時ぐらいは気にしたりします。

オーバーフローはうっかりで見落としがちなので、ヘラヘラゆとりゆとりと笑ってないで、普段から注意すべきだと自戒するこの頃。

まあ、今時はイテレータの手実装なんてする必要ないのですがね!シンプルなものならばLinqの組み合わせで実現出来ますし、そうでないものはyield returnを使えばいいので。手実装じゃなきゃダメなシチュエーションってなにかある、かなあ?

まとめ

「return thisじゃなくて新しいオブジェクトを返してる」「配列的なイメージで扱えるけれど実体はストリームのほうが近い」「デバッガ素晴らしすぎる」「生禁止」の以上四点でした。

Linq to ObjectsのJavaScript移植であるlinq.jsも同じ仕組みでやっているので、そちらのコードのほうがブラックボックスでなく、また、素直に書いているので分かりやすくお薦め、かどうかは、微妙なところですんがー。ブラックボックスになっている部分(yield returnなど)を表に出しているので(というか出さないと実装出来ない)余計分かりにくい感も。

で、基礎からのLinqといえば紹介したいシリーズが一つ。

LondonのGooglerでMicrosoft MVPでC# in Depthの著者でStack Overflowで凄まじい解答量を誇るJon Skeet氏が、BlogでReimplementing LINQ to Objectsと称して、これまた凄まじい勢いで再実装&超詳細な解説をやっているので必見です。

詳細、どころの話じゃなく詳細で大変ヤバ素晴らしすぎる。単純なサンプルコードと結果を貼ってメソッド紹介、などという記事とは一線を画しすぎるクオリティ。私もこういう記事を書いていきたいものです。こんな量とスピードの両立は超人すぎて無理ですが、今年は記事のクオリティは上げたいですね。

C# in Depth 2nd Editionはつい二ヶ月前に出たばかりで、内容も良さそうですね、読んでみたい本です。しかし私の手元には積み本がいっぱいで、とほほ。で、本といえばもう一つ、C# 4.0 Unleashedがもうすぐ(2011/1/14、明日だね)出ます。これは著者がBart De Smet氏なので大注目です。B# .NET BlogでキレキレのLinqコード、だけじゃなくILからSQLからあらゆる領域にエキスパートな凄さを見せているので超楽しみです。こちらは予約してあるので、届くのが本当に楽しみで(洋書なので届くのは月末予定のよう、F#本が読み終えた頃になる予定なのでちょうどいいー)。

Bart氏は大学時代はベルギーのMicrosoft MVP for Visual C#、その後MicrosoftのWPFチームに入り、現在はCloud Programmability Team、つまりRxを開発しているチームに入ってます。氏が入ってからIQbservableとかヘンテコなのが次々と上がってきて、ますます目が離せない状態に。PDC10ではLINQ, Take Two - Realizing the LINQ to Everything Dreamというセッションを行ってましたが、これは本当に必見。Linqの過去、そして未来を見る素晴らしいセッションでした。感動しすぎて3回ぐらい見直した。

LINQは今後「ますます」重要になるので、しっかり土台を固めて、未来へ向かおう!

2010年を振り返る

今年もありがとうございました。無事、更新を続けられました。一応毎年、31日は一年間を振り返っていたようなので、今年も振り返ることにします。

まず言えるのはもはやゲーマーじゃねえ、ということのようで、悲しいことに。ゲサイトじゃなくなったのはしょうがないとしても、プレイすらしなくなっていました……。今年は過去最高潮にゲームしてません。積みまくりです。こんな自分になるとは想像もつかなかったなあ。とか言いながらもこっそりGeometry Wars 2のEvolvedモードで日本3位ぐらい世界50位ぐらいなスコアに更新していたりはしました。全体的にゲームやらなかったぶん、ジオメトリだけはしっかりやったということで、ヨシとしますか。来年はもっとゲーマーでいたいですね。

なお、去年遊んだ中でのベストゲームはいりす症候群!です。10月頃に初めて知ったのですが、もう激ハマリ。フリーゲームですし、最高に面白いので皆様も是非。ゲームは勿論、ストーリー/演出面でも味わい深かった。大変素晴らしい。来年のゲームはPortal2に大期待、かな。そういえば、年末のついこないだに配信されたXbox LIVE インディーズ ゲームのREVOLVER360は、大変美しい映像と素晴らしいプレイ感覚で非常に面白いので現在プレイ中。

さて、ゲーム以外ではどんな一年だったかというと、キャッキャウフフ。過去の自分からじゃ全く考えられないぐらいキャッキャウフフした一年でした。そんなに沢山というわけじゃないですがC#系の会に出たりしました。そうやって実際に人と顔を合わせることでTwitter上でのコミュニケーションの輪も広がる、という効果でか、Twitterでの@率も高くなっていきました。おお、キャッキャウフフ。と、いうとアレですが、悪いことではないですよね。人付き合いは相当に苦手なのですが、幾分か何とかなってきたようにも思えます。いや、まだ全然ですが。徐々に徐々に。

C#erとしての成長度は、うーん。ちょっと鈍いかなあ。さすがに、初めてC#に触れ本格的にプログラミングを始めましたな2008年、ようやくプログラミングが分かってLinq最高ーとどんどん知識を吸収出来た2009年、に比べて鈍化してしまうのもしょうがないのかなー、とは思いつつも、その原因ってジャンルを広げられなかったからなのではないかなあ、と思います。結局今年もLinqですからね。来年もLinqな気がしますが。今年の成果物はlinq.jsをガッと書き換えたりDynamicJson作ったり、そこそこなくはないけれど、しかし1年分の仕事じゃあないのですよねえ。

来年

継続中の積みタスクの消化がー。DynamicJsonの更新とかReactiveOAuthの更新とかlinq.jsの更新とか。やるべきことは決まっているし、別にやり始めればそんなに時間のかかるものでもないので、とっとと手をつけて終わらせるべきなのである。と、11月頃から思っていたはずが手付かずでここまで来てしまった……。ということは、来年も永遠とこのままではフラグ。

何か作ると言い続けてやるやる詐欺が続いているのですが、手元にWindows Phone 7があるのが好機なので、それでSilverlightの学習と何か作るの達成を。と思ってますです。クライアントサイドでは。サーバーサイドは、ねえ。C#から離れてnode.jsを触るのが良いかな、とは思っていて。もしくは、Rxチームの次のミッションはサーバーサイドでの応用だと思うので、とりあえず追っかけてればそのうち時流に乗れるのではないかという楽観論。

というわけで、来年も暫くはReactive Extensionsとその周辺、プラスアルファで何か、といった感じでしょうか。興味あるのことは沢山ありますが、色々なことに手を出せるほどにはキャパシティの限界容量があまりないわけなので、現実を見つつ、着実に成長出来れば良いかな、と。まだあわてるような時間じゃない。と、言いたいところなのですが、私もあまり若いとも言えない年齢なので(一応まだ20台ですがー)、そろそろ焦ったほうがいい感じではある。やっぱ実務的な知識があまりにも欠けてるのよねえ、とほほ。来年は、少しその辺も見据えて動かなきゃいけないかもですね。

そんな感じですが、来年もよろしくお願いします。

Linqと総当り

各所でAdvent Calendarも終了し皆様お疲れさまでした。自分のよく知っている言語はもちろんですが、他の言語を見ていても非常に楽しめて良いですね、特に普段自分の書かない言語で、入門からちょっと突っ込んだものまでまとめて見れるというのは非常に良い機会でした。

そんな中で見かけたScalaによる リストモナドを使ってみる - みずぴー日記 という記事。おお、スッキリかっこよく求まりますね、Scalaカコイイ。さて、総当り。非決定性計算。リストモナド。といったら、Linq、クエリ式。そんな風に反射的に連想が成りたつならもう立派なLinq使いですね!いやまあ、クエリ式で総当たり - NyaRuRuの日記で読んだことがあるから、というだけの話なのですがー。ほとんど直訳でいけるとあるように、Scalaで書かれたsend + more = moneyもまた、直訳で書けました。

var digits = Enumerable.Range(0, 10);

var solve = from s in digits
            from e in digits.Except(new[] { s })
            from n in digits.Except(new[] { s, e })
            from d in digits.Except(new[] { s, e, n })
            from m in digits.Except(new[] { s, e, n, d })
            from o in digits.Except(new[] { s, e, n, d, m })
            from r in digits.Except(new[] { s, e, n, d, m, o })
            from y in digits.Except(new[] { s, e, n, d, m, o, r })
            where s != 0
            where m != 0
            let send = int.Parse("" + s + e + n + d)
            let more = int.Parse("" + m + o + r + e)
            let money = int.Parse("" + m + o + n + e + y)
            where send + more == money
            select new { send, more, money };

foreach (var item in solve) Console.WriteLine(item);

ちゃんちゃん。こういうの見ると、各言語は同じとこに向かってる感がありますね。微妙に表現は違いますが、同じ発想が通じるし同じように書ける。その点はC#もScalaもF#も同じパラダイム、同じ未来を目指して向かってるのではないかと思います。Javaは微妙に落伍している気がしますが、もう少し何とかなって欲しいものです。

と、いうだけなのもアレなので、 int.Parse("" + s + e + n + d) の部分について。数字を文字列的な並びとして解釈したい、というわけですが、手段をまとめるとこんな感じになるかしらん。

// という数字があった時に123にしたい
var x = 1;
var y = 2;
var z = 3;

// ダルい
var xyz1 = int.Parse(x.ToString() + y.ToString() + z.ToString());
// 複数ある時はこちらで
var xyz2 = int.Parse(string.Concat(x, y, z));
// コンパイル後の結果はxyz2と一緒
var xyz3 = int.Parse("" + x + y + z);
// 文字列変換しない
var xyz4 = x * 100 + y * 10 + z;
// ↑のは桁数が多い時に泣くのでこうしてやる
var xyz5 = new[] { x, y, z }.Aggregate((a, b) => a * 10 + b);

文字列にして並べてintに変換するのがお手軽なわけですが、.ToString()を並べていくのはダルいわけですよね!というわけで、そんな時はstring.Concatを使うと全部まとめて結合出来ます。しかし int.Parse(string.Concat と並ぶのすらダルい、とかいう不届き者は先頭に""と足し合わせることでstring.Concatで結合したのと同じ結果を得られます。これは、演算子は左から適用されていきますが、文字列と数値を+で足すと文字列として足される、以下繰り返し。の結果。キモチワルイといえばキモチワルイので、積極的に使うかというと悩ましいところですが……。

そもそも数字を扱うのに文字列に変えてー、とかが邪道だという話も割とある。効率的な意味でも。なので、そういうときは2の桁は10倍、3の桁は100倍……。とかやっていると桁数が多いときはどうするのどうもしないの?という話なので、Aggregateを使うという手もあります。Aggregateは一般的な関数型言語でいうfoldlに相当。左からの畳み込み演算。ところで、では右からの畳み込みはどうすればいいの?つまりはfoldrはどうなのか、というと、これは.Reverse().Aggregate() のようで。右からなら逆にすればいいぢゃない。

ところで、C#のLinqで出来ることはJavaScriptのlinq.js - LINQ for JavaScriptでも出来ますよ?やってみます?

var digits = Enumerable.Range(0, 10);

var solve = digits
    .SelectMany(function(s){ return digits.Except([s])
    .SelectMany(function(e){ return digits.Except([s, e])
    .SelectMany(function(n){ return digits.Except([s, e, n])
    .SelectMany(function(d){ return digits.Except([s, e, n, d])
    .SelectMany(function(m){ return digits.Except([s, e, n, d, m])
    .SelectMany(function(o){ return digits.Except([s, e, n, d, m, o])
    .SelectMany(function(r){ return digits.Except([s, e, n, d, m, o, r])
    .Select(function (y) { return { s: s, e: e, n: n, d: d, m: m, o: o, r: r, y: y} })})})})})})})})
    .Where("$.s != 0")
    .Where("$.m != 0")
    .Select(function(x){ return { 
        send: parseInt("" + x.s + x.e + x.n + x.d),
        more: parseInt("" + x.m + x.o + x.r + x.e),
        money: parseInt("" + x.m + x.o + x.n + x.e + x.y)}})
    .Where(function (x) { return x.send + x.more === x.money });

solve.ForEach(function (x) { document.writeln(x.send + ":" + x.more + ":" + x.money) });

C#クエリ式からの変換のポイントは、from連鎖をSelectManyの連鎖で、但しカッコは閉じず変数のキャプチャを内包したままで最後にSelectで一旦整形してやるというところです。正確なクエリ式の再現とはなりませんが、この程度ならば、まあ何とか書けなくもないレベルとなります(正確なクエリ式の変形結果の再現をやると手では到底書けないものになる)。

ちなみに総当りなので結構時間がかかってIEだと泣きます。Chromeなら、まあそれなりな速度で求まるかなー。

Rx Christmas Release 2010によるPublishの変化解説

メリークルシミマス。Happyなことに、Reactive Extensions for .NET (Rx)が更新されました。なんと、WP7に標準搭載されたことだし安定性・互換性はある程度保証されてきたよねー、とか思った側から完全に互換を崩壊させる素晴らしいBreaking Changesをかましてきました!常識では計り知れない大胆な所業。そこにしびれるあこがれれぅ。

さて、今回の変化はJoin/GroupJoin/Windowの新規搭載とPublish系の変更です。Joinについてはまた後日ということで、今回はPublishの変更について解説します。で、ですねえ、これはもう100%変わってます。昨日ちょうどPublishによる分配、とか書いたわけですが、出したコードは全部動かないです!素晴らしいタイミング!Ouch!

端的に言えばPublishは引数にISubjectを受け取るよう変化。Prune/ReplayはPublishに統合されたことにより消滅。何でかというと、元々、分配ソースにSubjectを使うのがPublish、BehaviorSubjectを使うのが初期値付きPublish、AsyncSubjectを使うのがPrune、ReplaySubjectを使うのがReplayでした。分配ソースを任意で受け取るようになったため、メソッドが分かれる意味がなくなったというわけですね。

ナンタラSubjectの解説は、まあまた後日にでも。簡単に言えばSubjectは普通のイベントと同じで素直な挙動、つまりOnNextしたら値が流れる。AsyncSubjectは非同期を模していて、OnNextされたら値を一つだけキャッシュし、OnCompletedされたらキャッシュした値を流す。以降はSubscribeされるたびにキャッシュした値を即座に流し、OnCompletedも発行。ReplaySubjectはOnNextされる度に値をキャッシュし、それを流す。Subscribeされるとキャッシュした値を全て即座に流す。BehaviorSubjectはOnNextされる度に最新の値一つのみをキャッシュし、それを流す。Subscribeされるとキャッシュした値を即座に流す。

イマイチ分かりづらいですね:) というわけで本当に詳しいことは後日で。AsyncSubjectだけは、今までにも、そういう挙動であることの意味とかしつこく書いてきましたが。

そして、IConnectableObservableが戻り値となるものはMulticastというメソッド名になりました。引数は勿論、ISubjectを受け取るという形に。では、昨日のコードで例を。新旧比較ということで。

Observable.Range(0, 10)
    .Select(_ => int.Parse(Console.ReadLine()))
    .Publish(new Subject<int>(), xs => xs.Min().Zip(xs.Max(), (min, max) => new { min, max }))
    .Subscribe(Console.WriteLine);

Publishの第一引数にSubjectを突っ込みました。第二引数にはそれで分配されたIObservableが渡ってくるという塩梅です。

var input = Observable.Range(0, 10)
    .Select(_ => int.Parse(Console.ReadLine()))
    .Multicast(new Subject<int>());

input.Min().Zip(input.Max(), (min, max) => new { min, max })
    .Subscribe(Console.WriteLine);

input.Connect();

こちらがMulticastです。まあ、普通にISubjectを受け取るようになったというだけで、今までのPublishでIConnectableObservableが返ってくるのと同じです。さて、ISubjectを受け取るということは、外部のISubjectを渡してもOKです。新しくこんな分配が可能になりました。

var input = new ReplaySubject<int>();

Observable.Range(0, 10)
    .Select(_ => int.Parse(Console.ReadLine()))
    .Publish(input) // 流れてくる値をReplaySubjectに保存する
    .Max()
    .Subscribe(i => Console.WriteLine("Max:" + i));

var inputArray = input.ToEnumerable().ToArray(); // 入力値を配列に変換

といっても、この例は全く意味がなくて、Max()で止めておいて、その戻り値をinputとして受ければいいだけの話なのですが。上手い利用例が浮かばなかったのです!まあ、色々と応用しどころというのは生まれてくるのではないかと思います。

まとめ

この破壊的変更ですが、私としては好意的に捉えたいです。如何せんPruneというメソッド名は意味不明でしたし、オーバーロードが8つもあるという状況も良くなかった。今回整理されたことで、使いやすくなったと思います。が、しかし、WP7版との互換性が切れてしまったのは相当痛い。今までusingをプリプロセッサディレクティブで#if WINDOWS_PHONE using Microsoft.Phone.Reactive という感じに切り替えてWPF/SL/WP7で互換を取っていたのですが、ここまで派手に互換性なくなるとなあ。

ちなみにWP7標準搭載"ではない"DevLabs版のRx for WP7というのも用意されているので、そちらを使えばいいわけなのですが、それはそれでどうかなー、どうかなー、困った話で。

それと、この変更はまだ追随されていませんが、そのうち System.Interactive(Ix.NET/EnumerableEx) や RxJS にも派生してくるような気がするので、再びAPI安定していない状態に戻った感がありますねー。要チェックで要注意で。Rxチームは大変なクリスマスプレゼントを贈ってきました。そして、Rxが.NET 4 SP1に入るの?入らないの?的な希望観測もあったわけですが、私個人の印象としては、SP1入りな可能性はなくなったな、という気がしてます。まだまだ作り替える気満々だもの、これ。

Reactive ExtensionsとPublishによるシーケンスの分配

Twitter上で見たお題、10個コンソールから整数の入力を受けて最少と最大を出す。Linqで書くならこうでしょうか。通常は0-10でfor文を回すところはRangeで。あとはMinとMax。int.Parseなので数字以外は例外飛んでしまいますが、その辺は無視で。

var input = Enumerable.Range(0, 10)
    .Select(_ => int.Parse(Console.ReadLine()));

var max = input.Max();
var min = input.Min();

はい。これだと20回入力させることになってしまいます。Maxで列挙され、Minでも列挙され。ダメですね。というわけで、配列に一度保存しますか。

var input = Enumerable.Range(0, 10)
    .Select(_ => int.Parse(Console.ReadLine()))
    .ToArray();

var max = input.Max();
var min = input.Min();

よろしい。しかしこれの列挙回数は3回です。ToArrayで一度、Maxで一度、Minで一度。しかもMaxとMinを出したいだけなのに、わざわざ配列に保存してしまっています。もし配列が巨大になる場合はメモリ的な意味でもちょっと嫌ですねえ。さて、そこで出番なのが、プッシュ型シーケンスを持つReactive Extensions。プッシュ型ならば分配が可能です。やってみましょう?

Observable.Range(0, 10)
    .Select(_ => int.Parse(Console.ReadLine()))
    .Publish(xs => xs.Min().Zip(xs.Max(), (min, max) => new { min, max }))
    .Subscribe(Console.WriteLine);

ふむ、これはクールだ……。Rxの格好良さは異常。

Publishは今までにも何度も出してきましたが、シーケンスを分配するメソッドです。今までは引数なしにして、IConnectableObservableを変数に受けて、それに対してポコポコ足して、最後にConnect。としていましたが、Publishには引数有り版も用意されています。引数有りの場合は分配されたIObservableが変数として渡ってくるわけです。つまり上のコードは

var input = Observable.Range(0, 10)
    .Select(_ => int.Parse(Console.ReadLine()))
    .Publish();

input.Min().Zip(input.Max(), (min, max) => new { min, max })
    .Subscribe(Console.WriteLine);

input.Connect();

と等しいということになります。ちなみにPublish内でSubscribeしたって構わないわけなので

Observable.Range(0, 10)
    .Select(_ => int.Parse(Console.ReadLine()))
    .Publish(xs =>
    {
        xs.Min().Subscribe(x => Console.WriteLine("min:" + x));
        xs.Max().Subscribe(x => Console.WriteLine("max:" + x));
        xs.Sum().Subscribe(x => Console.WriteLine("sum:" + x));
        xs.Average().Subscribe(x => Console.WriteLine("avg:" + x));
        return xs;
    })
    .Subscribe();

といったような書き方も可能ですね。では、min,max,sum,avgと4つを返したい場合はどうしよう。Join-And-Thenを使います。

Observable.Range(0, 10)
    .Select(_ => int.Parse(Console.ReadLine()))
    .Publish(xs => Observable.Join(xs
        .Min().And(xs.Max()).And(xs.Sum()).And(xs.Average())
        .Then((min, max, sum, avg) => new { min, max, sum, avg })))
    .Subscribe(Console.WriteLine);

この例だと単純に直列に繋いでいるだけなので、Zipと同じようにAndで繋いでThen。というだけですね。ちょいとゴチャッとしていますが。Joinの強力なところはAnd-Thenで作るPlan(Thenの戻り値)を複数受けられるという点にあります。これにより複雑な結合を生成する事ができます。ZipとMergeが混ざったような、でももう少し複雑な何か。といった雰囲気。

Publish

Publishはオーバーロードが8つもあって最初戸惑ってしまいますが、分類としては二つに分けられます(そしてそれぞれFuncを受け取る/スケジューラを受け取るの組み合わせで4つに分かれて2x4=8のオーバーロード)。一つは引数として初期値を受け取らないもの。もう一つは受け取るもの。今までは全部、受け取らないものを見てきました。内部的にはSubjectを使って分配しています。一方、初期値を受け取るものは内部的にBehaviorSubjectを使って分配します。というわけで、どんな挙動を取るのかよくわからない、という場合はBehaviorSubjectを試してみると良いでしょう!(何だこの解説する気ゼロな投げっぱなしな説明は)

ところでな余談

注意しないといけないのは、ふつーのシーケンスがあって、それをRxで分配やるとはっきし言って遅いです。列挙数が多くなると、ただ配列舐めるのに比べて数万倍のパフォーマンス差が出たりします。ほとんどのシチュエーションでRxで分配するぐらいなら二度三度舐めたほうが速いです。まあ、分配に限らずRxが普通に遅いということなのですがー。ご利用は計画的に。今回の例のConsole.ReadLineで、というのは、当然Rxのほうが速いですが。入力とリアルタイムに応答していますからね。Reactive!ようするところ、Rxが活きるのってそういうところ、かしらん。

Prev | | Next

Profile

Yoshifumi Kawai

Cysharp, Inc
CEO/CTO

Microsoft MVP for Developer Technologies(.NET)
April 2011
|
July 2027

X:@neuecc GitHub:neuecc

Archive