Archive - JavaScript
ltxml.js - LINQ to XML for JavaScript
- JavaScript linq.js - 12.10/24
以前、linq.js ver.3のセッションの時に、ちょびっとだけ触れたLINQ to XML for JavaScriptが公開されました!
作者は私ではなく、Eric White氏です。誰?ってことですが、元Microsoftの人です。氏のMS時代のMSDNブログのLINQ to XMLタグには超DEEPなLINQ to XMLの使いこなしが書いてあって必見。というわけで、非常にLINQ to XMLに詳しい、というか、MSDNのLINQ to XMLの解説ドキュメントを書いたのは氏だそうです。なので、詳しいとかそういう次元じゃなく、普通に中の人ということですね。
概要
そんなわけで、ltxml.jsとは、C#のXML操作ライブラリであるLINQ to XMLのJavaScript移植です。C#のLINQ to XMLがLINQ to Objectsの上に構築されている(ElementsやDescendantsの戻り値がIEnumerableとなり、LINQベースのクエリ操作となる)ように、ltxml.jsはLINQ to ObjectsのJavaScript移植であるlinq.jsの上に構築されています。ltxml.jsのelementsやdescendantsは、linq.jsのwhereやselectなどのクエリ操作によってXMLを展開できます。
C#版と構造はほとんど一緒です。ただし、JavaScriptの慣習に則りメソッド名がlowerCamelCaseであることと、プロパティが.getHoge()であること(ただしECMAScript 5に対応しているならば.hogeといったようにプロパティでアクセスできます)、オペレーターオーバーロードが存在しないことによる挙動の違い程度です。また、C#版よりも機能向上している面もあります。それは、私がlinq.jsにおいてC#のLINQ to Objectsで物足りないと思った機能を追加していたようなもの、でしょうか、多分ね、きっと。
また、パフォーマンス上の考慮により、descendantsなどは、デフォルトは即時実行で配列(をEnumerableでラップしたもの)を返します。.lazy = trueをXElementなどに投下することで、完全な遅延実行になります。もし巨大なXMLを扱うのならば、遅延実行が効果を発揮するでしょう。通常考えられるサイズのXMLならば、デフォルトのとおり即時実行のほうが良好だと思われます。
使い方
ぶっきらぼうにも、ドキュメントがほとんどないですね!まあ、それは追々紹介されていくことでしょう。ともあれ現状は、ファイルをダウンロードするとltxmlTest.htmlというファイルがあって、それがユニットテスト兼サンプルになっているので、とりあえずそれを読みましょう。また、JavaScript特有の違いはあるものの、基本的にはC#のそれと等しいので、MSDNのLINQ to XMLの解説ドキュメントがまんま使えないこともないです。
ともあれ、まずは簡単なXMLをパースしてみましょう。
var xml = "<Contacts>\ <Contact>\ <Name>Peter Hage</Name>\ <Phone>206-555-0144</Phone>\ </Contact>\ <Contact>\ <Name>John Hoge</Name>\ <Phone>106-333-2222</Phone>\ </Contact>\ </Contacts>"; // parseでただの文字列からLINQ to XMLのXElementに変換 var xElem = Ltxml.XElement.parse(xml); // 子孫ノードのNameを選択し、値だけ抽出 var names = xElem.descendants("Name") .select(function (x) { return x.getValue(); }) .toArray(); alert(names); // Peter Hage, John Hoge
descendants.selectといったように、LINQです!完全に!これをLINQと言わずして何をLINQと言うか!
名前空間
ltxml.jsの全てのクラスはLtxmlオブジェクトの下に格納されています。グローバルを汚さない。しかし、いちいちLtxml.XElementなどと呼ぶのは面倒くさい話です。以下のようなショートカットを先頭に用意するのをお薦めします。
var XAttribute = Ltxml.XAttribute; var XCData = Ltxml.XCData; var XComment = Ltxml.XComment; var XContainer = Ltxml.XContainer; var XDeclaration = Ltxml.XDeclaration; var XDocument = Ltxml.XDocument; var XElement = Ltxml.XElement; var XName = Ltxml.XName; var XNamespace = Ltxml.XNamespace; var XNode = Ltxml.XNode; var XObject = Ltxml.XObject; var XProcessingInstruction = Ltxml.XProcessingInstruction; var XText = Ltxml.XText; var XEntity = Ltxml.XEntity; var XEnumerable = Ltxml.XEnumerable;
また、C#版ではEnumerableへの拡張メソッドとして用意されていた幾つかのメソッドは、ltxml.jsではEnumerableに追加されているasXEnumerableを呼び、XEnumerableへと変換することで、呼び出すことができます。しかし、もしそれを手間だと思う場合は、linq.jsのEnumerableを拡張することで、よりスムーズに接続することが可能です。ただし、C#版ではジェネリックによって区別されていましたが、JavaScriptではジェネリックが存在しないので、汎用性のないシーケンスの要素がltxml.jsに固有でなければならないメソッドをEnumerableに追加することとなります。また、removeなどは、他の人の拡張と名前が衝突する可能性が高いことなどにも注意。
Enumerable.prototype.elements = Ltxml.XEnumerable.prototype.elements; Enumerable.prototype.ancestors = Ltxml.XEnumerable.prototype.ancestors; Enumerable.prototype.ancestorsAndSelf = Ltxml.XEnumerable.prototype.ancestorsAndSelf; Enumerable.prototype.attributes = Ltxml.XEnumerable.prototype.attributes; Enumerable.prototype.descendantNodes = Ltxml.XEnumerable.prototype.descendantNodes; Enumerable.prototype.descendantNodesAndSelf = Ltxml.XEnumerable.prototype.descendantNodesAndSelf; Enumerable.prototype.descendants = Ltxml.XEnumerable.prototype.descendants; Enumerable.prototype.descendantsAndSelf = Ltxml.XEnumerable.prototype.descendantsAndSelf; Enumerable.prototype.elements = Ltxml.XEnumerable.prototype.elements; Enumerable.prototype.nodes = Ltxml.XEnumerable.prototype.nodes; Enumerable.prototype.remove = Ltxml.XEnumerable.prototype.remove;
私個人としては、Enumerableへの拡張はそんなに薦められないかな、という感じですが、ヘヴィにXMLを処理する局面では、拡張したほうがサクサク書けて良いのではかとも思います。この辺は好みでどうぞ。
関数型構築
XMLを作るときは、コンストラクタで可変長引数として連鎖させます。これをLINQ to XMLでは関数型構築と呼んでいます。
var xml = new XElement("root", new XElement("user", new XAttribute("id", 1), new XElement("age", 100))); // <root><user id = '1'><age>100</age></user></root> alert(xml.toString()); // toStringで文字列化
閉じタグが不要であったり、安全であったり(JavaScriptだってカッコ閉じ忘れとかは警告入るからね)と、生文字列で組み立てるのに比べて、遥かに利点があります。また、要素がlinq.jsのEnumerableである場合も、きちんと展開されます。
// C#と同様にEnumerable<XElement>は展開される var users = Enumerable.range(1, 10) .select(function (x) { return new XElement("user", new XAttribute("id", x), new XElement("age", x * x)); }); var xml = new XElement("root", users); // <root> // <user id = '1'> // <age>1</age> // </user> // <user id = '2'> // <age>4</age> // </user> // (略) // </root> alert(xml.toString(true)); // 引数にtrueを渡すとインデントつけて整形
どうでしょう、イメージつきます?
用途
Eric White氏がOpenXML(Officeのxlsxとかがそれ)の専門家ということで、JavaScript上でOfficeファイルを容易に扱うことが可能になるでしょう。つまり、サーバーサイドでのコンバート処理など不要に、JavaScriptだけでビューワーであたり要素抽出であったりが、完結する未来があります。なんて興奮する話でしょう!
とはいえ、それはあまりにも専門的すぎて、実に面白いし役立つでしょうけれど、実際にそれでもって作り上げる側に周るのは極少数の人に違いありません。では、他にXMLを使う局面なんてあるのか?ふむ……。恐らく、ブラウザ上で動くアプリケーションにとって機会はないでしょう、どこもかしこもJSONに集約される!AJAXのXはXMLのX!だった時もありました。いや、今もそうでしょうけれど。
では、どこに?というと、Node.jsはどうだろう?結局、未だにXMLのサービスなどは多いのだ。RSSはJSONにならないでしょう。サーバーサイドで行うならXMLは無視できないのだ。またはクライアントアプリでも、TitaniumやPhoneGapはどうだ?またはWindows 8のアプリケーションはJavaScriptで作ることができる。そこではまだまだXMLは現役に違いない。JavaScriptの活躍範囲がブラウザを超えていけばいくほど、残り続けるXMLに遭遇する機会は増える。
AtomPub(ああ!今はもうあまり名前を聞かない!)の構築に、LINQ to XMLの関数型構築は役に立つことでしょう。とにかく言えることは、XMLは決して死んでいないし、望まれるか望まれないかはともかくとして、生き残り続けるでしょう。そのために、私達には強力な武器が必要なのです、LINQ to XMLという。
もしくは、単純にHTMLビルダーとして使ったっていいかもしれない。HTMLはXMLなのだもの、ええ、大丈夫、そのようにも使えます。文字列連結してHTMLを組み立てるなんてしなくていい。また、もちろん、HTMLのDOM操作にだって、ね(でもDOMの操作ならば、きっとjQuery使いますね)
未来
ところでしかし現状ltxml.jsはベータです。何故か。linq.jsがまだベータだからです。ver.3.0.3-Beta4ってなんだよクソが。すみませんすみません、なるべく早く正式リリース出来るよう鋭意努力します。NEETなので暇、じゃあないんですよねえ、残念ながら。でも急ぎます。さすがに!いやほんと私の作業ペースの遅さには我ながらどうかと思う感じですが、もうさすがに猶予ないですね!
ちなみに7月31日に作ってるよー、ってメール貰って、そこから何度かやり取りしてました。ltxml.jsのコード自体、かなりパワフルにlinq.jsを使いこなしているので(私のアドバイスの賜物です!)そういう意味でも面白いですね。ちなみに、その時には8月中にlinq.jsリリースするって言ってたかなあ、今もう10月末ですねえ、どうなってるんでしょうねえ、ごめんなさいごめんなさい。
linq.jsのTypeScript対応とTypeScript雑感
- JavaScript TypeScript linq.js - 12.10/12
MicrosoftからTypeScriptという新言語が発表されました。驚くべきは、あのC#のAnders Hejlsbergが関わっている!これはもう触るしかない。そしてこれはコンパイル後にJavaScriptになる言語(CoffeeとかJSXとかみたいな)なわけで、じゃあlinq.jsを対応させるしかない!というわけで、させました。
// TypeScript Enumerable.range(1, 10) .where(x => x % 2 == 0) .select(x => x * x) .writeLine(); // コンパイル後 Enumerable.range(1, 10).where(function (x) { return x % 2 == 0; }).select(function (x) { return x * x; }).writeLine();
ひゃっはー、もうfunction() { return }とはオサラバだ!そしてこの記述性と最強のコレクション操作であるLINQが合わさると最強に見える。
に型定義ファイルは同梱してありますので、是非是非お試しを。NuGetのlinq.js -Preでも入ります。NPMは予定はありますが、まだです、すみません。
TypeScriptについて
型安全なCoffee Script、といった印象ですね。基本的にはCoffee Scriptに近いと思います。JavaScriptにプラスアルファな構文を採用することで、既存のJSライブラリとの繋がりを良くすることと、綺麗なJavaScriptを吐くことに重きが置かれている。TypeScriptは、比較的素直にJavaScriptに読み解くことが出来て、独自のコード生成は、現状はほぼほぼ無いし、意図的に省かれているようです(例えば非同期にたいしてasync構文を入れたりすると、大量のコード生成が入り、出力されるJavaScriptが機械的に汚れがち)。
そういった点、機能面では、TypeScriptには物足りなさを感じるところが多いかもしれません。じゃあJavaScriptに対する強みってどこなんだよ!といったら、一つはJavaScriptの冗長な記述性の補正(class,module, arrow function, Object.create/definePropertyとかも最低だしね)。もう一つは、無理なく自然に馴染んだ型付け。
型はないよりあったほうがいい。でも、型を付けるのがあまりにも苦痛だったら?ちょっとしたコードを書くのにも型!型!型!と押し付けられたら?そりゃあ、嫌だ。というわけで、型推論によって、比較的スムースに書けるようになっています。
型推論の性質というか範囲というか強さというかは、C#と非常に近いというかC#とまるで一緒なので、C#erならサクッと馴染めます。もっと強力な型推論のある言語に馴染んでいる人だと、え、ここで効かないの?みたいな違和感はあるかもですが。
また、さすがはMicrosoftというかAnders Hejlsbergというか、入力補完のことを念頭に置いた言語設計になっているので、IDEとの相性が非常に良い。そして最初からVisual StudioによるIDE環境が用意されていることで、型のある利点の一つであるリアルタイムエラー通知や入力補完をたっぷり満喫できます。さらに、それはTypeScript PlaygroundによってWeb上でも体感できます。というか、もはやPlaygroundはWeb IDEとでも言うべき驚異的な動き!
また、Windowsだけではなく、最初からSublime, Vim, Emacsの対応ファイルが用意されているというところからも、Windowsに限らず幅広く請求したい、という表れだと思います。そして実際、言語はプラットフォーム中立なわけです(最終的にはJavaScriptだしね!)。
Structural Subtyping
TypeScriptの最も面白いところは、ここです。C#とかのインターフェイスとLLのダックタイピングの中間、みたいな。実にゆるふわなJavaScriptと絶妙に合っててイイ!というかそもそも私はStructural Subtypingって名前だけでぜーんぜん分かってなかったのですが、TypeScriptだと自然と馴染めます。ほむ、どういうことか、というと、一例を。
union(second: any[], compareSelector?: (element: any) => any): Enumerable; union(second: Enumerable, compareSelector?: (element: any) => any): Enumerable; union(second: { length: number;[index: number]: any; }, compareSelector?: (element: any) => any): Enumerable;
これはlinq.jsの型定義の一つでunion、和集合を生成するためのメソッドです。なので、元シーケンスと、対象シーケンス(second)が対象になるわけですが、じゃあシーケンスって何?と。列挙できればいいので、配列もそうだし、Enumerableもそう。そして、JavaScriptに特有の存在として、配列みたいだけど配列じゃあないもの(lengthを持っていてインデクサでアクセスできる)、例えばDOMのNodeListとか、もそう。
で、そういった「lengthを持っていてインデクサでアクセスできる」という型の定義が{ length: number;[index: number]: any; }。これにより、DOMのNodeListやjQuery([0]とかでHTMLElementが取れる)など、配列みたいだけど配列じゃないもの全てが型安全に定義されました。やったね!
もしC#だったら、対象はインターフェイスで指定するしかないので、IEnumerable<T>を実装していないクソコレクションクラスが存在したら、それは列挙不能になってしまいます。片っ端からオーバーロードを作るのは不可能だし、かといってdynamic secondなどとしてしまってはアレ。
とはいえ、基本的にC#では最初から全てのシーケンスはIEnumerable<T>を実装している、という前提が成り立っているので、問題はおこらない。でも、JavaScriptは違う。配列みたいだけど配列じゃあないもの、が跋扈してる。でも、そこをanyとして何でも受け入れられるようにしたら型安全じゃあない。安全にしたい。そこをStructural Subtypingが華麗に解決してくれました!惚れた……。
TypeScriptはJavaScriptか?
JavaScriptコードはそのままTypeScriptだ!ということにYesと言えるかというと、イエスでもあり、しかし割とノーです。私がこの話を聞いて、最初に思ったのは、既存JSコード、つまりライブラリ類もそのままで動くのかな?と。答えはNOです。JS固有の、実行時に切った貼ったして構造作っていくの、ああいうのTypeScriptだと軒並みコンパイルエラーになるので、ダメです。ほとんどのライブラリが絶滅でしょう。
と、勘違いしていたのですが(yharaさん指摘ありがとうございます!)
declare var $; // jQuery declare var _; // underscore declare var Enumerable; // linq.js
とかって定義すると、これはそれぞれ $:any, _:any, Enumerable:any という扱いになって、以降はどんなチェーンを繋げてもエラーが起こらない、つまりライブラリが正常に読み込めたかのようになります。
ただ、型チェックや入力補完が効かなくなるので、TypeScript用の型注釈ファイルはあったほうがいいですね。有名ライブラリはともあれ、無名プラグインとかは自前で型注釈書かなければならないかもり。手書きだとかったるいので、自動生成である程度テンプレート吐き出してくれないと、面倒くさい。この辺はMicrosoftだしやってくれるんじゃないかなあ、という淡い期待を抱いていますが……。
とはいえ、ちょっとしたコンパクトなプラグインを使ったり、ライブラリ使うとしても一部分だけだしー、などというのに、わざわざ型定義も馬鹿らしいわけで、さくっと動的な感じにdeclareできちゃう、というのは素晴らしい話。
そんなわけで、JavaScript→TypeScriptの相互運用性としては、繋がりはかなり良好。勿論、jQueryなどもスムースに扱うことができます。これは、文法がJavaScriptプラスアルファで構築されているがことの利点です。そしてTypeScript→JavaScriptは、というと、素直なJavaScriptを吐いてくれることもあり、良好です。TypeScriptで作られた生成物は、TypeScriptだけに閉じません。
JavaScriptを中間言語とする選択肢が増えた。JavaScriptを介することで他の言語とも自由に繋がる。ここには、Webの互換性、中立性を崩す要素は一切ありません。独自言語による囲い込みとかではありません。素直に歓迎できるはずです。ただし、言語としてはあくまでTypeScriptはTypeScriptです。そこだけは、誤解しないほうがいいと思います。文法的に、ES6を若干取り入れているとはいえ、違う言語です。将来的にもTypeScriptはEcmaScriptにならないでしょうし、EcmaScriptはTypeScriptにはならないでしょう。TypeScriptはEcmaScript6のただの代替なのではない、別の価値ある言語です。
変な期待をして、これJavaScriptじゃないじゃん、とかって難癖つけたりは、あまり良くないですね。
TypeScriptとVisual Studio
別にMicrosoft環境に閉じる言語ではないので、EmacsでもVimでもいいですが、やはりVisual Studioが第一な点は少なからずあります。LinuxでもIDEで書きたい?きっとJetBrainsがWebStormに搭載してくれるはずです!(実際、Voteはかなり集まってました)
ともあれ、Visual Studioです。専用拡張のインストールはTypeScriptのサイトから出来ます。プロジェクトテンプレートが何故かC#のところにあって気づきにくいことに注意!それともう一つ、Web Essentialsを入れましょう。元々Coffee ScriptとLESSに対応していたのですが、今回TypeScriptにも対応してくれました。Web Essentialsを入れることで、保存時のコンパイル(通常の拡張だとビルド時のみ)と、ウィンドウ分割での出力後のJS表示、それとSourceMapファイルの出力を行ってくれます。
勿論、IntelliSenseはフルに効くしエラーはリアルタイムでがんがん通知してくれます。TypeScript Playgroundと違うのは、エラーがあるとJSに変換してくれないところですね。まあ、それは正しい挙動なのでいいです。Playgroundで中途半端なエラーのある状態でもガンガン変更表示してくれるのは、それはそれで便利なので、それもまたいいです。
ちなみに、TypeScript Playgroundでは赤波線が出ている状態は、一応、JSを出力してくれてますが、それはコンパイルエラーの状態で完全な出力がされていないと思って良いです。つまり、本来的には動いてないわけです。この動いていない出力を指して、(現状Firefoxにしか乗ってない)JavaScriptへの互換が不完全とかって難癖つけたりするのは、ほんと良くないですね……。
SourceMap
Web Essentialsの吐いてくれるSourceMapとは何ぞや、というと、これはTypeScriptのままデバッグができます。コンパイル時にJSを吐いてくれる系言語の欠点として、デバッガを使ったデバッグが困難、というのが挙げられますがSourceMapを使うとそれも解決、します。
現状、対応ブラウザはChromeと、まあ、他は知らないのですが、とりあえずChromeは対応しています。IE10(とVS2012内蔵デバッガ)も対応してくれると嬉しいなあ。Chromeのデバッガの不満点としては、ブレークポイントが行単位でしか貼れないことですね。ラムダ式の内側に貼れないと、特にLINQのような一行ラムダを多用するものではデバッグがとても不便でして。この辺、改善されていってくれると嬉しい話。
vs JavaScript(のIntelliSense)
実は、VisualStudio 2012のJavaScriptはかなりサポートが手厚く、裏で常にコードを実行して補完候補を出してくれたりします。
なので、純粋なIntelliSenseの効きだけでいうと、TypeScriptはJavaScriptに負けているかもしれない!如何せん、特にlinq.jsではシーケンスの要素がanyになってしまいますからね。JavaScript(を裏で動かして解釈する)ならば、ここも補完効いてしまうという。最近のJavaScript IDEは進化しすぎで恐ろしい……。
ジェネリクス
仕様書にも明言されていますが、正式リリースまでには搭載する予定があるそうです(ちなみに現在は0.8)。ジェネリクスが乗っかるとlinq.jsがすっごくパワフルになるんですよ。如何せん、今はシーケンスの要素の型が全てany扱いで補完が全く効かなくてTypeSafeでもなんでもないのですが、ここが型付けされると完璧なIntelliSense生活!C#並というかむしろC#超えるぐらいの勢いでパーフェクトなLINQ to Objects!なので、相当に待ち遠しいです。
Compiler as a Serviceの未来
TypeScriptのコンパイラはTypeScriptで書かれてます。これ、別にかっこつけとかでもなんでもなく、非常に重要な意味を持ちます。で、いきなり分かりやすく成果物として出してくれているのがTypeScript Playground。構文解析がJavaScriptで可能だから、Web上で全て完結するIDEが作れる。C#も次のバージョンではC#コンパイラがC#で書かれるという計画があります。そのことがもたらす価値の一部分は、TypeScriptが教えてくれます。いや、むしろブラウザ上で全て完結というのは、C#以上の魅力がありますね、正直……。
結論
TypeScriptは、良い言語だと本当に本当に思います。私は、素のJavaScriptも別にそこまで嫌いではないのですけれど、やっぱ、違うなあ、と。なので今後は積極的に使っていきたいところです(CSSもLESSで!)。
言語設計者が同じということもありますが、特にC#erには絶対馴染むと思うので、(linq.jsとセットで)今までJavaScriptとは無縁だった人も手を出して欲しいですね。きっと気に入りますし、視点が変わります。勿論、ネイティブJSerも是非是非触ってみるといいと思います!というか触ってほしいです。
あ、あと、軽く流しましたがVisual StudioユーザーならWeb Essentialsも必ず入れておきましょう。これがあるのとないのとでは、TypeScriptの使い勝手全然違ってくるので、TypeScript試すならば必須です。
linq.js ver.3.0.2-RC, WinRT対応、最新RxJS対応など
- JavaScript linq.js - 12.09/16
RCリリースしました!これでAPI弄りは終了で、あとはドキュメント周りの調整のみといったところです。
ダウンロードはダウンロードボタンからではなく、ダウンロードタブからlinq.js ver.3.0.2-RCを選択してください。というかここからダイレクトに飛んでください。
Beta2から結構立ちましたが、その間に、ノートPCがぶっ壊れたり(今もサポートで修理中、ちょうどうっかり未Pushなコミットが溜まってた状態で逝ってしまったのが痛手でどぅーにもこーにも)、そもそも日本にいなかったり(シンガポールにいましたというか、今は日本にいますが、これからは基本的にシンガポール中心になる感)とか、まぁ色々で色々で捗らずで。
さて、その間でもないですが、プログラミング生放送で8/25にセッションを持ちまして、そこでlinq.js ver.3の紹介をしましたので、スライド・録画ともどもにどうぞ。
Visual Studio 2012はJavaScript関係がハイパー強化されているのですけれど、そのビッグウェーブにフルに乗っかって強力なんだぞ!みたいな。そういったVS2012のパワーとかの部分は、デモの比率が高いので、時間に余裕があれば、是非とも録画を見ていただけると嬉しいです。
あと、こそっとLINQ to XMLのアナウンスを、こそっと、ね。ふふり。
あ、そうだ、スライドに関しては一点、嘘があります。VS2012にはjQueryのIntelliSenseドキュメントの日本語版は入っていません。英語のみです。本当にごめんなさい、これ、確認していなくて、VS2010では日本語訳されたのが入ってたからそうなんだろうなあ、とか思ってたのですが、そんなことはなかったです。予算的な都合なのでしょうか……?ともあれ、申し訳ありませんでした。
更新事項
今回も破壊的変更が入っていて、firstOrDefault, lastOrDefault, singleOrDefaultの引数が変わりました。
// 以前 .firstOrDefault(defaultValue, [predicate]); // これから .firstOrDefault([predicate], [defaultValue]);
です。ようするに引数が逆になりました。predicateが先にあるのがC#のLINQと同じ並び順なわけで、ここだけ、何故かlinq.jsはC#に従ってなかったのですね。理由としてはJavaScriptにはdefault(T)は存在しないのでdefaultValueの指定を強制するために、第一引数に持ってきてやらなければならない。と、当時、3年前は思ってたらしーんですが、別に普通にないならないでnullでいいだろ馬鹿が、むしろ引数がこれだけ違うとか紛らわしいだろクソが。ということにやっと至りまして、変えられるのは今しかない!ということで変えました。
コードスニペット
そういえば3.0.1-Beta2のリリース時にはブログ書いてませんでしたが、そこでコードスニペットを改良したのを入れました。linq.js ver.2に同梱してたものよりずっと良くなってるのでリプレースするといいです。非常に捗ります。というか、もうこれなしで書く気しないぐらいに。
RxJS
Reactive Extensions for JavaScript(RxJS)がオープンソースになりました、完全にソースコード公開です、ぱちぱちぱち。今まではScriptSharpで変換してたような気配だったのですが、完全手書きに移行したようです。
それに伴ってlinq.jsのRxJS連携も、若干手を加えました。ところで、今のところNuGetにあがっているものは、GitHubの最新に追随してません。古いままです。なので、NuGet版だとlinq.jsのRxJS連携は動かなかったりします(ビミョーに中身変わってるんですよ、いやはや……)
今のところ新RxJSに関してはリリースとかも打たれていないので、ステータスがどうなっているのか、よくわかりません。まあ、近日中に、かなあ?どうなのでしょうね。とりあえず、動向には注目、ということで。
WinMD
Windows 8のアプリケーション(Metroとは言えなくなりました!)はJavaScriptでも開発できるわけでして&C++やC#で作成されたライブラリも条件付きというか専用のコンポーネントとして作れば、JavaScriptでも読み込むことができます。 コレクション周り、IList<T>はJavaScriptでは配列として扱えます。なので、これは何もなく普通に列挙できるし、今までのlinq.jsでも扱うことができました。しかし、IEnumerable<T>はIIterable<T>というものに化け、これは独特の列挙の仕方を要求するため、フツーには扱いづらく、また、今までのlinq.jsでも使えませんでした。
が、ver.3.0.2-RCからは、IIterable<T>対応を入れたので、列挙可能です!
// IIterable<T>を列挙できるのはlinq.jsだけ! var iterable = ToaruLib.GetIterable(); Enumerable.from(iterable).forEach();
WSH(JScript)対応といい、Windows固有のニッチ需要に100%応えるのはlinq.jsだけ。いやほんと。
文字列ラムダと無名関数
これは今までもの話なのですが、文字列ラムダに抵抗あるー、というのは分かります。しかし、無名関数を渡すこともできますぜ、というのは、分かって欲しいというか、利用シーンとしては半々なイメージなんですよね。例えばですが二つのJSONをJoinするのに
var jsArrayA = [{ "projectid": 122, "projecttype": "radio" },{ "projectid": 133, "projecttype": "tv" }]; var jsArrayB = [ { "actionid": 1, "name": "kuow", "pid": 122 }, { "actionid": 2, "name": "kplu", "pid": 122 }, { "actionid": 3, "name": "abc", "pid": 133 }, { "actionid": 4, "name": "espn", "pid": 133 } ]; var queryResult = Enumerable.from(jsArrayA) .join(jsArrayB, "$.projectid", "$.pid", function (a, b) { return { projectid: a.projectid, projecttype: a.projecttype, actionid: b.actionid, name: b.name, pid: b.pid } }) .toArray();
これ、全部、無名関数で書くことも可能です。”$.projectid”をfunction(x){ return x.projectid} と書けばいいので。”$.pid”の部分もそうです。でも、それってすごくだるいですよね。
LINQはプロパティ名を指定するだけの無名関数を要求するシーンが多いです。どうせ、JavaScriptは動的言語、コンパイルチェックも働かないのですから、文字列で指定しても一緒でしょう。また、これは、jQueryのセレクターと同じようなものです。そう考えれば、文字列指定にもさして抵抗感はないのではないでしょうか?短くサラッと文字列でプロパティ名を指定したほうが、書きやすいし可読性も高いです。
同様に、最後のJOIN結果を新しいオブジェクトに変換しているところは、文字列ラムダで書くことも可能です。”{projectid:$.projectid, projecttype:$.projecttype,….}”といったように。でも、それって今度は逆にとても見づらくて可読性落ちますよね。長いコード、入り組んだコードになるようならば、素直に無名関数を使ってもらうのがいいな、と思っています。
次回
次は正式リリースです!いつになるかは、いつになるかしらん!8月末が正式リリースのつもりだったのに、一か月遅れでRCリリースですからねえ、んもぅー。ともあれ、間違いなく良い出来になっているので、楽しみにしてください。で、もうその前にRCじゃんじゃん使ってくだしあ。
linq.js ver.3.0.0-beta, メソッドlowerCamelCase化、など
- JavaScript linq.js - 12.07/20
ずっとやるやる詐欺だったlinq.js ver.3ですが、ようやく始まりました!
トップページのボタンはstableのものになるので、DOWNLOADSタブからver.3.0.0-betaを選んでください。また、NuGetを使っている人はInstall-Package linq.js -Preで入ります。他にlinq.js-jQuery -Pre, linq.js-RxJS -Pre, linq.js-QUnit -Preなどり。
lowerCamelCase化
はい。ようやくJavaScriptらしくなりました。UpperCamelCaseにはC#っぽいとか、キーワードで衝突しなくなるとか、ちょっとした利点はなくもないのですが、そもそも.NETっぽくないほうがいい、というかJavaScriptの世界にちゃんと馴染ませたいという思いのほうが強い。そして、.NETの人以外にも使って欲しくて。
Enumerable.range(1, 10) .where(function(x){ return x % 2 == 0}) .select(function(x){ return x * x});
当然ながら超破壊的変更です。ver.2までのコードは一切動かなくなります。やりすぎですが、しょうがない。痛くてしょうがないけれどしょうがない。さて、ならばとついでにメソッド名の見直しもしました。
Return -> make CascadeBreadthFirst -> traverseBreadthFirst CascadeDepthFirst -> traverseDepthFirst BufferWithCount -> buffer ToString -> toJoinedString Do -> doAction Let -> letBind MemoizeAll -> memoize Catch -> catchError Finally -> finallyAction ToJSON -> toJSONString
これを機に、というかこういう機会じゃないとやれないですから。toStringやtoJSONは、上書きしてしまうとマズいので別名にしています。toStringは、まあそのままなので分かると思うのですが、toJSONのほうは、JSON.stringifyで特別扱いされるメソッドなので、こっそり注意が必要なんですね、というか実際ハマッて気づくのに時間かかりました。
extendTo
prototype.js以降、prototype拡張は悪、でしたが、最近のJavaScriptはfor inで列挙しない形での拡張(Object.definePropertyでenumerable:falseにする)が可能になっています。それを全面に押し出したSugarといったライブラリもあり、確かに便利なのですよね。
さて、linq.jsでは配列などをLINQで扱うためにEnumerable.fromで包んでやる必要があって面倒でしたが、配列からそのままselectとかwhereとかが生えていれば、便利、ですよね?なので、任意に拡張できるようにしました。
// Arrayを拡張する Enumerable.Utils.extendTo(Array); [1, 3, 10, 1000].where("$%2==0").select("$*$");
Enumerable.Utils.extendToを一度呼べば、from不要で直接LINQのメソッドを呼ぶことができます。もしブラウザがObject.definePropertyに対応していなければ、その時はprototypeを直接拡張しています。
さて、LINQのメソッド名とネイティブのメソッド名が被っている場合は、ネイティブのメソッド名を優先して、上書きはしません。例えばjoinとか、concatとか。その場合はByLinqがメソッド名の末尾につきます。joinByLinq、など。
// 名称が被るものはByLinqというプリフィックスがつく [1, 3, 10].reverseByLinq(); // もしくはasEnumerableメソッドを呼んであげればLINQのメソッドのみになります [1, 10, 100].asEnumerable().forEach(function(x, index){alert(x + ":" + index)});
forEachなどは古いブラウザではそのまま、新しいブラウザではforEachByLinqになる、といったようにブラウザ互換性がなくなるので、個人的にはByLinqの形で呼ぶよりかは、asEnumerableを使うことのほうをお薦めします。
Visual Studio 2012でのIntelliSense超拡張
VS2012でlinq.jsを使うと、ただでさえ充実していたIntelliSenseが更に超補完されます。どのぐらい補完されるか、というと、selector関数でオブジェクトの候補が並んでしまうぐらいに。
もはや完全にC#。あまりの快適さにチビる。勿論、↑の図ではFooは文字列なので、x.Foo.で文字列のメソッド候補がIntelliSenseに並びます。動的言語とは思えない超補完っぷりがヤバい。そして入力補完が最大限に活きるように設計されているLINQなので、組み合わさった時の快適度は半端ない。
Chaining Assertion for QUnit
ユニットテストを書く際に、equal(actual, expected)と書くのが嫌いでした。どちらがactualなのかexpectedなのか一瞬悩むし、そもそも外側から包むのがかったるくて。かといってshouldといった、英語的表現なのも冗長なだけで全く良いとは思っていませんでした。そこでC#ではChaining Assertionといった、actual.Is(expected)でアサートが書けるライブラリを作ったのですが、それをJavaScript用に移植しました。
// 流れるように.isと打ち込むだけ Math.pow(10, 2).is(100); // strictEqual(Math.pow(10, 2), 100) // コレクションに対する適用は可変長にカンマ区切りで値を並べるだけ。勿論、配列にも使えます。 Enumerable.rangeTo(10, 15, 2).is(10, 12, 14); // deepEqual(Enumerable.rangeTo(10, 15, 2).toArray(), [10, 12, 14]) // LINQと組み合わさることでコレクション系のチェックが遥かに容易になる! [1, 5, 10].all("$<12").isTrue(); // collection assertion with linq.js!
といった感じに書けて、超楽ちんです。使うにはlinq.qunit.jsを別途読み込んでください。
その他
createEnumerable, createEnumerator, createLambdaといった、自作Enumerableメソッドを作るための道具を外部公開するようにしました。是非作っちゃってください。
Enumerable.Utils.createLambda Enumerable.Utils.createEnumerable Enumerable.Utils.createEnumerator
更に、メソッドも追加されています。
Enumerable.defer asEnumerable merge choose isEmpty distinctUntilChanged weightedSample log
それらの細かい使い方などは追々書いていきます。また、merge, zip, concatは複数のシーケンスを引数に受け取れるようになりました。
そして、C#では、以前にneue cc - LINQのWhereやSelect連打のパフォーマンス最適化についてという記事を書いた通り、Where連打やSelect連打、それにWhere->Selectといったよくあるパターンに対して最適化が入っているのですが、それをlinq.jsでも再現しました。なので、Where連打などによるパフォーマンス劣化が抑えられています。また、頻出パターンのWhere->Selectで高速化されたのはかなり大きいと思っています。
それに加えてrange, rangeDown, rangeToといったよく使う生成関数の速度を大幅に上げました(以前はtoInfinity().take()で生成していたのを、独自生成に変更したため)。
なので全体的にパフォーマンスも向上しています。
それと最後に、jQueryのプラグインとしてのものは今回からやめました。なんか混乱するし意味ないな、と思ったので、jQueryとの連携はlinq.jquery.jsによるtoEnumerable/tojQueryを追加するファイルのみとなっています。RxJSに関しても最新版のRxJSと連携できるようにしました(linq.rx.js)
今後
VS2012に対するIntelliSenseの充実化がまだ1/5ぐらいしか出来ていないので、それの充実が優先です。あと、リファレンスやサンプルが書けてないので追加。それらが出来たら、いったんver.3として正式公開します。プログラミング生放送勉強会 第17回@品川 #pronama : ATNDで話すつもりなので、その日、8/25までには正式公開を目指します!というわけで是非是非聞きに来てください。
あ、あとnode.js用にnpm公開も、ですね。
linq.js LT資料
- JavaScript linq.js - 11.08/22
LTで簡単にlinq.jsの紹介をしましたので、その資料を。といっても、資料は全く使わないでLTの場では完全にデモ一本にしました。ええ、こういう場では、やっぱデモ優先のほうがいいかなー、と。資料は資料で、要素がきっちりまとまって紹介という感じなので、見てもらえればと思います。
スライドのテンプレは同じの使っていてそろそろ飽きたので、新しいのに変えたいところ。基本的にはテンプレのテーマまんまですが、やっぱ細かいところでスライドマスタの調整は必要なので、面倒くさー、と思ってしまい中々に気力が。むしろデザイン変更は一年に一回でいいかしらいいかしら?
そういえばどうでもよくないのですが、SlideshareをBlogに埋め込む時はlargeサイズを選んで欲しい。文字潰れてしまうもの、わざわざ小さいサイズで埋め込む必要はどこにもなくて。
文字列を先頭から見て同じところまで除去をlinq.jsとC#で解いてみた
- C# JavaScript Rx linq.js - 11.08/19
JavaScript で「文字列を先頭から見て同じところまで除去」をやってみました。という記事を見て、「linq.js を使いたかったのですが使いどころがパッと思い浮かびませんでした」とのことなので、linq.js - LINQ for JavaScriptで答えてみます。お題の元はお題:文字列を先頭から見て同じところまで除去からです。解き方も色々あると思いますが、最長の一致する文字を見つけて、それを元に文字列を削除していく、という方法を取ることにしました。
function dropStartsSame(array) { var seq = Enumerable.From(array); return Enumerable.From(seq.First()) .Scan("$+$$") .TakeWhile(function (x) { return seq.All(function (y) { return y.indexOf(x) == 0 }) }) .Insert(0, [""]) // 一つもマッチしなかった場合のため .TakeFromLast(1) .SelectMany(function (x) { return seq.Select(function (y) { return y.substring(x.length) }) }); } dropStartsSame(["abcdef", "abc123"]).WriteLine(); dropStartsSame(["あいうえお", "あいさんさん", "あいどる"]).WriteLine(); dropStartsSame(["12345", "67890", "12abc"]).WriteLine();
はい、ワンライナーで書けました、って何だか意味不明ですね!まず、例えば”abcdef”から[”a”,”ab”,”abc”,”abcd”,”abcde”,”abcdef”]を作ります。これはものすごく簡単で、Scanを使うだけです。
// ["a","ab","abc","abcd","abcde","abcdef"] Enumerable.From("abcdef").Scan("$+$$")
素晴らしい!そうして比較のタネができたら、あとは全てのindexOfが0(先頭に一致する)の間だけ取得(TakeWhile)します。[”abcdef”,”abc123″]だとシーケンスは[”a”,”ab”,”abc”]に絞られます。必要なのは最長のもの一つだけなのでTakeFromLast(1)で最後のものだけを取得。もし一つもマッチしなかった場合は代わりに”"が通るようにInsertで事前に先頭にさしてやってます。あとは、その”abc”を元にして文字列を置換したシーケンスを返してやるようにすればいい、というわけです、はい。
少し修正
SelectManyで繋げるのは悪趣味なので、ちょっと変えましょう。
function dropStartsSame(array) { var seq = Enumerable.From(array); var pre = Enumerable.From(seq.First()) .Scan("$+$$") .TakeWhile(function (x) { return seq.All(function (y) { return y.indexOf(x) == 0 }) }) .LastOrDefault(""); return seq.Select(function (x) { return x.substring(pre.length) }); }
変数を一つ置いてやるだけで随分とすっきり。無理に全部繋げるのはよくないね、という当たり前の話でした。
C# + Ix
C#とIxで書くとこうなるかな?基本的には同じです。(Ixって何?という人はneue cc - LINQ to Objects & Interactive Extensions & linq.js 全メソッド概説を参照ください)
static IEnumerable<string> DropStartsSame(params string[] args) { var pre = args.First() .Scan("", (x, y) => x + y) .TakeWhile(x => args.All(y => y.StartsWith(x))) .LastOrDefault() ?? ""; return args.Select(x => x.Substring(pre.Length)); } static void Main() { var x = DropStartsSame("abcdef", "abc123").SequenceEqual(new[] { "def", "123" }); var y = DropStartsSame("あいうえお", "あいさんさん", "あいどる").SequenceEqual(new[] { "うえお", "さんさん", "どる" }); var z = DropStartsSame("12345", "67890", "12abc").SequenceEqual(new[] { "12345", "67890", "12abc" }); Console.WriteLine(x == y == z == true); }
Ixで使ってるのはScanだけですけれど。
Deferの使い道
ところで、上のコードは遅延評価なのか遅延評価でないのか、微妙な感じです。preの計算までは即時で、その後は遅延されています。まるごと遅延したい場合はIxのDeferというメソッドが使えます。
// Deferで生成を遅延する static IEnumerable<string> DropStartsSame2(params string[] args) { return EnumerableEx.Defer(() => { var pre = args.First() .Scan("", (x, y) => x + y) .TakeWhile(x => args.All(y => y.StartsWith(x))) .LastOrDefault() ?? ""; return args.Select(x => x.Substring(pre.Length)); }); } // もしくはyield returnを使ってしまうという手も私はよく使っていました static IEnumerable<string> DropStartsSame3(params string[] args) { var pre = args.First() .Scan("", (x, y) => x + y) .TakeWhile(x => args.All(y => y.StartsWith(x))) .LastOrDefault() ?? ""; var query = args.Select(x => x.Substring(pre.Length)); foreach (var item in query) yield return item; } // 勿論、全部LINQで組んでしまってもOK static IEnumerable<string> DropStartsSame4(params string[] args) { return args.First() .Scan("", (x, y) => x + y) .TakeWhile(x => args.All(y => y.StartsWith(x))) .StartWith("") // linq.jsではInsert(0, [])でした .TakeLast(1) // linq.jsではTakeFromLastでした .SelectMany(x => args.Select(y => y.Substring(x.Length))); }
私はIx以前はyield returnを結構よく使ってました。今は、Deferのほうが、例えば if(args == null) throw new ArgumentNullException(); とかがそのまま書けるのでDeferを選びたいかも。この辺の評価タイミングの話は前回、詳説Ix Share/Memoize/Publish編(もしくはyield returnの注意点)で書きました。
まとめ
というわけで、Scanの使い方でした。Scan可愛いよScan。ようするにAggregateの計算途中も列挙する版なわけなので、これ、標準クエリ演算子にも入って欲しかったなあ。結構使えるシーン多いです。
ああ、あとJavaScriptでもforなんて使いません(キリッ。linq.jsは真面目に普通に多機能なので遅い、じゃなくて、いや、それはまあ事実なんですが、便利には違いないです。他の普通のコレクションライブラリじゃ出来ないことも平然と出来ます。でもかわりに(ry
linq.js入門記事を書きました
- JavaScript linq.js - 11.06/09
お話をいただき、@ITの.NET TIPSにlinq.jsの入門記事を、二週に渡り書きました。
- JavaScriptで配列をLINQにより処理できるライブラリ「linq.js」を利用するには? - @IT
- jQueryと「linq.js」を連携させてDOMをLINQにより処理するには? - @IT
このサイトでやると、アレもコレもとダラダラと書いてしまって分かりづらくなっていたのですが、記事では文字数制限などのお陰で構成がすっきり、校正してもらったお陰で文章の揺れもなく。つまるところ、ほとんど編集で助けてもらったというだけで、本当にありがとうございました。サンプルコードは、コードを見ただけで伝わるよう単純に、でもlinq.jsの威力を伝えなければならないので多少の複雑さは持たなければならない。などと思い結構悩んで作りました。
お陰様で反響も結構良かったみたいでなによりです。実績も良く分からない外部ライブラリは導入できない…… という方も、@ITに載ってるから大丈夫だよ!を説得材料(?)にできるのではないでしょうか。これを機に、是非試してみてください。
ところで幾つかの話。
メソッド名大文字
失敗した。かな……。特にTojQuery()でjQueryと見かけ上(あくまで見かけだけなんですが)シームレスに繋がっていると違和感が結構あります。以前に、jQueryとLINQの世界が視覚上切れて見えるから、むしろイイぐらいなんです、とか言ってましたが勿論ただの強がりです。この辺は今更変えにくいところで、どうしたものかな、と悩んでいるところです。
パフォーマンスについて
気になりますよね?ベンチマーク的に言えば、遅い。遅延評価の実現や豊富な機能は、速度を相当犠牲にしています。では、その遅さが許容できるほどか無視できないか。これは、卑怯な逃げ口になってしまいますが、状況次第。少なくとも普通のサイトでは問題ないレベルだと思いますし、また、ブラウザのJSはどんどん速くなっていってます。Chrome/Fx4/IE9の速さ!「許容できる」の範囲はどんどん広がっていってるのでは、と。
こういった話はC#でもそうです。LINQはベタforループより確実に遅い、が、じゃあベタforループで書くかといったら、よほどエクストリームに速度を求める場合以外は書きはしません。
Pentium3でWindows XPでIE6な奴らにも配慮する!というのも確かに美学なのですが、そうではない方向も見ないと、素敵な未来はやってこないのではないかな、って。21世紀にもなるのにループアンローリングで高速化!とかいう記事ばかりが踊る世界なんて悲しいじゃないですか。
モバイル機器のことも考えなければならないし、HTML5も控え、JavaScriptで高負荷な処理をすることも少なくないので、まだまだ時代は追いついていないけれど。それでも、私はもう少し先の未来を見ていたい……。JavaScriptがこれから先、本当に一級の言語となっていくのなら尚の事です。
l2o.js 世界最速分解解説
- JavaScript Rx - 11.01/17
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と総当り
- C# JavaScript linq.js - 10.12/28
各所で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なら、まあそれなりな速度で求まるかなー。
linq.js & Reactive Extensions for JavaScript(RxJS)入門
- JavaScript Rx linq.js - 10.12/20
このエントリはJavaScript Advent Calendar 2010 : ATNDの20日目として書きます。一つ前はsecondlifeさんのコマンドラインから JavaScript のシンタックスチェックを行う方法 - って、なんでですか〜 - subtechでした。
普段はC#でもしゃもしゃしている、@neuecc(twitter)といいます。そんなわけで今回はC#畑からのJavaScriptライブラリを二つほど紹介します。
ここではC#の中でも、LINQ: .NET 統合言語クエリという機能から来ているlinq.jsとRxJSを紹介します。linq.jsはコレクション操作を、RxJSはイベント操作と非同期操作に関するライブラリとなっています。どちらもwindowオブジェクトには一切触らない、DOMやXmlHttpRequestとは全く無関係の中立で地味な、phpspotで紹介されそうもない、それだけで何が出来るかというと別に何もできないけれど、あると便利。また、Enumerable(linq.js)とObservable(RxJS)という枠組みが双対であるという、面白い影響の与え方をしているため、セットで考えるとより深イイ話になるから、ちょっと長くなりますが二つ一緒に紹介します。
linq.js
linq.jsはLINQのうち、Linq to ObjectsというものをJavaScriptに移植したものとなります。ダウンロードは下記URLから。ライセンスはMs-PL、というと聞きなれないかもしれませんが、MITライセンスに近い、かなり緩めのものとなっています。
公式で移植されたものではなく、野良で勝手にやっているだけの話であり、作者は、私です。ん……。あ……。……。さて、LINQとは何ぞや、統合言語クエリがうんちゃらかんちゃら、SQLがどうのこうの、というのはJS的には全く関係ないのでスルーで!Linq to Objectsに絞って何ぞやというと、「関数型っぽいコレクション処理ライブラリ」です。ひとまず簡単な例から。
// こんなテキトーな配列があったとして var array = [101, 20, 2, 42, 33, 47, 52]; // 偶数だけ二倍して出力 : 40, 4, 84, 104 Enumerable.From(array) .Where(function(x){ return x % 2 == 0 }) .Select(function(x){ return x * 2 }) .ForEach(function(x){ document.writeln(x) });
よくある関数を渡してコレクション処理するスタイルです。グローバルに配置される名前空間はEnumerable。そこから、既存の配列に対して適用する場合はFromで包んで(jQueryの$のようなものと考えればOK、数字列の場合はRangeなどもあります)、以降はLinqの専用メソッドをチェーンで繋いでいく、というスタイルを取ります。ちなみにEnumerableはイニュミラボーと読むようです。最後のボーは、私は日本人なのでブルって言いますが。イニュミラブル。
ところでfilterしてmapしてforeach。そうですFirefoxにあるArrayへのmap/filterです。Whereがfilter、Selectがmap。名前はSQL風味ですが、つまるところ単純な話そういうことです。フィルタぐらいならjQueryにもmap/grep/eachがあるね。他に目を向ければ、コレクション系専用ライブラリではUnderscore.jsが有名のようです。
それらとの違いですが、一つはfunction(){}という、冗長にすぎる関数記述を省く文字列による簡易記法の搭載。もう一つは完全な遅延評価の実現。そのため、適用する関数を自由に追加していくことができます。簡単な例をもう少し。
var array = [101, 20, 2, 42, 33, 47, 52]; // この時点ではまだ列挙されていない。 // $は引数を示すプレースホルダーで、この場合 function(x){ return x%2==0 } と同じ var query = Enumerable.From(array).Where("$%2==0"); // 更に二乗したうえで配列へ変換するならToArray var array2 = query.Select("$*$").ToArray(); // [400, 4, 1764, 2704] // 昇順に並び替えた上でセパレータをつけて文字列化 var orderedStr = query.OrderBy().ToString(":"); // "2:20:42:52" // 先頭二つのみを列挙 query.Take(2).ForEach("alert($)"); // 20, 2
と、いったように様々なメソッドを繋げてコレクションを変形し、最後に任意の形に変換するというのが基本的な利用法になるわけです。わからん。というわけで図にするとこんな感じです。
構図的にはjQueryと同じで、Enumerable.From/Range/etc.. によってEnumerableオブジェクトが生成され、その中でのメソッド(Select/Where/etc..)はEnumerableオブジェクトを返す。そのため幾らでもメソッドチェーンを繋げられる。jQueryと違うのは、繋げた関数が実行されるのは、戻り値がEnumerable以外になるときまで遅延される。Enumerable以外を返すメソッドとは、ToArray(配列に変換)、ToString(文字列に変換)、ToObject(オブジェクトに変換)、Contains(値が含まれていればtrue、含まれていなければfalse)、Max(最大値を返す)、ForEach(列挙する)、etc…(まだまだ大量にあります)。
遅延評価であることによるメリットとして、無限リストを扱えることが挙げられます。というわけで、無限リストを活用する例を一つ。「nを1から初めてその2乗を足していき、和が2000を初めて超えたとき和はいくつになるかという問題」をScalaで解いてみたから、nを1から(以下略)をlinq.jsで解いてみます。
var result = Enumerable.ToInfinity(1) // 1から無限大まで数値をジェネレート [1, 2, 3, 4,...] .Select("$*$") // 二乗 [1, 4, 9, 16,...] .Scan("$+$$") // 和 [1,5,14,30,...] .First("$>2000"); // 2000を超えた最初の要素
元がC#/Linq版のコードであるから当然ではありますが、リンク先のScala版のコードと完全に一致、ですね。JavaScriptは関数型言語。無限リストの生成には、今回はToInifinityを使いましたが、関数型言語に馴染みのある人ならばUnfoldなどもありますので、望む物が生成出来るはずです。
“$+$$”でサクッと和を出せるのは中々強力で便利。やりすぎるとイミフになるので、こういうササッとした部分にだけ限定すれば。任意の識別子を使いたい場合は”sum, x => sum + x”というように=>の左側は引数、右側は式、という形でも書けます。なお、実装は new Function(”$,$$,$$$,$$$$”, “return ” + expression) というだけです。渡す文字列が式でないとダメな非常に単純な理由。
その他、メソッド類の一覧と、その場で実行/確認可能なLINQ Padはlinq.js Referenceで。
といったように、リアルタイムに実行結果を確認しながら試せます。無限リストを停止条件つけないで書いてしまっても、列挙は1000件までにリミッターかかるので一安心。
メソッド群ですが、LINQにある標準クエリ演算子(と、言うと大仰ですが、ようするにただのメソッドです)を全て実装、その上でHaskellやRubyなどのコレクション用メソッドを眺めて、便利そうなら移植してあります。そのため、コレクションライブラリとしてはとしてこのメソッドが不足してる!と不満を感じることはないはず。また、遅延評価を活かしてメソッドを組み合わせることにより、大抵の操作が可能になっています。
とはいえ、そのせいで肥大しすぎな感がなきにしもあらず。とりあえず、フィルタ(Where)とマップ(Select)、非破壊的で複数キー連結が可能なソート(OrderBy/ThenBy)、重複した値を取り除く(Distinct)といった辺りだけ押さえておけば良いかなー、と。
そういえばでドットが前置なのは何で?というと、その方が入力補完に便利だからです。
この辺の話はJavaScriptエディタとしてのVisual Studioの使い方入門で。IDEを使ってJSを書くなら、ドットは前置一択になります。補完のない普通のエディタで書くのならスタイルはお好みで。私としては、入力補完・コードフォーマッタ・デバッガとのシームレスな融合などなどから、JavaScriptであってもIDEを使うのが良いと思ってます。
Reactive Extensions for JavaScript
続けてReactive Extensions for JavaScript(RxJS)について。Linqという名前は付いていませんがLinqの一味と見なせます。いわばLinq to Events, Linq to Asynchronous。ということで、対象となるのはイベントと非同期。ダウンロードは下記URLの右側、Rx for JavaScriptから。
Reactive Extensions for .NET (Rx)
こちらはlinq.jsと違って公式が提供しています。
さて、ではこれは何が出来るのでしょう。出来るのは(Functional) Reactive Programmingです。とは何ぞや。というと、既に親切な解説があるので やさしいFunctional reactive programming(概要編) - maoeのブログ そちらを見るといいと思うな!
とりあえず簡単な例をまず先に。
// マウスの動きの座標ストリーム(無限リスト) var mousemove = $("#js_advcal_field").toObservable("mousemove") .Select(function (e) { return { X: e.pageX, Y: e.pageY} }); // 位置をTextに書き出し mousemove.Subscribe(function (p) { $("#js_advcal_status").text("X=" + p.X + ":Y=" + p.Y) }); // 1.5秒遅れて四角形を座標位置に出す mousemove.Delay(1500) .Subscribe(function (p) { $("#js_advcal_rect").css({ left: p.X, top: p.Y }) });
コード似てませんか?今まで出してきたLinq to Objectsのコードに。RxJSは、イベントをコレクションとして扱うことでフィルタ(Where)やマップ(Select)を可能にします。図にするとこうです。
toObservableすることにより、jQueryでアタッチされるMouseMoveイベントは、時間軸上に無限に発生し続ける「無限リスト:Observable Collections」として変換されます。このコレクションの上では時間軸を自由に扱えるため(そもそもMouseMove自体が、いつ次の値が発生するか不確定な時間の上にのっている)、ごくごく自然に、1.5秒後(Delay)という指示を与えるだけで到達時間を遅らせることが出来ます。
RxJSもlinq.jsと同じく、基本的にメソッドの戻り値はObservableオブジェクトとなっていてひたすらメソッドチェーンしていきます。違うのは、linq.jsはToArrayやToString、ForEachなどなど、Enumerable外に出るメソッドが複数ありますが、Observableの場合はSubscribeのみです。SubscribeメソッドがForEachのような役割を担っています。何でToArray出来ないの!というと理由は簡単で、扱う対象が時間軸上に流れる無限リストだからです。無限を有限のArrayに変換は出来ませんよねー。本質的にObservable Collectionsは非同期な状態なので、何か戻り値を返すということは出来ません。出来るのは、向こうからやってくる値に対して実行する処理を示すことだけです。
RxJSは、扱いにくい時間や非同期、複数のイベントが同時に絡む時のイベントの合成を、慣れ親しんだシンプルなコレクション処理のように見た目上落としこんで、foreachするように処理を適用することが出来ます。それが特徴となります。
jQuery
前のコードに出ていた$(”#hoge”)はjQueryです。脈絡なく出してきているわけですね!解説が前後してれぅー。どういうことなのかというと、RxJSは基本的にwindowとは中立です、が、メインで扱う物はDOM(イベント)だったりXmlHttpRequest(非同期)で、これらは抽出の必要があったりクロスブラウザの必要があったりと、一手間二手間な問題を抱えている。それを解決してくれるのがjQueryだったり他のライブラリだったりするわけですね。そこで取られた手段が、jQueryに乗っかること。ようするにプラグイン。RxJSとjQueryを繋ぐ部分を注入。そうして注入されたのがtoObservableというわけです。
linq.jsもjQueryもRxJSも、基本的にメソッドチェーンで自分の世界に閉じっぱなしです。jQueryはモナドだという記事がありましたが、linq.jsもモナドです。RxJSもモナドです。いや、本当に。ただそのへんの理屈は割とどうでもいいわけですが、ただ、交互に変換出来ると便利よねー、なところはあるわけで、三者はプラグインを介して上記の図のような形で遷移可能になっています。
Asynchronous
最後に非同期を。非同期もObservable Collectionsになる、というわけで例から行きましょう。
$("#js_advcal_twbutton").toObservable("click") .Do(function () { $("#js_advcal_twitter").empty() }) .SelectMany(function () { return $.ajaxAsObservable({ url: "http://twitter.com/statuses/public_timeline.json", dataType: "jsonp" }) }) .SelectMany(function (json) { return Rx.Observable.FromArray(json.data) }) .Where(function (status) { return status.user.lang == "ja" }) .Select(function (status) { return $("<p>").text(status.user.screen_name + ":" + status.text) }) .Subscribe(function (q) { $("#js_advcal_twitter").append(q); });
胡散臭い(笑)public_timelineボタンをクリックすると、Twitterのpublic_timelineから日本人のツイート(user.lang==”ja”)のみを表示します。これは、ちょっとメソッドチェーンが多めで微妙にワケワカラン。そんな困ったときはとりあえず図。
起点はボタンのクリックです。これもまたMouseMoveの時と同じで考え方としては無限リスト状。一回目のクリック、二回目のクリック、と無限に続いていきます。このコレクションに対する処理として、次に流れてくるのはDo。これは副作用で、コレクションの値以外の、流れてきたということをトリガーにして外部に影響を与えたい時に使います。今回はクリックをトリガーとして、一旦DIVの中の要素をクリア(empty())しています。
そしてSelectMany。SelectManyはSelectと似ていますが、1:多(だからMany)へと分配するのが特徴です。ここでコレクションの流れは非同期へとバトンタッチされます。非同期のリクエスト、今回はjQueryにおんぶに抱っこでajaxAsObservableにより、twitterのpublic_timeline.jsonからデータを取得。特徴的なのは、非同期なので戻り値が得られるまでに若干のタイムラグがあるわけですが、それは以前に扱ったDelayと同じように、時間軸が少し右に移るだけで、流れ自体はそのままで扱えてしまいます。
1:多ですが、非同期リクエストの戻り値は1なので、見た目上は一個の値が変形しているだけのように見えて、再び次のSelectMany。ここでの値はJSONで、20個分のpublic_timelineのstatusが届いています。それを1:他という形でバラす。RxJS上では「イベント」「非同期」を載せてきましたが、「配列」も問題なく載っかるということです。
ここまで来たら、あとは普通のコレクション処理と同じようにWhereでフィルタリングし(言語が”ja”のみを通す)、Selectで変形し(jQueryで新しいDOMノードを作成)、Subscribeで実行処理を書く(divの中にappend)。
というわけで、「イベント」「非同期」「配列」を一本のコレクションへと合成し、統合しました。一見すると、ただそれだけのことに何でわざわざ複雑めいたことを、と思えてしまいますが、複数の非同期を合成したくなったら?待ち合せたくなったら?などなど、シチュエーションが複雑になればなるほどに、威力を発揮します。
つまりそれJSDeferredで…
です。領域はかなり被ると思います。waitはDelay、nextはSelectまたはSelectManyが相当するでしょう。
// Hello -> 5秒後 -> HelloWorld のalertが出るというもの(UIはフリーズしません) Rx.Observable.Return("Hello") // パイプラインに"Hello"を流し始める .Do(function (x) { alert(x) }) // alertを出す .Delay(5000) // 5秒遅延 .Select(function (x) { return x + "World" }) // 値にWorldを結合 .Subscribe(function (x) { alert(x) }); // パイプラインの実行開始+alertを出す
例はJSDeferred 紹介より、少し違いますが。また、関数のDeferred化のdeferred.call/failはAsyncSubjectのOnNext/OnErrorが相当しそうです。詳しい話はまたそのうち、もしくはC#で良ければReactive Extensionsの非同期周りの解説と自前実装などを。
まとめ
なげー。スミマセンスミマセン。Enumerable -> コレクション処理 -> 無限リスト -> Observable -> イベントが無限リスト -> 時間軸が不定 -> 非同期 -> コレクション処理。という流れのつもりでした!分量的にEnumerableかObservableか、どっちかに絞るべきでしたね……。もっとあっさり終えるはずだったのにどうしてこうなった。
prototype.jsはRubyっぽい色がある。Firefoxで拡張が続いてるJavaScriptはPythonからの影響が濃ゆい感じ。linq.js/RxJSは勿論C#からで、更にLinqの元はSQLは勿論なのですが、Haskellからの影響も濃く(Linqや、そして現在はRxの開発チームを率いているErik MeijerはHaskellの人で、The Haskell 98 Language Reportにも名前を連ねている)、そうこうして他言語同士が相互に影響を与えてより良くなる。というのはイイ話だなー、って思っていまして。
そして、他言語の文化を受け入れられる懐の広さと、それでもなお自分の色を持ち続けられるJavaScriptってイイ言語だよね、と思います。
と、駄エントリを〆て次のAdvent Calendarにタッチ!
Titanium Mobile + Visual Studio用のAPI入力補完vsdoc自動生成T4 Temlate
- C# JavaScript - 10.12/14
Titanium Mobileをご存知ですか?私はつい最近知ったばかりです。かなりHotみたいで紹介をよく見るし、はてブでも色々な記事がブクマ数百行ってたりしますが、さっぱり意識していなかったせいで右から左に流れていくだけで名前を覚えていませんでした。が、 【就職先決定】Titanium MobileのAppceleratorに勤めることになりました - @masuidrive blog を見て、ようやくTitanium Mobileの名前と出来ることが一致しました。つまりはJavaScriptでNativeなAndroid/iPhoneアプリケーションが作れて、とってもHotなテクノロジだそうですはい。
ちょっと触った感じコンパイルというかコード変換というか、な部分だけが提供されてるような雰囲気でIDEとかそーいうのは今のところないようで?かしらん。デバッガとかないのかな、printオンリーかしら……。ともあれ、Javascriptということで何で開発してもいいわけですね。エディタですか?ありえない!入力補完のない環境で開発なんて出来ません。デバッガがなくても生きていけますが入力補完はないと生きていけません。
というわけでVisual Studioです。以前にJavaScriptエディタとしてのVisual Studioの使い方入門という記事を書いたように、Visual StudioはJavaScriptエディタとしても最高なのです。さて、では入力補完だ。というわけで捜すとあった。Appcelerator Titanium vsdoc for Visual Studio。へー、確かに動いてるね、簡単だね。以上。
と、まあ興味持った当時はそれで一旦終えたのですが、昨日に【追記あり】「Titanium Mobile1.4」の入力支援をMicrosoft Visual Web Developer 2010 Expressで行う。 - 葛城の日記という記事を見て、そもそも元はjsonとして公式にデータが提供されていてそれを加工したものであること、またその加工っぷりはちょっといま一つであることを知りました。
さて、前置きが長くなりましたが、つまりイマイチなら自分で作ればいいんですね。幸いにもデータは公式で用意してくれているので。
T4でjsonからvsdocを生成する
いつもならドヤ顔でコード張るんですが、ちょっと長いのとC#の話なのでスルー。というわけでコードはBitbucketに置いておきますのでご自由にどうぞ。TitaniumMobileApi-vsdoc.tt。使い方は後ほど説明します。そんなの面倒臭いー、という人は生成済みのほうをどうぞ。TitaniumMobileApi-vsdoc.js。2010/12/10のver1.5.0で生成していますが、どうせVS使うならそんな手間でもないので、.ttで自分で生成するほうをお薦めします。
.ttの使い方
.tt、の前にVisual StudioでのJavaScriptの開発の仕方。ですがJavaScriptエディタとしてのVisual Studioの使い方入門で解説しているのでその通りというわけで省略。
TitaniumMobileApi-vsdoc.ttと、公式サイトにあるAPIリファレンスのJSON(api.json)を同じフォルダに入れて、TitaniumMobileApi-vsdoc.ttを開き保存(Ctrl+S)を押します。するとTitaniumMobileApi-vsdoc.jsが生成されます。オシマイ。
こんな感じに階層が出来て、8000行ほどのjsが生成されているはずです!
さて、では使ってみましょう。適当な名前のjsを作り、reference pathに先ほど生成されたjsを記述。あとはTitanium名前空間から始まるので、ポンポンと書くだけ。
解説付きで引数などなどもバッチシ!
煩わしい大量の定数も、入力補完で一覧が出るので楽ちん!
海外の、Jeremy Meltonさんの作ったvsdocとの最大の違いは、関数での生成後の戻り値のObjectもある程度補完し続けてくれることです。例えばcreateHogeとか。
これ、もとのapi.jsonでは戻り値がobjectとして記載されているため、普通に自動生成するだけだと追随出来ないのです。私はただの機械的な変換ではなく、実用に根ざしたアドホックな対応を施したため、多少は追随するようになっています。
ただし、一点だけ残念なことがあって、プロパティ名にハイフンがつくものは補完に表示されません。例えばMapViewはfont-familyといったプロパティを持つのですが補完に出てきません。これはVisual Studioの仕様というか不具合というか残念な点というか。直るといいです。要望としての報告が出てなければ報告しよう……。と思って要望を出してしまったのですが、そもそもJavaScript的にハイフンのつくのが動くほうがオカシイ!普通は動かん!ということに要望出して30秒後に気づきました。頭がホカホカだったので全然考えが回ってなかったのでする。こういうのって出した直後に頭が冷えて気づきますよね。というわけで、これはむしろTitanium MobileのAPIがオカシイのでは感。そして私は悲しいほどに赤っ恥を書いてしまった、いや、今更一つや二つ、しょうもない恥が増えてもいいんですけど、いいんですけど、恥ずかしすぎるぅ。泣きたい。
余談(T4の話)
配布用なので1ファイルにするためJsonReaderWriterFactoryを生で使って書いてる(ためちょっと冗長)のですが、そうでなければDynamicJson使ってサクッと済ませます。DynamicJsonは楽ですね、ほんと。
あと、T4で困ったのが末尾カンマ。最後の要素だけカンマ不要なわけなんですよねー、これがどう生成したものか困ってしまって。ただの文字列ならstring.Join(”,”,xs)とかで対応するわけですが、T4だとインデントとかの絡みもあるので、そういうのは使わない方が楽だし綺麗に仕上がる、けれど上手く出来ない!とりあえず今回はこんな対応をしてみました。
// こんなコードを吐く、つまり最後の要素の末尾には","は不要 { "anchorPoint": null, "animate": true, "animatedCenterPoint": null } // こんな拡張メソッド(第二引数のboolは末尾でないか末尾か) public static void Each<T>(this T[] source, Action<T, bool> action) { for (int i = 0; i < source.Length; i++) { var hasNext = (i < source.Length - 1); action(source[i], hasNext); } } // T4のView側はこんな感じで、最後に<#= hasNext ? "," : "" #>をつけて対応 <#o.Properties.Each((x, hasNext) =>{#> "<#=x.Name #>": <#=x.Value #><#= hasNext ? "," : "" #> <#});#>
微妙に苦しい感じでしたねえ。こういうのは ASP.NET/ASP.NET MVCではどう対応しているものなのかしらん。全然知らないので誰か教えてくれれば嬉しいです。
まとめ
私はこの手のダミーのvsdoc生成は色々やっていて、linq.jsのvsdocは手作業、RxJSのvsdoc作りはmono.cecilを使ってdllとXMLから結合するConsoleApplicationを作成、などなどで割と手慣れてきました。テンプレートが統合されている点と(printlnってねえ…)更新の楽さ(新しいjsonが出たらそれを放りこんで再セーブするだけ、ある意味T4はC#/VSにおけるLL的スクリプト言語と見ても良いのではないでしょーか)を考えると、T4で作るのがベストな選択に思えます。スタンドアロンで動かしたい場合は”前処理されたテキストテンプレート”も使えますしね。
さて、こうしてわざわざ手間かけて入力補完作る理由ですが、だって補完ないとやる気出ませんから!IDEで楽が出来ないなら楽が出来るよう苦労すればいいぢゃない。そしてIDEに依存するのもまた美徳です。プログラマの三大美徳。楽で何が悪いって?何も悪くないよ!あと、誰か一人が苦労すれば、お裾分け出来るわけなので、先立って作ってシェアしたいところです。
というわけで、Visual StudioでiPhone開発もAndroid開発も幸せになりましょー。個人的にはTitaniumは良いと思いますが、C#で開発できるmonotouchやmonodroidに興味津々ではありますが。んで、何で手を出し始めているかというと、linq.jsを使うシーンを求めてるんですぅー。ふふふ。というわけで、Titanium Mobileな方はlinq.jsもよろしぅお願いします。
C#から使うMicrosoft Ajax MinifierでのJavaScriptコード圧縮
- C# JavaScript - 10.09/09
いつのまにか独立してCodePlex入りしているMicrosoft Ajax Minifier。その名の通り、Microsoft謹製のJavaScript/CSSの圧縮/整形ツールです。発表からすぐに、GoogleのClosure Compilerが出てしまったのですっかり影も薄く、ていうか名前悪いよね、Ajax関係ないじゃん……。CodePlexのプロジェクトページも何だか活気ない寂れた感じで、あーあ、といった趣。良いツールなんですけどねえ。
コマンドライン版とDLL版が用意されていて、コマンドラインの、単体で実行可能なexeの解説は【ハウツー】Microsoft Ajax MinifierでJavaScriptを縮小化しようで解説されているので、DLL版の使い方を簡単に解説します。
C#が使える人ならばDLL版のほうが遥かに使いやすかったりして。設定をダラダラと引数を連ねるのではなく、オブジェクト初期化子とenumで出来るので、そう、IntelliSenseが効くわけです。ヘルプ要らずで書けるのは快適。Visual Studioは偉大だなぁ。コマンドライン, PowerShellを捨て、VSのConsoleApplicationを常に立ち上げよう。実際、ちょっとしたテキスト処理とかC#で書いちゃうんですよねえ、私。Linqが楽だから。
Minifierオブジェクトを作ってMinifyJavaScript(CSSの場合はMinifyStyleSheet)メソッドを実行するだけなので、コード見たほうが早いかな。例としてlinq.jsをlinq.min.jsに圧縮します。そして、解析されたWarningをコンソールに表示します。ただの圧縮だけでなく、このコード分析機能が実にありがたい。
using System; using System.IO; using Microsoft.Ajax.Utilities; class Program { static void Main(string[] args) { var minifier = new Minifier { WarningLevel = 3, // 最大4。4はウザいのが多いので3がいいね。 FileName = "linq.js" // エラー表示用に使うだけなので、なくてもいい }; var settings = new CodeSettings // CSSの場合はCSSSettings { LocalRenaming = LocalRenaming.CrunchAll, OutputMode = OutputMode.SingleLine, // MultiLineにすると整形 IndentSize = 4, // SingleLineの時は意味なし CollapseToLiteral = true, CombineDuplicateLiterals = true // その他いろいろ(それぞれの意味はIntelliSenseのsummaryを読めば分かるね!) }; var load = System.IO.File.ReadAllText(@"linq.js"); // 読み込み // 最後の引数はグローバル領域に何の変数が定義されてるか指定するもの // 別になくても構わないんだけど、warningに出るので多少は指定しておく var result = minifier.MinifyJavaScript(load, settings, "Enumerable", "Enumerator", "JSON", "console", "$", "jQuery"); // Warningで発見されたエラーはErrorsに格納されてる foreach (var item in minifier.Errors) { Console.WriteLine(item); } File.WriteAllText("linq.min.js", result); // 書き出し Console.WriteLine(); Console.WriteLine("Original:" + new FileInfo("linq.js").Length / 1024 + "KB"); Console.WriteLine("Minified:" + new FileInfo("linq.min.js").Length / 1024 + "KB"); } }
二つエラー出てますね。変数がvarで宣言されてないそうです。つまり、コードがその行通ると変数がグローバルに置かれてしまいます。確認したら、OrderBy実行したらsortContextという変数がグローバルに飛んでしまってました。ええ、つまりバグと言っていいです。……。あうあう、すみません、直します。これからは必ずAjax Minifierの分析を通してから公開しよう。どうでもよくどうでもよくないんですがlinq.jsもアップデートすべきことが結構溜まってるので近いうちには……。
というわけで、Ajax Minifierの偉大さが分かりました。素晴らしい!みんな使おう!DLLの形になっているので、T4 Templateと混ぜたりなどもしやすく、夢が膨らむ。minifyだけでなく、JSのパーサーとして抽象構文木を取り出したりなども出来ます。
WebでGUIでサクッと実行できる環境とかあるといいと思うんですよね。それこそDLLになっているので、Silverlightならすぐ作れるわけですし。誰か作ればいいのに。いや、お前が作れよって話なんですが。Ajax Minifierが出てすぐの頃に作ろうとしてお蔵入りしちゃったんだよね、ふーみぅ。そうそう、あと、VSと統合されて欲しい!このWarningは大変強力なので、その場でVSのエラー一覧に表示して欲しいぐらい。今のVSのエラー表示はWarningLevelで言うところの1ぐらいで警告出してくれて、それはそれで良い感じなんですが、やっぱこう、もっとビシッと言って欲しいわけですよ。
ワンクリックで整形/圧縮してくれるのとエラー一覧表示が出来るようなVisual Studio拡張を誰か作って欲しいなあ。いやー、本気で欲しいので、とりあえず私も自分で作ってみようと思い、VS2010 SDKは入れてみた。気力が折れてなければ来月ぐらいには公開、したい、です、予定は未定のやるやる詐欺ですががが。
linq.jsやRxJSのベンチマーク
- JavaScript linq.js - 10.08/11
どうも、定期的linq.js - LINQ for JavaScript宣伝の会がやってまいりました。最近はページビューも絶好調、なのだけどDL数はそこまで伸びない(でも同種のライブラリよりもDL数多かったりするので需要が限界値と思われる)などなどな近況ですがこんばんわ。乱立するLinqのJavaScript実装……。などと言うほどに乱立はしてないし、そもそも2009/04に最後発で私が出したのが最後で、それ以降の新顔は見かけないのですが(しいて言えばRxJS)、ちょうどjLinqを実装した人が、ベンチ結果がボロボロだった、作り直してるという記事を出したので、ほぅほぅとそのベンチマークを見て、ちょっと改良して色々なLinq実装で比較してみました。
左のがIE8、重ねて後ろ側のがChrome。この画像は77件のJSONをGroupIDが107か185のもののみをフィルタして配列を返すという処理を1000回試行したもの。毎度思いますが、V8恐ろしく速い。そりゃnode.jsとか普通に現実的な話ですよね、大変素晴らしい。
jOrderについて
このベンチマークは、もとはjOrderという、Linq……ではなくてSQL風のもので(SQLっぽいのは結構いっぱいあります)、巨大なJSONを効率よく抽出するために、先にインデックス的なのを作ってそれから処理すれば速くなるよ!というライブラリが先々月ぐらいに出来たばっからしいのですが、それがjLinqと比較してこれだけ速いぜ!とやっていたようです。結果見る限りはjLinqクソ遅くてjOrderクソ速くて凄ー、となったのですが、なんかどーにも胡散臭さが拭えないわけですよ、ベンチ詐欺に片足突っ込んでいるというか。
jOrderは初回にインデックスっぽいものを作成するので、二回目以降の抽出は爆速、というのがウリ(っぽい)ようで、ベンチは確かに速い。で、その初回のインデックス生成は何時やってるんでしょうか?このベンチのソースを見ると、ボタンを押してからじゃなくて、ページのロード時にやってますね……。あの、それも立派なコストなのですが、無視ですか?無視ですか?そりゃあ試行回数を1000でベンチ取るならば無視出来るほどに小さいかもですね?でも、Test Cycles 1とか用意しているわけですが、どうなんでしょうね、インデックス作成時間を無視するのは、ちょっと卑怯すぎやしませんか?そもそも対象にひぢょーに遅いjLinq「だけ」を選んでいるというところがやらしい。
というわけで、オリジナルのベンチにはないのですがwith create indexというボタン押してからインデックスを作成する項目を足しました。1000回の試行では、コンセプトに乗っ取るなら1回のインデックス作成にすべきなんでしょうが、普通に1000回インデックス作成に走るのでクソ遅いです。あ、いや、別にアンチキャンペーン張ろうってわけじゃあないんですが、単純に面倒なので……。インデックス作成コストは試行回数1にすれば分かる。
ベンチ結果を見ると、まず、インデックス的なものの作成には非常にコストがかかってる。そして、わざわざコストをかけて生成したところで、Small table(77件のJSON)では、フィルタリングに関してはjQueryの$.grep、つまりは何も手をかけてないシンプルなフィルタリングと同じ速度でしかなくて、あまり意味が無い。Large table(1000件のJSON)ではそれなりな効果が出ているようですが、インデックス作成コストをペイするまでの試行回数を考えると、やはりあまり意味がなさそうな……。コンセプトは面白いんですが、それ止まりかなあ。機能的には、このインデックス生成一点勝負なところがあるので、他のLinq系ライブラリのような多機能なクエリ手段があるわけでもないし。
その他のライブラリについて
どれも似たり寄ったりで同じことが出来ますが、処理内容は全然違います。linq.jsは遅延評価であることと、列挙終了時にDisposeすることを中心に据えているので、シンプルにフィルタするだけのもの(jQueryの$.grepとか)よりも遥かに遅くなっています。JSINQも同じく遅延評価で、実装も大体似てます。なので、計測結果もほぼ同じですが、linq.jsのほうが遅い。これは、jsinqはDisposeがないため、その分の速度差が出ています(それ以外にも、単純にlinq.jsのほうが色々処理挟んでて遅め)。
LINQ to JavaScript(JSLINQ)はLINQの名を冠していますが、即時評価で、中身はただの配列のラッパーです。その分だけ単純な実装になっているので、単純なことをこなすには速い。jQueryの$.grepも同じく、普通に配列をグルッとループ回してifで弾いて、新しい配列にpushして、新しい配列を返すもの。というわけで、両者はほとんど同じ速度です。ただ、若干jQueryのほうが速いようで。これは、JSLINQはthis.itemsという形で対象の配列にアクセスしていて、それが速度差になってる模様。var items = this.itemsと列挙の前に置いてやれば、jQueryとほぼ同じ速度になる。1000回の試行だと20msecぐらいの差にはなるようですね。これが気にするほどかは、どうでしょう……。私は全く気にしません。
残念なことにめっちゃ遅いjLinqは、うーん、中はevalだらけだそうで、それが響いたそうです。と、作者が言ってるのでそうなのでしょう(適当)。RxJSも割と遅いんですが、これはしょうがないね!C#でもToObservableで変換かけたものの速度は割と遅くなるし。構造的に中間にいっぱい処理が入るので、そういうものだということで。
速度ねえ……
jLinqはさすがにアレゲなのですが、それ以外は別に普通に使う範囲ではそんな致命的に低速ってわけでもないんで、あまり気にしなくても良くね?と、かなり思ってます。linq.jsは速度を犠牲にして遅延評価だのDisposeだの入れてるわけですが、勿論、犠牲にしたなりのメリットはある(表現できる幅がとっても広がる)し。その辺はトレードオフ。配列をSelectしてToArrayするだけ、とかWhereしてToArrayするだけならば、、どうせjQueryも一緒に使うでしょ?的に考えて、jQueryの$.map, $.grepを使えば精神衛生上良いかもしれません。これは、C#で言うところのArray.ConvertAllは化石メソッドだけど、SelectしてToArrayならばConvertAllのほうが高効率なんだぜ(内心はどうでもいーんだけど)、といったようなノリで補えば良いでしょう。
それにしても、何でjQueryは$.eachの引数がmapやgrepと逆(eachだけindexが第一引数で値が第二引数)なんですかね。これ、統一してたほうが良いし、だいたいがして値が第一引数のほうが使いやすいのに。もう今更変えられない、ということなのかしらん。
そういえばで、せっかくなので「表現できる幅」の例として、ベンチには第一ソートキーにCurrency、それが重複してた場合の第二ソートキーにTotalを指定してみた例(OrderBy.ThenBy)とか(linq.js無しで書くとちょびっと面倒だよ!)、GroupIDでグルーピングした後にTotal値を合計といった集計演算(これもlinq.js無しだと面倒だよ!)とかを入れておいたので、良ければ見といてください。はい。まあ、別にこの辺はeager evaluationでも出来るというかソートもグルーピングも一度バッファに貯めちゃってるんですけどね!
まとめ
JSINQは良く出来てると思うのよ。ほんと(私はただのLinqマニアなので、基本的に他の実装は割と読んでますですよ)。ベンチ的にもlinq.jsより速いし(Disposeないからね、でもDispose使うシーンがそもそもあんまないという)、文字列クエリ式も(使わないけど)使えるし。じゃあ、JSINQじゃなくてlinq.jsがイイ!というような押しは、そこまであるかないか、どうなんでしょうね。1.メソッドの数が全然違う 2.ラムダ式的な文字列セレクターが使える 3.Dispose対応 4.RxJSにも対応 5.jQueryにも対応 6.WSHにも対応 7.VS用IntelliSense完備。ふむ、結構ありますね。というわけでlinq.jsお薦め。冒頭でも言いましたが最近のCodePlex上でのページビュー/ダウンロード数を見ると、競合のlinq移植ライブラリの中でもトップなんですよ、えへへ。まあ、4DL/dayとかいうショボい戦いなのですが。
jLinqの人が、パフォーマンス改善のついでにLinqという名前をやめてブランディングやり直すって言ってますが、きっと正しいと思う。「Linq」という名前がつく限りは「.NETの~」という印象が避けられないし、そのせいで敬遠されるというのは、間違いなくある。jLinqは、中身全然Linqじゃない独特な感じのなので、名前変えるのは、きっと良い選択。
linq.jsは100% Linqなので名前がどうこうってのはないですが、しかし、RxJSもそうなのだけど、.NET以外の人にも使って欲しいなって気持ちはとてもあります。やれる限りは頑張ってるつもりなんですが、中々どうして。JavaScriptエディタとしてのVisual Studioの使い方入門は100ブクマまであとちょい!な感じで、そういうとこに混ぜて宣伝とかいうセコい策を取ってはいるものの(いや、別にそういうつもりでやったわけでもないですが)色々と難すぃー。海外へも少しは知名度伸ばせたようなのだけど、そこでも基本的には.NET圏のみって雰囲気で、どうしたものかしらん。
つまるところ、そろそろ御託はどうでもいいから、RealWorldな実例出せよって話ですね!
linq.js ver.2.2.0.0 - 配列最適化, QUnitテスト, RxJSバインディング
- JavaScript Rx linq.js - 10.06/29
CodePlex - linq.js - LINQ for JavaScript
linq.jsをver 2.2に更新しました。変更事項は、メソッドの追加、配列ラッピング時の動作最適化、ユニットテストのQUnitへの移行、RxJSバインディング追加の4つです(あと、若干のバグフィックスと、RxJS用vsdoc生成プログラムの同梱)。まずは、追加した二つのメソッドについて。
var seq = Enumerable.From([1, 5, 10, 4, 3, 2, 99]); // TakeFromLastは末尾からn個の値を取得する var r1 = seq.TakeFromLast(3).ToArray(); // [3, 2, 99] // 2.0から追加されているTakeExceptLast(末尾からn個を除く)と対になっています var r2 = seq.TakeExceptLast(3).ToArray(); // [1, 5, 10, 4] // ToJSONはjson文字列化します(列挙をJSON化なので必ず配列の形になります) // JSON.stringifyによるJSON化のため、 // ネイティブJSON対応ブラウザ(IE8以降, Firefox, Chrome, Opera...) // もしくはjson2.jsをインポートしていないと動作しません var objs = [{ hoge: "huga" }, { tako: 3}]; var json = Enumerable.From(objs).ToJSON(); // [{"hoge":"huga"},{"tako":3}]
TakeFromLast/TakeExceptLastはRxからの移植です(Rxについては後でまた少し書きます)。RxではTakeLast, SkipLastという名前ですが、諸般の都合により名前は異なります。より説明的なので悪くはないかな、と。
もう一つはToJSONの復活。ver 1.xにはあったのですが、2.xでばっさり削ってたました。復活といっても、実装は大きく違います。1.xでは自前でシリアライズしていたのですが、今回はJSON.stringifyに丸投げしています。と、いうのも、IE8やそれ以外のブラウザはJSONのネイティブ実装があるので、それに投げた方が速いし安全。ネイティブ実装はjson2.jsと互換性があるので、IE6とかネイティブ実装に対応していないブラウザに対しては、json2.jsを読み込んでおくことでToJSONは動作します。
json2.jsはネイティブ実装がある場合は上書きせずネイティブ実装を優先するようになっているので、JSON使う場合は何も考えずとりあえず読み込んでおくといいですね。
配列ラップ時の最適化
Any, Count, ElementAt, ElementAtOrDefault, First, FirstOrDefault, Last, LastOrDefault, Skip, SequenceEqual, TakeExceptLast, TakeFromLast, Reverse, ToString。
Enumerable.From(array)の直後に、以上のメソッドを呼んだ際は最適化された挙動を取るように変更しました。各メソッドに共通するのは、lengthが使えるメソッドということです。Linqは基本的に長さの情報を持っていない(無限リストとか扱えるから)ため、例えばCountだったら最後まで列挙して長さを取っていました。しかし、lengthが分かっているのならば、Countはlengthに置き換えられるし、Reverseは[length - 1]から逆順に列挙すればいい。ElementAt(n)はまんま[n]だしLastは[length - 1]だし、などなど、lengthを使うことで計算量が大幅に低減されます。
C#でも同様のことをやっている(ということは以前にLinqとCountの効率という記事で書いてあったりはする)のですが、今になってようやく再現。先の記事にあるように、C#では中でisやasを使ってIEnumerableの型を調べて分岐させてますが、linq.jsでは Enumerable.FromでEnumerableを生成する際に、今まではEnumerableを返していたところを、ArrayEnumerable(継承して配列用にメソッドをオーバーライドしたもの)を返す、という形を取っています。
これが嬉しいかどうかというと、そこまで気にするほどではありません。C#では array.Last() のように使えますが、linq.jsではわざわざ Enumerable.From(array).Last() と、ラップしなきゃいけませんから、それならarray[array.length - 1]でいいよ、という。ちなみに当然ですがFrom(array).Where().Count()とか、他のLinqメソッドを挟むと、ArrayEnumerableじゃなくEnumerableになるため最適化的なものは消滅します。
でもまあ、意味はあるといえばあります。配列を包んだだけのEnumerableは割と色々なところで出てきます。例えばGroupJoin。これのresultSelectorの引数のEnumerableは、配列をラップしただけです。又は、ToLookup。Lookupを生成後、Getで取得した際の戻り値のEnumerableは配列を包んだだけです。GroupByの列挙(Grouping)もそう。特にGroupingで、グループの個数を使うってシーンは多いように思います。そこで今まではCount()で全件列挙が廻っていたのが、一度も列挙せずに値が取れるというのは精神衛生上喜ばしい。
パフォーマンス?
このArrayへの最適化は勿論パフォーマンスのためなのですが、じゃあ全体的にlinq.jsのパフォーマンスはどうなの?というと、遅いよ!少し列挙するだけで山のように関数呼び出しが間に入りますから、速そうな要素が一つもない。ただ、遅さがクリティカルに影響するほどのものかは、場合によりけりなので分かりません。遅い遅いと言っても、jQueryでセレクタ使って抽出してDOM弄りするのとどちらが重いかといったら、(データ量にもよりますが)圧倒的にDOM弄りですよね?的な。
JavaScriptは、どうでもいいようなレベルの高速化記事がはてブなんかにも良く上がってくるんですが、つまらない目先に囚われず、全体を見てボトルネックをしっかり掴んでそこを直すべきだと思うんですよね。「1万回の要素追加で9msの高速化」とか、意味無いだろそれ絶対と思うのですが……。
ただ、アプリケーションとライブラリだと話は別で、ライブラリならば1msでも速いにこしたことはないのは事実です。linq.jsは仕組み的には遅いの確定なのはしょうがないとしても、もう少しぐらいは、速度に気を使って努力すべきな気はとてもします。今後の課題。
コードスニペット
無名関数書くのに、毎回function(x) { return って書くの、面倒くさいですよね。ということで、Visual Studio用のコードスニペットを同梱しました。func1->Tab->Tabで、linq.jsで頻繁に使う一行の無名関数 function(x){ return /* キャレットここ */ } を生成してくれます。これは激しく便利で、C#の快適さの3割ぐらいを占めていると言っても過言ではないぐらいに便利なのですが、動画じゃないと伝わらないー、けれど動画撮ってる体力的余裕がないので省略。
func0, func1, func2, action0, action1, action2を定義しています。0だの1だのは引数の数。funcはreturn付き、actionはreturn無しのスニペットです。また、Enumerable.RangeとEnumerable.Fromにもスニペットを用意しました。erange, efromで展開されます。jQueryプラグイン版の場合はjqrange, jqfromになります。
インストールは、Visual Studio 2010でツール→コードスニペットマネージャーを開いてインポートでsnipetts/.snippetを全部インポート。
binding for RxJS
RxJS -Reactive Extensions for JavaScriptと接続出来るようになりました。ToObservableとToEnumerableです(jQuery版のTojQueryとtoEnumerableと同じ感覚)。
// enumerable sequence to observable var source = Enumerable.Range(1, 10) .Shuffle() .ToObservable() .Publish(); source.Where(function (x) { return x % 2 == 0 }) .Subscribe(function (x) { document.writeln("Even:" + x + "<br>") }); source.Where(function (x) { return x % 2 != 0 }) .Subscribe(function (x) { document.writeln("Odd:" + x + "<br>") }); source.Connect(); // observable to enumerable var subject = new Rx.ReplaySubject(); subject.OnNext("I"); subject.OnNext(4); subject.OnNext("B"); subject.OnNext(2); subject.OnNext("M"); var result = subject.ToEnumerable() .OfType(String) .Select(function (x) { return x.charCodeAt() - 1 }) .Select(function (x) { return String.fromCharCode(x) }) .ToString("-"); alert(result); // H-A-L
ToObservableでは、こないだ例に出したPublishによる分配を。ToEnumerableは、例が全然浮かばなかったので適当にOnNextを発火させた奴をEnumerable化出来ますねー、と。なお、cold限定です。hotに対して適用すると空シーケンスが返ってくるだけです(ちなみにC#版のRxでhotに対してToEnumerableするとスレッドをロックして無限待機になる)
それと、RxVSDocGeneratorの最新版を同梱してあります。以前に公開していたのは、RxJSのバージョンアップと同時に動かなくなっちゃってたのよね。というわけで、修正したうえで同梱商法してみました。
プレースホルダの拡張
無名関数のプレースホルダが少し拡張されました。今までは引数が一つの時のみ$が使えたのですが、今回から二引数目は$$、三引数目は$$$、四引数目は$$$$が使えるようになりました。
// 連続した重複がある場合最初の値だけを取る // (RxのDistinctUntilChangedをlinq.jsでやる場合) // 1, 5, 4, 3, 4 Enumerable.From([1, 1, 5, 4, 4, 3, 4, 4]) .PartitionBy("", "", "key,group=>group.First()") // $$でニ引数目も指定出来るようになった Enumerable.From([1, 1, 5, 4, 4, 3, 4, 4]) .PartitionBy("", "", "$$.First()")
便利といえば便利ですが、あまりやりすぎると見た目がヤバくなるので適度に抑えながらでどうぞ。なお、以前からある機能ですが”"は”x=>x”の省略形です。PartitionByでは、それぞれkeySelectorとelementSelector。
入門QUnit
今までlinq.jsのユニットテストはJSUnitを使用していたんですが、相当使いにくくてやってられなかったため、QUnitに移しました。QUnitはjQueryの作者、John Resigの作成したテストフレームワークで、流石としか言いようがない出来です。物凄く書きやすい。JSUnitだとテストが書きづらくて、だから苦痛でしかなかった。テストドリブンとか言うなら、まずはテストが書きやすい環境じゃないとダメだ。
JSUnitのダメな点―― 導入が非常に面倒。大量のファイルを抱えたテスト実行環境が必要だし、クエリストリングでファイル名を渡さなければならなかったり、しかも素ではFirefox3で動かなかったりと(Firefox側のオプションを調整)下準備が大変。面倒くささには面倒くささなりのメリット(Java系の開発環境との連携とかあるらしいけど知らない)があるようですが、俺はただテスト書いて実行したいだけなんだよ!というには些か重たすぎる。一方、QUnitはCSSとJSとHTMLだけで済む。
また、JSUnitはアサーションのメソッドが微妙。大量にあるんだけど、逆に何が何だか分からない。assertObjectEqualsとかassertArrayEqualsとか。ArrayEqualsはオブジェクトの配列を値比較してくれない上に、それならせめて失敗してくれればいいものの成功として出されるから役に立たなかったり、ね……。QUnitは基本、equal(参照比較)とdeepEqual(値比較)だけという分かりやすさ。deepEqualはしっかりオブジェクト/配列をバラして再帰的に比較してくれるという信頼感があります。
テスト結果画面の分かりやすさもQUnitに軍配が上がる。というかJSUnitは致命的に分かりづらい。一つのテスト関数の中に複数のアサートを入れると、どれが失敗したか分からないという有様。なのでJSUnitではtestHoge1, testHoge2といった形にせざるを得ないのだけど、大変面倒。更に、JSUnitのテスト実行は遅くて数百件あるとイライラする。
そもそもJSUnitはコードベースが古いし最近更新されてるかも微妙(GitHubに移って開発は進んでるようですが)。というわけで今からJavaScriptでユニットテストやるならQUnitがいいよ!残念ながらか、ネットを見ると古い紹介記事ばかりが見当たるので、ていうかオフィシャルのドキュメントまで古かったりしてアレゲなので、簡単に解説します。と、思ってたのですが、一月程前に素晴らしいQUnitの記事が出ていました。なので基本無用なのですが、文章を書いてしまってあったので(これ書いてたのは4月頃なのです)出します、とほほ。
まず、QUnit - jQuery JavaScript LibraryのUsing QUnitのところにあるqunit.jsとqunit.cssを落とし、下記のテンプレHTMLは自前で作る。ファイル名はなんでもいいんですが、私はtestrunner.htmとでもしておきました。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>linq.js test</title> <link href="qunit.css" rel="stylesheet" type="text/css" /> <script src="qunit.js" type="text/javascript"></script> <!-- テストに必要な外部ライブラリは好きに読み込む --> <script src="linq.js" type="text/javascript"></script> <!-- ここにテスト直書きもアリだし --> <script type="text/javascript"> test("Range", function() { deepEqual(Enumerable.Range(1, 3).ToArray(), [1, 2, 3]); }); // 自動的にロード後に実行されるので、これも問題なく動く test("hoge", function () { var h2 = document.getElementsByTagName("h2"); equal(h2.length, 2); }); </script> <!-- 外部jsファイルにして読み込むのもアリ --> <script src="testEnumerable.js" type="text/javascript"></script> <script src="testProjection.js" type="text/javascript"></script> </head> <body> <h1 id="qunit-header">linq.js test</h1> <h2 id="qunit-banner"></h2> <h2 id="qunit-userAgent"></h2> <ol id="qunit-tests"></ol> </body> </html>
実行用のHTMLにqunit.jsとqunit.cssを読み込み、body以下の4行を記述すれば準備は完了(bodyの4行はid決め打ちで面倒だし、どうせ空なので、qunit.js側で動的に生成してくれてもいいような気がする、というか昔はそうだった気がするけどjQuery依存をなくした際になくしたのかしらん)。
あとは、test(”テスト名”, 実行される関数) を書いていけばいいだけ。そうそう、test関数はHTMLが全てロードされてから実行が始まるので、jQuery読み込んでjQuery.readyで囲む必要とかは特にありません。testProjection.jsは大体↓のような感じ。
/// <reference path="testrunner.htm"/> module("Projection"); test("Select", function () { actual = Enumerable.Range(1, 10).Select("i=>i*10").ToArray(); deepEqual(actual, [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]); actual = Enumerable.Range(1, 10).Select("i,index=>i*10+index").ToArray(); deepEqual(actual,[10, 21, 32, 43, 54, 65, 76, 87, 98, 109]); });
reference pathはVisualStudio用のパスなのであんま気にせずにー。詳細はJavaScriptエディタとしてのVisual Studioの使い方入門のほうで。読み込み元のHTMLを指定しておくとIntelliSenseが効いて、書くのが楽になります。
actual(実行結果)は私は別変数で受けてますが、当然、直書きでも構いません。アサーション関数は、基本は(actual, expected(期待する結果), message(省略可能)) の順番になっています。
equal(1, "1"); // okay - 参照比較(==) notEqual strictEqual(1, "1"); // failed - 厳密な比較(===) notStrictEqual deepEqual([1], [1]); // okay - 値比較 notDeepEqual ok(1 == "1"); // okay - boolean ok(1 !== "1"); // okay - notの場合は!で
基本的に使う関数はこれらだけです。ドキュメントへの記載はないのですが、以前にあったequalsとsameはequalとdeepEqualに置き換わっています。後方互換性のためにequals/sameは残っていますが、notと対称が取れるという点で、equal/deepEqualを使ったほうが良いんじゃないかと思います。
非同期テストとかは、またそのうちに。
まとめ
linq.js ver.2出したときには、もう当分更新することなんてないよなあ、なんて思っていたのですが、普通にポコポコと見つかったり。でもさすがに、もうないと思いたい。C#との挙動互換性も、私の知る限りでは今回の配列最適化が最後で、やり残しはない。そして今回がラストだー、とばかりに思いつく要素を全部突っ込んでやりました。
そんなわけなので、使ってやってください。私がVisualStudio使いなのでVS関連の補助が多めですが、別にVS必須というわけじゃなくプラスアルファ的なもの(入力補完ドキュメントだのコードスニペットだの)でしかないので、エディタ書きでも何ら問題なく使える、かな、きっと。
JavaScriptエディタとしてのVisual Studioの使い方入門
- JavaScript linq.js - 10.05/24
linq.jsってデバッグしにくいかも……。いや、やり方が分かればむしろやりやすいぐらい。という解説を動画で。HDなので文字が見えない場合はフルスクリーンなどなどでどうぞ。中身の見えないEnumerableは、デバッガで止めてウォッチウィンドウでToArrayすれば見えます。ウォッチウィンドウ内でメソッドチェーンを繋げて表示出来るというのは、ループが抽象化されているLinqならではの利点。sortしようが何しようが、immutableなので元シーケンスに影響を与えません。ラムダ式もどきでインタラクティブに条件を変えて確認出来たりするのも楽ちん。
ところで、JavaScript開発でもIDE無しは考えられません。デバッグというだけならFirebugもアリではありますが、入力補完や整形が可能な高機能エディタと密接に結びついている、という点でIDEに軍配があがるんじゃないかと私は思っています。動画中ではVisual Studioの無料版、Visual Web Developerを使っています。Visual Studioというと、何か敷居が高く感じられるかもしれませんが、使う部分を絞ってみれば、超高性能なHTML/JavaScriptエディタとして使えちゃいます。有料版の最高級エディションは170万円ですからね(MSDNという何でも使えるライセンスがセットなので比較は不公平ですが)、機能限定版とはいえ、その実力は推して知るべし、です(機能限定部分は、主にC#でのASP.NET開発部分に絡むものなのでJavaScript周りでは全く関係ありません)。
VSを使うと何が嬉しいのでしょう?JavaScriptでの強力な入力補完、自動整形、使いやすいデバッガ、リアルタイムエラー通知。そしてこっそり地味に大切なことですが、jQueryの完璧な日本語ドキュメント付き入力補完が同梱されています。と、嬉しいことはいっぱいあるのですが、ASP.NETの開発用ではあるので、JS開発には不要なメニューが多くて戸惑う部分も多いのは事実。分かれば不要部分はスルーするだけなので簡単なのですが、そこまでが大変かもしれない。なので、JavaScript開発で使うVisualStudio、という観点に絞って、何が必要で不要なのかを解説していきます。
インストール
何はともあれまずはインストール。Microsoft Visual Studio ExpressからVisual Web Developerを選び、リンク先のWeb Platform Installerとかいうのをダウンロード&実行。
PHPとかWordPressとか色々ありますがどうでもいいので、Visual Web Developer 2010 Expressだけ入れましょう。クリックして指示に従って適当に待つだけ、10分ぐらいあれば終わるはず。10分は短くはないですが、インストール自体は非常に簡単です。
プロジェクト作成
実行すると初回起動時はイニシャライズが若干長いですが、それを超えれば新しいプロジェクトと新しいWebサイトの違いが分からねえええええ。で、ここは新しいWebサイトです。プロジェクトのほうはC#でASP.NETが基本なので関係ありません。スタートページから、もしくはファイル→新規作成→Webサイト。
更に項目があって分からねえ、けどここはASP.NET空のウェブサイトを選びます。次にソリューションエクスプローラーウィンドウを見ます(なければ表示→ソリューションエクスプローラー)。web.configとかいうゴミがありますが、それはスルーしておきましょう(消してもいいですが復活します)。空なので、ルートを右クリックして新しい項目の追加。
いっぱいあると思いますが、ほとんど関係ありません、ノイズです。真ん中ぐらいにあるHTMLページかJScriptファイルを選びましょう。あとは、エディタでガリガリと書いたら、Ctrl+F5を押せば簡易サーバーが立ち上がり、ブラウザ上に現在編集中のHTMLが表示されます。
以上が基本です。手順は簡単なので一度覚えればすんなり行くはずです。最初は如何せんHTML/JS用としてはダミー項目が多いのがやや難点。なお、保存時はデフォルトではMy DocumentのVS2010のWebSites下にHTMLとかが、Projects下に.slnファイル(プロジェクトを束ねている設定とかが書かれたファイル)が置かれています。以後プロジェクトをVSで開くときは.slnのほうをダブルクリック、もしくはスタートページの最近使ったプロジェクトから。
では、Visual Studioを使ってJavaScriptを書いて嬉しい!機能を幾つか挙げていきます。
エラー表示
小括弧が、波括弧が、足らなかったり足しすぎだったりを見落とすことは割とあります。そして起こる実行時エラー。こんなのコンパイルエラーで弾かれてくれ、あばばばば。と思うときはいっぱいあります。そこでVisual Studioのリアルタイムエラー検出。
hoge = functionではなくhoge : function。下のは波括弧が一個多い。というのを、リアルタイムで検出してくれて、疑わしいところには波線を敷いてくれます。エラー一覧にも表示されるので、このウィンドウは常時表示させておくと書くのが楽になります。私は縦置きにしてエディタの左側にサイドバーとして常時表示。カラムはカテゴリと説明だけにしています。
エラー通知のためのコード走査はバックグラウンドで定期的に動いているようですが、任意に発動させたい場合はCtrl + Shift + Jで行えます。修正結果が正しいのかとっとと確認したいんだよ馬鹿やろー、って時に便利。というか普通に押しまくります、私は。
コードフォーマット
コード整形は大事な機能だと思っています。手動でスペース入れていくとか面倒くさいし。かといって整形が汚いコードは萎えます。
ショートカットはCtrl+K、で、Ctrlを押しながら続けてD。微妙に覚えにくいショートカット。ちなみに選択範囲のコメント化はCtrl+K, Cで、非コメント化はCtrl+K, U。ようするに整形系はCtrl+K始まりで、DはDocumentFormat、CはComment、UはUncommentの意味になるようです。フォーマットのルール(改行をどこに入れるか、とか)は設定で変えられます。
デバッグ
当然のようにブレークポイントの設定、ステップイン、ステップアウトなどのデバッグをサポートしています。
F9でブレークポイントを設定してF5でデバッグ実行。が基本です。ローカルウィンドウで変数の値表示、そして便利なのがウォッチウィンドウで、見たい値を好きに記述出来ます。式も書けるので平気で副作用かませます。で、デバッガで良いのはthisが見れるところですねー。JavaScriptはthisが不定で、いったいこの中のthisは何を指しているんだ!と悩んでしまうわけですが、そんなものデバッガで見れば一発で分かりますね、はは。考えるより前にとりあえずデバッグ実行。
さて、そんなデバッグですが、初回時には何やら怪しげなダイアログが上がります。ここはYESで。そして、デバッグ出来ましたか?出来なかった人も多いかもしれません。実は、IEじゃないとデバッガ動かないのです。というわけで、ソリューションエクスプローラーからプロジェクトのルート部分を右クリックしてブラウザの選択を選ぶ。
IEをデフォルトにしてください。一度設定すれば、以降はこの設定が継続されます。IEとか冗談じゃない。と思うかもしれませんが、えーと、IEで常に書くことで、IEで動かないスクリプトを書くことを避けられるのです、とかいうどうでもいい効用はあります。でもまあ、Firefox拡張とかChrome拡張を書くのにはデバッガが使えなくなるも同然なのは不便ですね。その時はデバッグは当然ブラウザ固有のデバッガを使い(デバッガを使わないと言う選択肢はないよ!)、エディタとしてだけに使えばいいぢゃない。
入力補完/日本語jQuery
入力補完(IntelliSense)は素敵。ローカル変数があればそれが出てくる。もう変数名打ち間違えで動かない、とかない。ドットを打てば、補完候補に文字列であればreplaceとか、配列であればjoinとか、DOMであればappendChildとか出てくる。メソッド名を暗記する必要もなければ、打ち間違えることもない。
補完は割と賢くて、関数では引数を見て(というか裏でインタプリタ走ってるんですね、きっと)、ちゃんと返す値を判別してくれます。
ところでですが、最初の釣り画像にあるjQueryの日本語化ドキュメントはどこにあるのでしょうか?
ファイル→新規作成→プロジェクトからASP.NET Webアプリケーションを選びます。すると、Scriptsフォルダの下にjquery-1.4.1-vsdoc.jsとかいうものが!こいつを、コピペって頂いてしまいましょう。ASP.NET Web Application自体はどうでもいいので破棄です、破棄。でもせっかくなので、Default.aspxを開いてCtrl+F5で実行してみてください。出来た!ウェブアプリが出来た!そう、C#+ASP.NETは驚くほど簡単にウェブアプリが作れるんです。あとは安レンタルサーバーさえ普及してくれれば……。
vsdocについて
-vsdoc自体は<script src>で読み込む必要はありませんし、実際にサーバーにアップロードする必要もありません。仕組みとしてはhoge.jsとhoge-vsdoc.jsが同じ階層にあると、VisualStudioの入力補完解析はhoge-vsdoc.jsを見に行く、といった感じになっています。なので、jquery-1.4.1.jsだけを読み込めばOKです。
HTMLファイルに記述する場合はscript srcで読み込めて補完が効くのは分かるけど、単独JSファイルの場合は読み込みの依存関係をどう指定すればよいでしょうか。答えは、ファイルの先頭にreference pathを記載します。
これで、JScript1.jsという単独JSファイルでもjQueryの補完が効かせられるようになりました。reference pathというのはVSだけで効果のあるタグで、ブラウザの解釈上はコメントに過ぎないので、ブラウザ表示時に問題が出ることもありません。
なお、このreference pathというのを覚えている必要はありません。refと記述してTabを二回押すとこのタグが展開されるはずです。コードスニペットというコード挿入の仕組みに予め用意されているわけです。なお、コードスニペットは、この他にもfor->Tab x2でforが展開されたりなど色々あって便利です(自分で作成することも出来る)。
その他設定など
その他、好みもありますが設定など。ツール→オプションから。
何はともかくフォントの変更。MSゴシックとかありえん。フォントをConsolasにしましょう! Consolasはプログラミング用のClearTypeに最適化された見やすい素敵フォントです。勿論、スラッシュドゼロ。サイズは私は9で使ってます。
Ctrl+F5押す度にアウトプットウィンドウが立ち上がるのが猛烈にウザいので、「ビルド開始時に出力ウィンドウを表示」のチェックは外しておく。
HTMLでの属性の引用符自動挿入はチェックつけといたほうが幸せ気分。
入力候補の、このTabかEnterのみで確定させるってのはチェックを外す。だってメソッド書くときは「(」で確定させたいし、オブジェクトを開くときは「.」で確定させたいもの。例えばdocument.getElementByIdは「doc -> Enter -> . -> get -> Enter -> (」じゃなくて「doc -> . -> get -> (」というように、スムーズに入力したい。一々Enterを挟むのは流れを止めてしまう。
まとめ
IDEを知ってて使わない、というのは個人の好き好きなのですが、単純に知らないというのは勿体無いな、と。特に初心者ほどIDEを必要とすると思います。初心者がプログラミング始めるなら、導入がメモ帳とブラウザだけで開発出来るJavaScriptお薦め!って台詞は、あまりよろしくないんじゃないかなー。初心者ほど些細なスペルミスや構文ミスでつまづく上に、目を皿のようにしてみても原因が分からない。たとえ導入までの敷居が若干高くなろうとも、親切にエラー箇所に波線を敷いてくれるIDEこそ必要なんじゃないかな。あと、デバッガ。ビジュアルに変数が動き変わることほど分かりやすいものもないでしょう。
IDEもEclipseのプラグインとか色々ありますが、Visual Studioの強力なjQuery対応度は何にも代え難いんじゃないでしょうか。導入もオールインワンなので何も考えなくてもいい簡単さですし。是非一度、試してみてもらえればいいなあ。
ついでですが、冒頭動画のlinq.jsは便利なJavaScriptライブラリ(無名関数を多用して関数型言語的にコレクション操作を可能にする)でいて、更にVisual Studioの入力補完に最適化してあるので使ってみてください、と宣伝。いや、作者私なので。ごほごほ。jQueryプラグインとして動作するバージョンも同梱してあります。
それと、勿論Visual Studioは有料版のほうが高機能な面もあります。JavaScript開発のみだとあまり差はないのですが、WindowsScriptHostをJavaScriptで書いてもデバッグ出来るとか無料版に比べて大したことない利点があるにはあります。C#でSilverlightなどもごりごり書きたい、とかになれば断然、有料版のほうが輝いてきます。
Ultimateは100万オーバーで無理なので、Professional買いましょう、私は買います。(メインはC#の人間なので。JSの人は正直Expressでイイと思うよ……)。まだ発売されてないのでこれから買います。「アップグレード」ですが、Express(無料版)からのアップグレードも認められているという意味不明仕様なので(誰が倍額する通常版買うんでしょうかね……)皆様も是非、上のリンクからamazonで買ってくれれば、ごほごほ。