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

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

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

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

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

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

Cast

天下一プログラマーコンテスト、ほうほう、例題とな?

var input = "e4bba5e4b88be381aee69687e5ad97e58897e381af5554462d38e38292e69687e5ad97e382a8e383b3e382b3e383bce38387e382a3e383b3e382b0e5bda2e5bc8fe381a8e38199e3828b3136e980b2e695b0e381aee38390e382a4e38388e58897e381a7e38182e3828be38082";
var bytes = Regex.Matches(input, "..").Cast<Match>().Select(m => Convert.ToByte(m.Value, 16)).ToArray();
var result = Encoding.UTF8.GetString(bytes);
Console.WriteLine(result); // 以下の文字列はUTF-8を文字エンコーディング形式とする16進数のバイト列である。
Console.WriteLine(result.Length); // 41

題意に全然沿わずフレームワーク丸投げ。それにしても、古いクラス(MatchCollectionとか)はIEnumerable<T>じゃないのでCast<T>が必要で面倒くさい。Castは、その段階で「型を意識」しなければならないのが思考の流れを阻害する。「Select(m=>m.」と打つと、ああ、送られてくるのはMatchなのかあ、って分かる。それ以上には考えさせないで欲しい。型推論万歳。型推論は、JavaScriptを始めとしたゆるふわ加減の心地良さと、型キッチリでコンパイルしてエラーが出て弾かれて万歳の気持ちよさがちょうどよく混ざり合ってて、イイなーって思う。だからというわけじゃないけど、なんでもvarで済ませてます、私は。var hoge = 3とか平気で書く。

そういえば、AchiralはMatchとMatchCollectionに、Selectのみ拡張メソッドで足してあるので、Castを書く必要なくスムーズに繋げられる。この面倒なMatchCollectionをどうしたものか、という解答として丁度良い妥協バランスで、なるほどなるほどでした。Achiralは読んでて本当にためになります。

追記:続き→neue cc - Linqで文字のバイト単位でグループ分け

IEnumerableに文字列連結

C#と諸々 10分でコーディングから。ネタ元、のネタ元の問題文の解読しづらさは異常。例も酷かった。というのはともかく、Linqならグルーピングのお話だよねー

class Cards
{
    public string[] Deal(int numPlayers, string deck)
    {
        var count = deck.Length / numPlayers;

        var result = deck
            .Select((c, i) => new { c, i })
            .GroupBy(t => (t.i % numPlayers), t => t.c)
            .Select(g => new String(g.Select(c => c).ToArray()))
            .Select(s => (s.Length > count) ? s.Substring(0, count) : s)
            .ToArray();

        return (result.Length < numPlayers)
            ? Enumerable.Repeat("", numPlayers).ToArray()
            : result;
    }
}

あれ、スッキリどころか意外と汚い……。んー、最初はGroupByのところがToLookupでそのままreturnしてすっきり終了。だったのですが、「求められるのはIGroupingじゃなくて文字列」なのでLookupじゃなくGroupByにして後段のSelectで結果を文字列化。「余ったカードは配られず捨てられる」ので更に後段のSelectで廃棄処分。「カードが足りなかった場合は人数分の空文字列配列を返す」のでreturnを別に分けてRepeatでそれを生成。後付けでごにゃごにゃ足していった結果、汚くなってしまった、残念。

まあでも、何が嫌かってnew Stringの部分ですね。中身がcharじゃなくて文字列の時はstring.Joinを使いますが、どちらもToArrayがウザくなってイヤー。IEnumerableにToStringは文字列で連結して欲しい。いや、Aggregateで代替出来るのは知ってますが定型処理のくせに必要なタイプ数多くてnew Stringとあんま変わらないし、セパレータも付けたいとなるといよいよ面倒くさい。というわけで拡張メソッドの出番。

public static class Ext
{
    public static string ToJoinedString<T>(this IEnumerable<T> source)
    {
        return source.ToJoinedString("");
    }

    public static string ToJoinedString<T>(this IEnumerable<T> source, string separator)
    {
        var index = 0;
        return source.Aggregate(new StringBuilder(),
                (sb, o) => (index++ == 0) ? sb.Append(o) : sb.AppendFormat("{0}{1}", separator, o))
            .ToString();
    }
}

残念ながら拡張メソッドは元からあるメソッド名を上書きできないのでToStringではなくToJoinedString。関数名が微妙? そうですね……。 セパレータを使いたい場合にAggregateでサクッと書けなくて昔悩んだのですが、カウント用の変数を場外に用意すればいいんだ!と気付いたのでサクッと書けるようになりました。Linqだけで1行で済ませたい場合はSelectでindex作ってやればいいですねー。

var result = Enumerable.Repeat("hoge", 10)
    .Select((s, i) => new { s, i })
    .Aggregate(new StringBuilder(), (sb, t) => (t.i == 0)
        ? sb.Append(t.s)
        : sb.AppendFormat("{0}{1}", "|", t.s));

悪夢だ。実行効率的にもアレゲ。いくらAggregateで何でも出来るといっても、この辺は標準搭載して欲しいものですねー、Sumがあるなら文字列連結があってくれてもいいじゃない、みたいな。

AutoHotKeyでウィンドウのタイル配置

以前にもAutoHotKeyカスタマイズネタを書きましたが、私はウィンドウ移動系を多用しています。トリプルモニタの間を瞬時にジャンプさせたり、サイズをフィットさせたり。Windows7からWindowsボタン + ←でディスプレイの左半分にリサイズとかがあるので、幾分か被ってしまっているところもありますが、細かい部分では自分で調整したもののほうが、やはり使いやすいです。今回はその中から一つ、同一アプリケーションのウィンドウを水平に並べる関数を紹介します。水平に並べるって、昔懐かしMDIアプリにはありましたが、タブ全盛の昨今だと結構忘れ去られ……。

OSをWindows7に変更したところ、便利なQTTabBarが動かない。困ったねえ。ファイラー使えば?って話なのですが、エクスプローラーに統合されてる便利さは何にも代え難いものがある。と、いうわけで、なんかもうファイラーに戻る気しないしエクスプローラーのままでいいよぅ。でも、せめて多用するファイル移動をスムーズに行いたい→ウィンドウ複数毎開いたた時にズらすのが面倒くさい→こんなの全自動で並べるべき、人力で毎回やるなんてアホ→AutoHotKeyでスクリプト書きましょう。

;アクティブなアプリケーションと同一種類のウィンドウを水平垂直に並べる(最大4枚まで)
;アクティブウィンドウの左上座標が含まれるモニターに並べる
TileMove()
{
    WinGet, activeWindowID, ID, A
    WinGetPos, x, y, w, h, ahk_id %activeWindowID%
    SysGet, monitorCount, MonitorCount
    Loop, %monitorCount%
    {
        SysGet, m, MonitorWorkArea, %a_index%
        if (mLeft <= x && x <= mRight && mTop <= y && y <= mBottom)
        {
            WinGetClass, activeWindowClass, ahk_id %activeWindowID%
            WinGet, id, list, ahk_class %activeWindowClass%
            Loop, %id%
            {
                w := (mRight - mLeft) / 2
                h := (id > 2) ? (mBottom - mTop) / 2 : mBottom - mTop
                x := (Mod(a_index, 2) == 1) ? mLeft : mLeft + w
                y := (a_index <= 2) ? mTop : mTop + h

                StringTrimRight, this_id, id%a_index%, 0
                WinActivate, ahk_id %this_id%
                WinWaitActive, ahk_id %this_id%
                WinMove, ahk_id %this_id%,,%x%, %y%, %w%, %h%
            }
            break
        }
    }
}

AutoHotKeyは三項演算子ないのかよ、クソ!とか思ってたのですが実は普通にあったことが判明。知らんかった。今回ようやく気付いた。で、これは関数の形にしているので、設定ファイルの上のほうにでも張り付けてやって、割り当ては好きなキーを選んでください。私は無変換+Tabに振っています。その場合は「vk1Dsc07B & Tab::TileMove()」になります。

実行するとこんな風に整列します。二枚の場合は縦領域を全部使って平行並べになる。ワンタッチでサクッと出来て、かなり便利だと思います。あ、そういえば全然関係ないのですが、Win7からフォルダの新規作成にCtrl + Shift + nというショートカットキーが振られています。これは嬉しい。今まではQTTabBarのプラグインCreateNewItemButtonを使ってCtrl+Kを振っていたのですが、これでQTTabBarがなくてもやっていけるようになった、かどうかはまだまだ微妙だけど、何とか我慢できる。

つまるところ、Win7はかなりイイ。タスクバー回りも大きく変更が加えられていて、最初は慣れなくて以前の方式に戻そうかと思ったのですが、慣れてくると良く出来てるなーと感心しました。いやー、発売日には憧れの(?)秋葉で並ぶ、をやりたいぐらいに良いですねえ。ドライバも、インストール時に跳ねられるものもVista互換モードで動かすと大抵入るので発売日から入れようと、RCから入れようと、不安なところはない。

XboxInfoTwit ver 1.2.0.0 フルスクレイピングに変更

Xbox.comのサイトがリニューアルされましたね!フレンドのフレンド表示機能のお陰で、ついに念願かなって自分自身のステータス取得に成功。野良APIを使わずに全てスクレイピングで実装することができました。よって、今回からは100%取りこぼしなく、ステータスの変更も瞬時に反映されます。他に変更点としては日付が投稿時の時間になりました(Xbox.com上からは時間を取れるものがないので) あと、書式に実績の数を追加しました。"%TitleTotalAchievementCount%:タイトルの最大実績数", "%TitleAchievementCount%:取得した実績数" です。この変のよくわからないうえに長ったらしい名前はとても後悔しています……。

バグがあったら教えてください。どう見てもテスト不足なのでサクッと落ちるかもしれません。(言ってるそばから不具合発見しました。おまけに、zipファイルに格納するファイルが足りなかったので新規にダウンロードした場合は動かなかったっぽい。酷すぎる。というわけでver 1.2.0.1になりました。)

linq.js ver 1.3.0.2 - CascadeDepthFirst/BreadthFirst

linq.jsにCascadeDepthFirst, CascadeBreadthFirstを追加しました。ちょっと分かり辛いのですが非常に強力です。深さ優先/幅優先で多段SelectManyをかけていくようなイメージ。説明しづらいので例をどうぞ。

<div id="tree">Root
    <div>Spine
        <div>Neck
            <div>Head</div></div>
        <div>RClavicle
            <div>RUpperArm
                <div>RLowerArm
                    <div>RHand</div></div></div></div>
        <div>LClavicle
            <div>LUpperArm
                <div>LLowerArm
                    <div>Hand</div></div></div></div></div>
    <div>RHip
        <div>RUpperLeg
            <div>RLowerLeg
                <div>RFoot</div></div></div></div>
    <div>LHip
        <div>LUpperLeg
            <div>LLowerLeg
                <div>LFoot</div></div></div></div></div>

こんなHTML、というかツリーに色々と操作することにします。例えばinnerTextのように、深さ不明のchildNodesを全て掘ってTextNodeだけを取り出して値を連結するとしたら、通常は再帰を使いますよね。でも、CascadeDepthFirstを使えば

var root = document.getElementById("tree");
var innerText = E.Make(root)
    .CascadeDepthFirst("$.childNodes")
    .Where("$.nodeType == 3")
    .Select("$.nodeValue")
    .ToString();

こんな感じに書けます。rootから深さ優先探索で子ノードを全取得、そのうちnodeTypeが3のもの(TextNode)のnodeValueを文字列連結。宣言的に、再帰よりも分かりやすく書けます。何より後段で豊富なLinqメソッドを使って値を操作していけるのが利点になります。今回から新しく追加されたMake(hoge)というのはRepeat(hoge,1)と等しい。Fromを用いるとオブジェクトはKeyValuePairに、文字列は一文字ずつのシーケンスに化けてしまうので、今回からこのメソッドを用意しました。

実のところMakeもCascadeDepthFirstもC# 3.0 Supplemental Library: Achiral - NyaRuRuの日記のパクりです(AchiralではMakeはMake.Sequence)。ぶっちゃけ今までも勝手に散々パクッているので(ごめんなさい、ありがとうございます。ライセンス的にアレっぽいので、どこかに書いておかないと……) むしろ何で今更追加なのかというと、あまりにも露骨すぎて劣化コピーを入れるのは失礼に思っていて。結局入れてしまったけれど。そんなわけで応用的なものを一つ。引き続き↑のDOMツリーを使って……

var root = document.getElementById("tree");
E.From(root.childNodes)
    .Select(function(child) { return { child: child, parent: root} }) // 外部の値を取りこむ時は文字列ラムダ式は使えません
    .CascadeDepthFirst(function(pair) // 中を入れ子で親の値を参照したいので文字列ラムダ式は不可
    {
        return E.From(pair.child.childNodes)
            .Select(function(child) { return { child: child, parent: pair.child} });
    }, "v,n=>{value:v,nestLevel:n}") // CascadeDepth/BreadthFirstの第二引数はネストレベルを利用可能
    .Where("$.value.child.nodeType == 1") // ELEMENT_NODEだけを取得するためフィルタ
    .WriteLine(function(t)
    {
        return t.nestLevel + ':'
            + E.From(t.value.child.childNodes).First().nodeValue + ' . '
            + E.From(t.value.parent.childNodes).First().nodeValue;
    });

例もパクりです。与えられた木から,子→親への対応を作る,を C# で - NyaRuRuの日記を、HTMLだったらツリーはDOMだよね、という感じで書いてみました。第二引数にはネストレベルを取得できるリザルトセレクターが使えます(省略も当然可能)。んー、DOMだとTextNodeが混ざるせいで、例として不適切に処理が混沌としてしまいました。あ、WriteLineの部分は子要素のFirstがTextNodeであると決め打ちしてます。正確にやりたいなら冒頭のinnerTextの例のようにWhereでTextNodeのみに絞った上で、ToStringで連結したほうが良いですね。

CascadeDepthFirstをCascadeBreadthFirstに変えると(両者は探索方式が違うだけで引数や戻り値は一緒)、こんな結果になります。

動作の差異がよく分かるんじゃないかなーと思います。

continue/break

ForEachでcontinue/breakを使えるようにしました。

E.Range(1, 100).ForEach(function(i)
{
    if (i % 2 == 0) return true; // continue
    if (i == 7) return false;    // break
    alert(i); // 1,3,5
});

灯台下暗しというか、こういうのって自分が必要になるまで気付かないというか。Linqって基本的に前段階のWhereで絞るからこの辺のものって必要になる機会が少ないんですよね。いや、言い訳ですけど。で、まあ、jQueryと同じでtrueをreturnすればcontinue、falseをreturnすればbreakになります。実装は超単純。

while (enumerator.MoveNext())
{
    if (action(enumerator.Current, index++) === false) break;
}

ActionなのにFuncになってしまった!しまった!と、思わなくもないけど、この辺が後付けでグダグダになるのはしょうがないので気にしないことにしよふ。じゃなくて、基本はActionなのでヨシとしておこう。むしろこの辺はJavaScriptがユルフワなのでC#ルールに(用語を)当てはめようとして無理が出ているだけ。そういえばで、個人的にはreturn;でcontinue、return true;でbreakにしたいなあ、とか思ったり思わなかったりなのだけど、その辺は標準に合わせた方が混乱しなくていいよねー、と思うことにしました。

Suggest(モドき)

どうも、新PC組んだりして(Core i7だしメモリ12GだしIntelのSSD2枚刺しでこれで数年は買い換えないでも前線で闘っていける!と思いたいのだけど、そこら中で微妙にコスト削減で若干残念になっていたり、ExtremeじゃなくてMainstreamなところとか) まあ、それはそれ。で、Windows7 RC1を入れて若干の苦戦を強いられていて環境整えるのに時間かかりそう-、というわけでショボいネタを一つ。

// これが補完候補、ただの文字列配列
var list = ["tokyo", "kanagawa", "tochigi", "saitama", "sapporo", "nagano"];

// イベントハンドラは引数を二つ取る、一つは送り元、もう一つはイベント
X.ID("inputBox").AttachEvent("keyup", function(sender, event)
{
    var suggestArea = X.ID("linqsuggest");
    suggestArea.RemoveNodes(); // 子ノード全削除
    if (event.keyCode == 13) // eventはイベント
    {
        sender.value = ""; // 空にするだけ……
        return;
    }
    var text = sender.value; // senderは送信元DOM要素、今回はTextBox

    // この4行が本編です、ええ
    suggestArea.Add(X.Elem("tbody",
        E.From(list)
            .Where(function(s) { return s.indexOf(text) != -1; })
            .Select(function(s) { return X.Elem("tr", X.Elem("td", s)) })
    ));
});

入力補完モドきです。見にくいですけど、ソースコードの上にある入力ボックスにgaでkanagawaとnaganoが表示される、とかそんな感じ。エンターキーによる入力には対応してません、ただたんに絞り込んで表示するだけのデモです。んーと、suggestArea.Addからの、たった4行でリストの絞り込みして->DOM作成が出来るね、簡単だね、素晴らしいね、関数型構築とLINQの合わせ技は強力ですよね、だからみんなも使ってね、ということが言いたいそうです。世の中、WhereしてSelectするだけで9割方カバー出来る。気がする。気のせい。

ちゃんと真面目に作っていきたい気もするのですが、この強引リニアサーチのまま拡張しても野暮ったいだけで意味ないなー、と思うので気が乗らない。でも、大量のデータを扱うわけじゃなければ、この程度の手抜き実装でも普通に何とかなるものですよねー、とは思わなくもない。無理に洗練されたものを作ろうとせず、領域を見極めて労力を最小限にしたいと私は思ふ。いや、手抜きじゃなくて。手抜きですけど。

今後のlinq.xml.jsは微妙に機能足りてないというか、クロスブラウザ回りがグダグダなのと(Attributeにstyle指定がIEだとコケるとか!) DOMと混ぜ合わせてると気になるところが幾らかあるので、ボチボチと作っていきます。素のlinq.jsには深さ優先探索/幅優先探索を入れようとしているのですが、PC入れ替え中なのでもうしばらく後。

一体誰にアピールしてるの?な寂しさを感じつつも、それを言ったら前回の記事なんてどこからどうみても露骨なまでに受け狙いに走ったわりには大不発で寒すぎたので、それを考えたらマシです、いや、よくわからないけれど。でも簡単アピールは何か違う気がする……。うーん。まあ、誰が使わなくても自分は使うわけなので適当に行きましょふ。

最もタメになる「初心者用言語」はVisualStudio(言語?)

本題、に入る前にlinq.jsの更新内容を。今回からVisualStudio上でJavaScriptを書く際にIntelliSense(コード補完)のサポートが効くようになりました。これでもうリファレンスと睨めっこしなくてもすみます。IntelliSenseが出てくれば使えて、出てこなければ使えない。引数の型までは補完してくれませんが、メソッドチェーンに関しては100%信頼出来ます。利用するにはVisualStudio2008 SP1と対応パッチをあてる必要があります。私はProfessionalで確認していますが、Express Editionでも行けると思います。

もう一つ、linq.xml.jsのほうは、今回からXML/DOMの関数型構築が可能になりました(関数型構築の詳細は前回記事を参照ください)。それに伴い実例として、同梱してあるTwitterビューアサンプルも関数型構築に、ついでに@usernameにアンカーを張るように変更しました。

DOM構築時にLinqクエリを埋め込めると何が便利なの?のサンプル。全体像(obj.textって何だよ、とか)は同梱のものを見てください。obj.textは「@hoge hugahuga @foo hogehoge」な感じ(ようはTwitterの投稿の本文)で、文字列置換なら@usernameをa hrefで囲むだけだけど、DOMでやるにはどうしよう→全部マッチさせちゃえばいいぢゃない、それならSelectで射影すると楽ですね、という流れ。E.Matchesはキャプチャ付きの正規表現グローバルマッチが気楽に使えて非常に便利。

VisualStudioの薦め

以前に最もタメになる「初心者用言語」は、という話題で盛り上がっていたけれど、その時JavaScriptを挙げた人の多くが開発環境を入れる必要なく、テキストエディタがあればそれだけで始められるから、といったものが多かったけど、正直どうかなー、と思っていました。1年以上前のネタに今頃乗っかるのって感じですが今頃乗っかてみます。

初心者は、使えるメソッドが分からないから一々リファレンスを引くのに手間取るし(私は今だって文字列のsubstrとsubstringで悩む、特にJavaScriptはこの手の罠が多いし)、しょうもないタイプミス(大文字小文字とか)に悩まされてプログラムが動かない。演算子やカッコにスペースを空ければいいのかどうかも悩むし、一々手で直すのは手間だけど直さなければ汚いし……。

言語の学習って、特に最初はとにかく書くこと、それが最も覚えやすいと思うのです。リファレンスを引く時間も、手動でスペースを空ける時間も、言語の学習とは全く関係ない余計な時間です。限りなくゼロに近づけた方が良い。リファレンスを引く時間がゼロならば、その分だけ書く作業に時間を回せます。だから、テキストエディタじゃなくIDEを使おう。入力補完は一々リファレンスを引く必要もなく使えるメソッドをリストアップしてくれるし、ヘルプを見る必要もなくメソッドの解説が書かれている。補完があるからタイプミスも起こらないし、「長くてもいいから良い名前を付ける」習慣もつく。インデントだのスペースだのカッコの段落だのは、全てドキュメントフォーマッタ(VSではCtrl+E,D)で機械に任せるのが、完璧で綺麗で、何より手間がかからない。

そんなことより前に、そもそもforで変数がどう移り変わっているのかもよくわからないし。デバッガ、あったほうがいいでしょう、絶対に。あるのとないのとでは学習速度の桁が違ってくる。Firebugがあるって?いや、Firebugは確認用には物凄く便利だけど、それの上で開発とか辛くない?私は無理でした。ついでに言えば、Firebugインストールする手間もVisualStudioをインストールする手間も一緒。

Microsoft Visual StudioはWebインストールを選べば何の手間もいらずダウンロードされ、そのままインストールに入り30分程度で終わる(ダイアログの「はい」を押して待つすだけです、Eclipseと違って何入れればいいか、プラグインがどうのこうの、で悩む必要なくそのものがAll in One)、VSの使い方を知るのに30分。周囲に使える人がいれば、勘所を聞くだけで済むので10分。別に何も最初から全てを知る必要はなく、ダミーのASP.NETプロジェクト作って、F9でブレークポイント設定してF5でデバッグ実行してウォッチウィンドウで変数を監視する。それだけ分かれば十二分です(そこを分かるまでが辛い、というのはあります、分かっている人が横にいれば1分で済む話なのに、こんな初歩的な情報はあまりネットに転がってないから分かりようがない。需要がありそうなら書きますが……)

jQueryにも使える

jQuery大人気! やっぱjQueryが使えないとね!と、で、VisualStudioはjQueryに、jQueryオフィシャルが対応しています。どういうことか、というとjQueryのダウンロードページを見てください。Release NoetsとMinifiedとUncompressedと、Documentation: Visual Studio。そう、この公式に配布されているjquery-vsdoc.jsを使う(jquery.jsと同じディレクトリに置くだけです)と、VS上で丁寧に書かれた解説付きの入力補完が効くようになります。

jQueryの特徴といったらメソッドチェーン。ドット打つとの候補が出てくる出てくる、という感覚はとても楽しい。その楽しさを更に加速させるのがIntelliSense。メソッド名何だっけ、引数は何を取るんだっけ、と悩むこともない。ドット打って選ぶだけ。処理の本質部分だけに集中できる。

そういえばオブジェクトっぽい話が分かるかもしれないJavaScript講座が大人気ですけれど、便乗して言うとjQuery使うなら、まずVisual Studio、があれば、もうあとはドット打つだけなので何も考えないでもいいぐらい。んーと、本題はオブジェクトっぽい話、ですけれど、アレですね、何のかんのでオライリーのJavaScript 第5版を読むのが一番分かりやすかった。ケチッてWeb上のチュートリアルをブクマして読んで、なんてやるよりも、この場合は黙って金出して本読むのが時間の節約にも、得られる知識の濃さ的にもよろしいかと思われます。

amazonついでにLINQ本も張っておきます。LINQ本は以前に「LINQテクノロジ入門」という薄い本が出ているのですが、ほとんどLinq to Sqlのことしか書いていないので、少し高いのですが本で学ぶのならこちらの、プログラミングLINQのほうが良いです。初級-中級的にはLinq in Actionのほうがお薦めなのですが翻訳されてないしされる気配もなさそうですね、残念。そうそう、linq.jsも宜しくお願いします。 C#との高い互換性と、リファレンスにおいてあるLINQ Padはリアルタイムに動作を確認可能、なので学習用途ならかなりイイと思います。パフォーマンスは?実装がヘコい?是非とも突っ込みを!

入力補完の嬉しさ

まんじゅうの例を借りると、prototypeにメソッド突っ込んでオブジェクトほにゃららで何が嬉しいのか、というと、入力補完が効く。使えるメソッドがドット打つだけで出てくるし手入力する必要ない。jQueryのようにヘルプは出てきませんが、補完はちゃんと効くのです。同様に何のメソッドがあるのかさっぱり分からないDOM要素に対しても動くし、紛らわしさ全開な文字列に対しても効きます、便利便利。こうして眺めてると、知らないメソッドがあったりして勉強にもなる。

ところで突然C#の話に移って同様に、ポリモーフィズムで何が嬉しいの→入力補完が効く。ジェネリクスの何が嬉しいの→入力補完が効く。動的言語じゃなくて静型な型付け言語(C#とかJavaとか)の何が嬉しいの?→入力補完が超完璧に効く。型推論の何が嬉しいの?→入力補完が超完璧に効く上に更に型を入力する手間も最小限に抑えられる。

// これをそれぞれ二乗する
int[] array = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
// C# 2.0ではこんなクソみたいなコードが
int[] c2 = Array.ConvertAll<int, int>(array, delegate(int i) { return i * i; });
// C# 3.0ではこんなに短く! Rubyっぽく見えませんか?
var c3 = array.Select(i => i * i);

C#2.0だと、本質的な記述部分(i x i)以外の量の方が死ぬほど多くなってしまい、これじゃ嫌われて然りであり動的言語に行きたくもなる。何個intを書かせるんだよボケ、intなんて自明なんだよクソがって感じです。けれど、C#3.0からの型推論+ラムダ式があればとてもすっきりした上に、ちゃんと補完が効く(i x iを記述している最中もiがintであることを示してくれる)ので物凄く書きやすい。JavaScriptもいいけどC#も良いです。無名関数を愛するJavaScripterならC#のラムダ式はもっと愛せると思います(ウザいカッコとreturnが必要ないので)

C#は特にWindows用のデスクトップアプリケーションを作るなら、作りやすさで言えばベスト。.NET Frameworkは嫌だ?やっぱC++でしょって?うーん、ゲームを作るんでもないならC++の性能は必要ないし、作りやすさでいったらC#>超えられない壁>C++ですよ。C#は開発者に優しいゆるふわ言語。C++はF1マシン、とっても扱いにくいけど凄く速い。C#はプリウス。実用性と最先端ハイテクのバランスが絶妙。そもそも公道をF1カーで走ることが無茶苦茶なのです、公道はプリウスで十分どころか、むしろベストというもの。何にでも、適した領域というものがある。

しかも今C#を覚えると、もれなくSilverlightも付いてきます(Flashの開発言語がActionScriptであるように、Silverlightの主な開発言語はC#)。Flashに勝てるわけないだろヴァーカって?何にせよとりあえずツバだけでもつけておくと生きやすいかなーって思いません?Flash業界なんてレッドオーシャンもいいとこ、今から飛び込むならSilverlight。Microsoftは本気なのであっさりと消滅することはないでしょう。本気の時のMicrosoftはとってもしぶとくしつこい。

でもまあ、そんな云々はどうでもよくて、普通にLINQ楽しいですよ!が一番に言いたいことです。ええ、LINQ楽しい。LINQがあるからC#万歳だし、LINQがなくても作ったのでJavaScriptも万歳。linq.jsは初回リリース時にチュートリアルめいたものが書いてあるので、よければどうぞ、というかお願いしますお願いします。

Profile

Yoshifumi Kawai

Cysharp, Inc
CEO/CTO

Microsoft MVP for Developer Technologies(C#)
April 2011
|
July 2024

Twitter:@neuecc GitHub:neuecc

Archive