JavaScriptで総当たり

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的に特異なコードが書けるのでお遊びにでも使ってもらえると嬉しいです。

Profile

Yoshifumi Kawai

Cysharp, Inc
CEO/CTO

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

Twitter:@neuecc GitHub:neuecc

Archive