AutoHotKeyでウィンドウのタイル配置
- 2009-06-20
以前にも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 フルスクレイピングに変更
- 2009-06-18
Xbox.comのサイトがリニューアルされましたね!フレンドのフレンド表示機能のお陰で、ついに念願かなって自分自身のステータス取得に成功。野良APIを使わずに全てスクレイピングで実装することができました。よって、今回からは100%取りこぼしなく、ステータスの変更も瞬時に反映されます。他に変更点としては日付が投稿時の時間になりました(Xbox.com上からは時間を取れるものがないので) あと、書式に実績の数を追加しました。"%TitleTotalAchievementCount%:タイトルの最大実績数", "%TitleAchievementCount%:取得した実績数" です。この変のよくわからないうえに長ったらしい名前はとても後悔しています……。
バグがあったら教えてください。どう見てもテスト不足なのでサクッと落ちるかもしれません。(言ってるそばから不具合発見しました。おまけに、zipファイルに格納するファイルが足りなかったので新規にダウンロードした場合は動かなかったっぽい。酷すぎる。というわけでver 1.2.0.1になりました。)
linq.js ver 1.3.0.2 - CascadeDepthFirst/BreadthFirst
- 2009-06-15
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(モドき)
- 2009-06-10
どうも、新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(言語?)
- 2009-06-06
本題、に入る前に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は初回リリース時にチュートリアルめいたものが書いてあるので、よければどうぞ、というかお願いしますお願いします。
DOMの関数型構築
- 2009-05-29
Linq to XMLのJavaScript移植、もポチポチと進めています。
var xml = new XElement("customer", new XAttribute("id", "c001"),
new XElement("firstName", "Paolo"),
new XElement("lastName", "Pialorsi"),
new XElement("addresses",
new XElement("address", new XAttribute("type", "email"), "paolo@devleap.it"),
new XElement("address", new XAttribute("type", "url"), "http://www.devleap.it/"),
new XElement("address", new XAttribute("type", "home"), "Brescia - Italy")
)
);
alert(xml.Source.outerHTML); // .Sourceは気にしないでください(最終的には隠蔽されるので)
これは何と、C#にコピペしても動く。つまり互換性100%(笑) 所謂マークアップビルダになります。私がC#っ子だから、というのもあるけれど、JSON風に書くより好き。JSON風だとそれぞれのライブラリが属性対応のため変則的な書き方ルールが用いられていて、それを覚えなきゃいけないけれど、これなら要素に属性と値を突っ込むだけなので学習コストがほとんどない。と、思う。IntelliSenseのサポートも効くし(現在linq.jsをIntelliSenseが効くよう整備中です、土日に書きすすめて公開、したいなあ)。そのかわりnewだらけでちょっと冗長に過ぎるところはあるかもしれない。
// ユーザー名と発言と投稿を持ったクラス
var Twit = function(user, text)
{
this.user = user;
this.text = text;
}
// とりあえずサンプルデータ
var data1 = new Twit("ika", "いかはいかが");
var data2 = new Twit("tako", "たこやき");
var data3 = new Twit("sakana", "丸焼き");
// XmlHttpほげほげだのJSONだのな結果の例として配列
var list = [data1, data2, data3];
// リストをSelectでXElementに射影すると自動的に展開される
var xml = new XElement("twitter",
new XElement("account", "neuecc"),
new XElement("twits", E.From(list).Select(function(t)
{
return new XElement("twit", new XAttribute("user", t.user), t.text)
}))
);
alert(xml.Source.outerHTML); // .Sourceは気にしないでください(最終的には隠蔽されるので)
例をもう一つ。例が適当かつ分かり辛くてすみませんなのですが、Selectで射影しておくと、展開されます。何らかのオブジェクトを繰り返してDOMにする、というシーンは多いと思うのですが、forはもう必要ありません。むしろfor禁止。ループは悪。「この配列を」「この形に展開する」と宣言的に記述するだけで良いのです。
var table = new XElement("table",
new XElement("tr",
new XElement("td", "column1"),
new XElement("td", "column2"),
new XElement("td", "column3")
),
E.Repeat("tr", 5).Select(function(tagName)
{
return new XElement(tagName,
E.Range(1, 3).Select(function(i)
{
return new XElement("td",
new XAttribute("style", (i % 2 == 0) ? "background:red" : "background:green"),
"row" + i);
})
)
})
);
// FirefoxではouterHTML動きませんけど、最終的には隠蔽されるので(ry
document.body.innerHTML = table.Source.outerHTML;
簡単なTableの生成例。これrowになってませんね、あはは。まあ、いいや。関数の引数の中に式を書くという感覚が最初はキモかったのですが、今では平然と書くようになってしまいました。何というLinq中毒……。しかし、JavaScriptだとfunction(){return }のせいでゴチャゴチャして分かり難いものになってしまう。ラムダ式欲しいなあ。{とかreturnとかいらない。Firefox3では省略出来るけど、Chromeはまだ出来ないみたい……。
Firefox3は使えば使うほど嫌いになるという素敵ブラウザなので、本気でChromeに乗り換える気はありますよ!アドオンがなければ自分で作ればいいじゃない、と思うことにしますので、作りたいですね。一応Hello,Worldは書いた。
<script type="text/javascript" src="linq.js"></script>
<script type="text/javascript" src="linq.xml.js"></script>
<script type="text/javascript">
// Initialize
X.Initialize(function()
{
X.ID("panel").AttachEvent("click", panel_onclick);
});
// EventHandler
function panel_onclick(sender, event)
{
X.ID("titleLabel").SetValue(event.screenX);
}
</script>
<div id="panel" class="toolstrip-button">
<span id="titleLabel">Hello, World!</span>
</div>
ええ、ただのHTMLです。ステータスバーのところにHello,Worldが出てクリックできるという、それだけのもの。Chromeの拡張はまだ何が出来るか分からないというか何も出来ないというか、それグリモンでいいよね、的なことしか出来ない感じなので、その間、今のうちにlinq.xml.jsを完成させよう。何かもう意地でも自分は使う、な方向に傾いてる気がしますが。jQuery?何それ。
JavaScriptで総当たり
- 2009-05-27
Baker, Cooper, Fletcher, MillerとSmithは五階建てアパートの異なる階に住んでいる。Bakerは最上階に住むのではない。Cooperは最下階に住むのではない。Fletcherは最上階にも最下階にも住むのではない。MillerはCooperより上の階に住んでいる。SmithはFletcherの隣の階に住むのではない。FletcherはCooperの隣の階に住むのではない。それぞれはどの階に住んでいるか。
// 条件を並べて総当たり問題を解く
var apart = E.Range(1, 5);
var answers = apart
.SelectMany(function(baker){ return apart
.SelectMany(function(cooper){ return apart
.SelectMany(function(fletcher){ return apart
.SelectMany(function(miller){ return apart
.Select(function(smith){ return {
baker: baker, cooper: cooper, fletcher: fletcher, miller: miller, smith: smith}})})})})})
.Where("E.From($).Distinct('$.Value').Count() == 5")
.Where("$.baker != 5")
.Where("$.cooper != 1")
.Where("$.fletcher != 1 && $.fletcher != 5")
.Where("$.miller > $.cooper")
.Where("Math.abs($.smith - $.fletcher) != 1")
.Where("Math.abs($.fletcher - $.cooper) != 1");
// 動作確認、Templateのお陰で記述がとても楽
answers.ForEach(function(obj)
{
var str = Linq.Tools.Template("baker : {baker},\
cooper :{cooper},\
fletcher :{fletcher},\
miller : {miller},\
smith : {smith}",
obj);
alert(str); // とりあえずalertで確認
});
メソッド構文の最大の敵、多重fromを何とかする。今回の場合はカッコを閉じず、最後にSelectを使えばクエリ構文に近い見た目が確保できる。大量の閉じ括弧と、大量のWhereに対してラムダ式が面倒くさくなるのだけはどうにもならないのだけれど。C#で一通り書いてから、JavaScript + linq.jsに書き直したのが上記のもの。C#のは載せませんけれど、ほとんど一緒です。
Distinctの部分だけはlinq.jsのほうがC#より書きやすい。匿名型を突っ込んでもハッシュなので、そのままセレクター渡して比較出来ちゃうので。あとWhereラッシュに対して$がGroovyのitのようなデフォルトのイテレータ引数として機能するので記述が楽なのは利点といえば利点。itのようなものはC#でも欲しいなあ、と思ってたりはする。
パフォーマンス?気にしたら負け、かな……。Chromeなら十分な速度で動くし。理想ではJavaScriptな人にもC#やLINQの良さが伝えられればな!なわけなのですが、現実は非常に厳しく誰もDLしてないよねですよね的ではあるけれど。うーん。
まあ、とりあえず、JavaScript的に特異なコードが書けるのでお遊びにでも使ってもらえると嬉しいです。
linq.js ver 1.3.0.0 - Unfold, Matches, etc...
- 2009-05-24
今回は本体も含めて大量更新です。生成子にUnfoldとMatchesを追加しました。Unfoldは応用範囲がとても広く(広すぎて逆に使い道が思いつかない)、Matchesは正規表現がより使いやすくなるので、実用度が非常に高いと思われます。そして、FromにStringを入れたときの動作を一文字毎に分解するよう変更。更に操作用メソッドにも、Insert, IndexOf, LastIndexOfを追加しました。あと、その他色々。
Unfold
UnfoldはAggregateの反対、といっても良くわからないのですが、引数と戻り値の型が同一の関数を無限に適用し続ける、という感じです。例えばE.Unfold(0, "$+3")は、初期値が0で、適用する関数が+3。というわけで、0,3,6,9....と無限に3ずつ増幅していく値が取り出せます。これだけでは終了しないので、終了条件はTakeかTakeWhileで別に与えます。
// フィボナッチ数列の無限リスト
var fib = E.Unfold({ a: 1, b: 1 }, "{a:$.b, b:$.a + $.b}").Select("$.a");
// 10個分だけ画面に出力 - 1,1,2,3,5,8,13,21,34,55
fib.Take(10).WriteLine();
// abcdefを一文字ずつ削っていく- abcdef,bcdef,cdef,def,ef,f
E.Unfold("abcdef", "$.substr(1)").TakeWhile("$.length>0").WriteLine();
例はフィボナッチ数列で、これは熟練した C# 使いは再帰を書かない? - NyaRuRuの日記の丸コピです。タプルの片方を計算用の一時領域として使う、という感じでしょうか?
static void Main(string[] args)
{
// 16進の文字列をByte配列に変換する
var str16 = "FF-04-F2 B3 05 16F3";
var regex = new Regex(@"[abcdef\d]{2}", RegexOptions.IgnoreCase);
// 全部マッチさせてから変換
var byteArray1 = regex.Matches(str16)
.Cast<Match>()
.Select(m => Convert.ToByte(m.Value, 16));
// Unfold使って一つずつ変換
var byteArray2 = Unfold(regex.Match(str16), m => m.NextMatch())
.TakeWhile(m => m.Success)
.Select(m => Convert.ToByte(m.Value, 16));
}
static IEnumerable<T> Unfold<T>(T seed, Func<T, T> func)
{
while (true)
{
yield return seed;
seed = func(seed);
}
}
UnfoldをC#で正規表現のマッチに使ってみた。あまり意味はない。これをJavaScriptでやると、マッチオブジェクトにNextMatch()がないので、lastIndexの変化したRegExpにexec()し続ける必要がある。というわけで、Timesを使えば同じことができます。E.Times("regex.exec(input)").TakeWhile("$ != null") です。String.match(オプションはglobal)を使って配列を取得したほうが楽なのですが、それだと文字列配列(みたいな何か)であって、個々のマッチオブジェクトが取れないので、個々のindex(一致した位置)やキャプチャが必要な場合はRegExp.execを使う、という使い分けかなー、と私は思っています。
Matches
そんなことを考えていたら、やっぱRegExp.execのglobalって使いづらいね、と思ったのでE.Matchesを追加しました。C#のRegex.Matchesと同じようにマッチオブジェクト全てを返します。配列で欲しい場合はToArray()を。そのまま処理を加えたい場合は、Linqのメソッド群全てが使えます。マッチのうち先頭だけが欲しいけど射影処理もしたい場合はMatches().Select().First()という手が使えます。
var input = "abcdefgABzDefabgdg";
E.Matches(input, "ab(.)d", "i").ForEach(function(match)
{
for (var prop in match)
{
document.write(prop + " : " + match[prop] + "<br />");
}
document.write("toString() : " + match.toString() + "<br />");
document.write("<br />");
});
E.Matches(input, /ab(.)/i); // こうも書ける、gフラグはつけなくていい
E.Matches(input, "ab(.)d"); // 大文字小文字を区別するならflag無しで
E.Matches(input, pattern, flags)で、patternは文字列でも正規表現オブジェクトでも、どちらでも可能。flagsは省略可、与える場合は仕様通り"i", "m", "im"が使えます。gフラグを明示的に与える必要はありません。与えても与えなくても関係なくglobalで検索します。
中に入るマッチオブジェクトなのですが、[0]にマッチした文字列全体、キャプチャがある場合は[1]以降にキャプチャした文字列が入ります。あとは.indexと.input。IEだと.lastIndexも取れてますが、IE以外のブラウザではlastIndexは使えません(undefinedでした)
From(String)
今までE.From("hoge").ToArray()とすると[0]="hoge"になっていました。つまりE.Repeat("hoge",1)というわけです。これは、何というか、意味ないですよね。C#だと['h','o','g','e']というように、Charに分解します。というわけで、E.From("hoge").ToArray()の結果が["h","o","g","e"]になるように動作を変更しました。
var input = "こんにちは みなさん おげんき ですか? わたしは げんき です。\
この ぶんしょう は いぎりすの ケンブリッジ だいがく の けんきゅう の けっか\
いかりゃく"
var result = E.From(input.split(/[\s\t\n]/))
.Select(function(s)
{
return (s.length > 3)
? s.charAt(0)
+ E.From(s).Skip(1).Take(s.length - 2).Shuffle().ToString()
+ s.charAt(s.length - 1)
: s;
})
.ToString(" ");
alert(result);
サンプルとして、流行から100歩遅れてケブンッリジ変換をlinq.jsで。
真ん中の、「E.From(s).Skip(1).Take(s.length - 2).Shuffle().ToString()」という部分が「こんにちは」を「こんちには」に変換する部分です。E.From(s)で文字列を一文字ずつにバラしているわけです。Skip(1).Take(s.length-2)が、「最初と最後の文字を除く」です。実際の実行例は下のURLからどうぞ。
↑で動かしているものは、必ずシャッフルされるようにしたり、「、。!?」が末尾の時には別の処理をしていたりと、例に出しているコードとはちょっと違いますけれど。詳しくはソースを見てください。
その他
他に追加したメソッドはぶっちゃけ全然使い道ないのでササッと書き流します。まずInsertですが、これはConcatの場所自由版という感じにシーケンスを特定場所に挿入。IndexOf, LastIndexOfは位置の発見で見つからない場合は-1を返すというお馴染みな動作をします。他にlinq.tools.jsにHashSetを追加しました。これはC#のHashSetに似たようなもので、詳しくはリファレンス見てください。そして、Stopwatchに静的メソッドBenchを追加。これは、どうせStopwatch使うのはベンチマークの時だけでしょ?ということで。
var result = Linq.Tools.Stopwatch.Bench(1000, function()
{
E.Range(1,100).Where("$%2==0").Select("$*$").Force();
});
document.write(result + 'ms')
第一引数に繰り返し回数、第二引数に実行する関数。わざわざDateの引き算をすることなく、気楽に計れるので、便利といえば便利。というか、この手のものは手間がかかるとついつい避けてしまうので、サクッと計れないとダメですものね。
Stopwatch
- 2009-05-18
昨日の今日で変更というのも申し訳ないのですが、メソッド名を変えました。DateFormatはDateUtility.Format、DateParseはDateUtility.Parseになります。すみません。それだけじゃアレなので、その月の日数を返すDateUtility.DaysInMonthと、うるう年かどうかを判定するDateUtility.IsLeapYearを追加。それと、ストップウォッチクラス。
// Stopwatchを生成・開始して……
var sw = Stopwatch.StartNew();
// 重たい処理をしたりして
E.Range(1, 10000).Select("$*$").Force();
// Elapsed()でms単位で表示
alert(sw.Elapsed() + "ms");
/* その他のメソッド */
var sw = Stopwatch.Create(); // Stopwatchの生成(開始はしない)
sw.Start(); // 開始/再開
sw.Stop(); // タイマー停止
sw.Reset(); // タイマー停止+経過時間リセット
sw.IsRunning(); // 動いてるか止まってるか
そろそろ変数名とかインデントは、郷に入り手は郷に従うべきですよねー。ていうか、単純にJavaScriptでC#的な書き方を持ちだすと普通に宜しくない。特に、コンストラクタとメソッドを区別するために大文字小文字にする必要性はとても感じる。と言いつつも、懲りづに続けていたりいなかったり。
それにしても本体を更新していないのにバージョン番号が増殖していくのはどうかと思う。しかも今回のStopwatchとDateTime系のはlinq全く関係ないという有様。
linq.tools.js
- 2009-05-16
更新しました。今回は本体じゃなくて、ユーティリティスクリプトの追加が更新内容になります。少し便利なあると嬉しいちょっとした関数群をlinq.jsを使って。つまりそれってようするにただのサンプル……とは言ってはいけません。実際問題、sampleフォルダに入れちゃってますけどね!
中身は前回のテンプレート置換、配列を使って文字列を追加するStringBuilder、HTMLタグのエスケープをするHtmlEncode/Decode、クエリストリングをオブジェクトに変換するParseQueryStringとオブジェクトをクエリストリングに変換するToQueryString、そして書式を利用してDateを文字列に変換するDateFormatと文字列からDateに変換するDateParse。
詳しくは同梱のリファレンスを参照してください。それだけでもアレなので中身を幾つか。
Linq.Tools.HttpUtility.ParseQueryString = function(query)
{
return E.From(query.split('?'))
.SelectMany("$.split('&')", "a,b=>b.split('=')")
.Where("$.length == 2")
.ToObject("decodeURIComponent($[0])", "decodeURIComponent($[1])");
}
と、こんな感じでLinqを活用しています。?でバラして&でバラして=でバラしてオブジェクトに変換、と、QueryStringを見てまんま、思考のまんまに記述出来ます。便利便利。こういうのは個人的にもうforで書きたくないのですが、どうでしょう。
DateFormat / DateParse
Linq.Tools.DateFormat(date, format)はその名の通り、書式に基づいて日付を文字列に変換します。このぐらい標準で用意してあるといいのになあ……。formatは大体C#のに従う感じで、それなりに豊富。曜日も出せます。
Linq.Tools.DateFormat = function(date, format)
{
// 変数準備部省略
var PadZero = function(str, width)
{
var count = width - str.length;
return E.Repeat("0", count).ToString() + str;
};
var formatDict =
{
yyyy: year,
yy: year.substring(2),
y: year.substring(3),
MM: PadZero(month, 2),
M: month,
dd: PadZero(day, 2),
d: day,
// 以下略
};
var regex = new RegExp(E.From(formatDict).Select("$.Key").ToString("|"), "g");
return format.replace(regex, function(m) { return formatDict[m] });
}
変数部分がダラダラ長いので省略してます。0埋め関数が地味にLINQ。で、中身はいつものマンネリな手口です。置換用辞書作って、キー抜き出して正規表現作って、置換する。というだけです。実質二行。辞書の持ち方が若干富豪ですが、この程度なら全然気にならないでしょふ。あ、正規表現はこれだと順序が大事なので(yyyyの前にyyがマッチングされては困る)オブジェクトのキーを並べるやり方だと少し不安だけど、まあ、大丈夫でしょう!辞書の中身が動かないのだから、別に動的生成する必要は全くないどころか百害あって一理無しなのですが、それもまあ、無視黙殺。
var day = Linq.Tools.DateParse("2009-11-12 131034", "yyyy-MM-dd hhmmss");
var str = Linq.Tools.DateFormat(day, "yyyy/MM/dd HH:mm:ss");
FormatがあったらParseもセットだよね、ということでParseも作りました。更に手抜きで、桁数が揃っていないと変換出来ません。だから使えるのはyyyy|MM|dd|HH|mm|ssだけです。hhはダメです。紛らわしくてすみません。あと、動作はyyyyとかのフォーマット文字しか見てないので、それ以外は何だっていいです。_でも空白でも、文字数だけ合わせれば動きます。とても適当。ソースも実にスッカスカ。
JavaScriptで文字列テンプレート
- 2009-05-14
以前のString.Formatモドきが散々だったので、今回はもっとマトモに真面目にlinq.jsを使って書きます。
// {}で囲まれたものを置換する
var template = "食べ物は{food}で{dummy}飲み物は{drink}らしい。";
// このオブジェクトに対応したものに置換、という簡易テンプレート
var dict =
{
food: "タコ焼き",
drink: "コーラ"
};
// 目標とする結果は「食べ物はタコ焼きで{dummy}飲み物はコーラらしい。」
// dummyが引っかかってしまうのでダメ
var text = template.replace(/\{(.+?)\}/g, function(m, c) { return dict[c] });
普通に横着して.+?で置換しようとすると、{dummy}もマッチしてしまって宜しくない。別にひっかかってもいいじゃん、オブジェクトで指定しているのしか{}で囲まないよ!とばかりも言ってられないシチュエーションは、それなりにある、と、思う。というわけでちゃんとオブジェクトのキーだけを抜き取って正規表現を生成する。
// テンプレートに使うキーだけを抜き取る
var key = E.From(dict).Select("$.Key").ToString("|"); // food|drink
var regex = new RegExp("\{(" + key + ")\}", "g");
var text = template.replace(regex, function(m, c) { return dict[c] });
linq.jsならSelectしてToStringするだけで出来あがる。と、いうわけで簡単です。ついでなので汎用的な関数にしてみる。区切りは決め打ちで{}です。気に入らなければ%%でも${}でも好きに変更してください。
// テンプレートで置換・区切り文字はとりあえず{}
function templateReplace(template, replacement)
{
var key = E.From(replacement).Select("$.Key").ToString("|");
var regex = new RegExp("\{(" + key + ")\}", "g");
return template.replace(regex, function(m, c) { return replacement[c] });
}
var template = "食べ物は{food}で{dummy}飲み物は{drink}らしい。";
var text = templateReplace(template, { food: "タコ焼き", drink: "コーラ" });
しかし毎回オブジェクトを作るってのも面倒くさい。順番決め打ちで気楽に置換したい時って沢山あると思う。というわけで、オブジェクト以外を渡した時はC#のstring.Formatと同じ動作をするように変更。typeofとかinstanceofとか、JavaScriptって面倒くさいよね。
// replacementがオブジェクトでない場合は可変長引数として動かす
function templateReplace(template, replacement /* args */)
{
var key;
if (typeof replacement != "object")
{
key = E.Range(0, arguments.length - 1).ToString("|");
replacement = E.From(arguments).Skip(1).ToArray();
}
else if (replacement instanceof Array)
{
key = E.Range(0, replacement.length).ToString("|");
}
else
{
key = E.From(replacement).Select("$.Key").ToString("|");
}
var regex = new RegExp("\{(" + key + ")\}", "g");
return template.replace(regex, function(m, c) { return replacement[c] });
}
// 名前をつけてObjectを渡す形式でも
var template = "食べ物は{food}で{dummy}飲み物は{drink}らしい。";
var text = templateReplace(template, { food: "タコ焼き", drink: "コーラ" });
// 数字で指定する形式でも、どちらでも動く
var template = "食べ物は{0}で{dummy}飲み物は{1}らしい。";
var text = templateReplace(template, "タコ焼き", "コーラ");
var array = ["タコ焼き", "コーラ"]
var text = templateReplace(template, array); // 配列で渡しても平気
argumentsをFrom.Skip.ToArrayと、サクサクーとメソッド繋げて変換出来るので楽ちん。
C#でも大体同じ感じで書けます。ToStringは、String.Joinで代用可能。置換部分は、Regex.Replace(template, "正規表現", m => replacement[m.Groups[1].Value]);ですね。replacementはDictionary<string, string>で。ラムダ式が便利なので、MatchEvaluatorがようやくホイホイ使えて、JavaScript並みに気楽に正規表現を書けるようになりました。
と、いうことで
試してみてくれるとかDisってくれるとか、はてブしてくれるとか紹介してくれるとか切望中。
無限日曜・ベンチマーク
- 2009-05-08
var DayOfWeek =
{
Sunday: 0,
Monday: 1,
Tuesday: 2,
Wednesday: 3,
Thursday: 4,
Friday: 5,
Saturday: 6
}
var today = new Date();
var sundays = E.ToInfinity()
.Select(function(i) { return new Date(today.getFullYear(), today.getMonth(), today.getDate() + i) })
.Where(function(d) { return d.getDay() == DayOfWeek.Sunday });
今日以降の日曜日を無限に羅列。どうも日付話はLinqネタでよくあるらしいので、今更に。大量の使い捨てDateが気持ち悪いですが、ケチケチしちゃあいけません。これが富豪的プログラミング!?(何か違う気がする) ちなみに一年間なら、最後に.TakeWhile(function(d) { return d < 一年後 })とでもすれば良いわけです。無限万歳。
ベンチマーク
さて、linq.jsは遅い遅い連呼しているわけですが、じゃあ実際どのぐらい遅いのでしょうか。JSEnumeratorのベンチマークを拝借してテストしてみました。
ブラウザによって結構変わるようです。IEが爆遅でChromeが爆速なのは変わらないのですが、順位がわりと入れ替わっています。ていうかそもそも計る度に変動が激しいような……。linq.jsは、ForEachが若干遅いのが気がかりですが、LinqではForEachよりも(そもそもC#のLinqにはForEachが無い)mapとfilter中心なわけで、reduceがかなり健闘しているから問題ないと言えるのではないでしょうか? と、言いたいところなのだけどmapが遅いですね。一番よく使うmapが遅いですね。ふむ。
結論:Chrome速すぎ。Chromeの速度からすれば全て誤差範囲内に収まってしまうわけなので、じゃんじゃん使っていいと思うよ!ていうか、私としては思ってたよりも遅くなかった、が感想です。
linq.js ver 1.2.0.1 - 少しメソッド追加
- 2009-05-07
ちょっとした、前から足そうと思ってた機能を追加しました。面倒くさくて放置していたのですが、ゴールデンウィーク何もしませんでした記念に追加しました。何もしなかったというか、今更ながらLeft 4 Deadを買って(Xブレードは勿論スルー)、それが面白いの何のでほんと連休はゲームしかしてなかったような。オラタンは操作に馴染めないなあ、とか思ってたんですが段々と感覚を取り戻せたみたいで(まあ、DC版しかプレイしてないんですけどね、回線利用料で月数万吹っ飛んだとか、今考えると泣けます) 当分は楽しめそうです。スペースインベーダーエクストリームは、これから考える。
Times
Repeat(null,10)という書き方をしてforの代用をしていたわけですが、非常にダサかった。ので、それと同効果のメソッドを用意しました。
E.Times("Math.random()", 10) // 10回ランダムを送出
E.Times(function() { return Math.random() }, 10) // 上のはこれの略記方です
E.Times(function() { alert("Hello") }, 5).Force() // 5回Helloはこんな風で、Rubyっぽく?
例によって例はランダムで。まあ、こんな感じに使うと便利かなー、と思います。回数を省くと無限リピートになります。ようするに関数版Repeatなわけです。回数指定は後よりも前のほうがいいよねえ、とか思いつつも、Repeatと同じ感じにしたいというか、すべき(混乱するので、なるべくこういうのは統一したいです)だと思ったので、そんなわけでして。
step
MATLABやHaskellや、なんかにもあるのですが、増減値に1以外の任意の数が選べます。
E.Range(1, 9, 3); // 1,4,7,10,13,16,19,22,25
E.RangeDown(1, 9, 3); // 1,-2,-5,-8,-11,-14,-17,-20,-23
E.RangeTo(1, 9, 3); // 1,4,7
E.RangeDownTo(1, -5, 3); // 1,-2,-5
E.ToInfinity(5, 3).Take(3); // 5,8,11
E.ToNegativeInfinity(5, 3).Take(3); // 5,2,-1
この辺、Rangeの直後にSelectで加工すればいいってだけの話ではあるんですけど、ショートカットが提供されるのも悪くないな、と思います。実際のLINQだってWhere(predicate).First()の短縮としてFirst(predicate)が用意されていたりしますしね。まあ、しかしこれでRangeの増減値に-1を使えるようにしておけば(今は無理です) RangeDownの存在意義が消滅してしまったことは気にしないことにします。
ToJSON
酷くバグッていたので修正。まーたポカミスです。最近、あちらこちらでポカミスを連発していて困りました。もっとちゃんとしたテスト書いておかないとダメですかねー。面倒くさくて簡易的というか手抜きというか、そんなようなテストしか用意していないので役に立っている度は半分ぐらいのようです。こうもクソバグを連発しているようじゃあ、どこがStableなんだよ、って話ではある。すみません。ごめんなさい。とりあえず、今度は、今度こそは大丈夫なはず。
まあしかし、追加はすぐなんですが、そのあとreference書いてtest書いて、ってのがダルくて追加作業を躊躇ってしまうんですよねー。うーん。この辺、もっと自動化というか、手軽に出来る仕組みを整備しないとダメですね。
そういえば、何だかんだでDL数が100を超えていました。ありがとうございます。100っていうのは、偉大な数字ですね。マイルストーンです。ニコニコ動画なんかでもそうで、普通に閲覧者として見ていると再生数1万以下の動画なんて存在しないに等しい、ぐらいの勢いですけれど、実際は1万以下の動画しかうpしてないような人のほうが多いんですから!遥かに!というような世界が見えます。で、それはそれで、幸せなのです。そもそもの目標が二桁であったことを考えると、三桁というのは嬉しい数字です。
ver 1.1.0.1 日本語が含まれる場合に正しく投稿されない問題を修正
- 2009-04-29
表題通りの修正、です。これが酷い話で、大往生を買った頃に不具合が発覚していたというのに、今の今まで放置していたという。指摘があって(ありがとうございます!) ようやく重たい腰をあげて……。しかも大体どこが問題発生の場所なのか検討ついてたし、事実その通りだったし、しかも一行書き換えるだけで終わる程度の話だったうえに、不具合原因が超凡ミスだったり、それなのに今の今まで放置していたし、と二重三重に酷い話です。
もう本当にごめんなさいとしか言いようがない。
JavaScriptでString.Format
- 2009-04-28
function Format(format /*,obj1,obj2...*/)
{
// 置換文字に対応する配列
var dict = E.From(arguments).Skip(1).ToArray();
// 正規表現用の文字列( {(0|1|2)} )
var str = "\\{(" + E.Range(0, dict.length).ToString("|") + ")\\}";
var regex = new RegExp(str, "g");
return format.replace(regex, function(m, c) { return dict[c] });
}
var result = Format("食べ物は{0}で飲み物は{1}だそうです。", "タコ焼き", "コーラ");
document.write(result); // 食べ物はタコ焼きで飲み物はコーラだそうです。
(C#の)String.Formatは便利!お手軽!JavaScriptでも使いたい!ていうかprototype.jsのTemplateは書き方がヘヴィすぎだしね!というわけでサクサクッとその場使い捨てなフォーマット記述関数を、linq.jsを使って(←ここ重要)作りました。
やってることはごくごく単純で、正規表現でガッと置換するだけです。その正規表現を生成するのにlinq.jsが役立ち……ってほど役立ってないというかあんまし使ってませんね。別に決め打ちで数字だけマッチさせるのなら、余計な手間もループして結合もいらないし。うわ……。
function Format(format /*,obj1,obj2...*/)
{
var args = arguments;
return format.replace(/\{(\d)\}/g, function(m, c) { return args[parseInt(c) + 1] });
}
はいはい。別に使わなくてもたった二行でしたねすみませんすみません。次はもっとマシな例を考えてきます。最近脳みそがゆるふわ化しててほんとすみませんすみません。
追記:私の制作しているライブラリlinq.jsのアドオンという形で、機能強化したフォーマット用のコードを置いてありますので良ければそちらも参照ください。linq.jsと周辺については最もタメになる「初心者用言語」はVisualStudio(言語?)にまとめました。