読者です 読者をやめる 読者になる 読者になる

おっさんホイホイとしての Code Complete と,近くにあっても気付かない guard 句の話

.NET

ガード句かぁ,じゃんぬねっとさんは『Code Complete』が好きなんだろうなぁ,とか思いつつ久しぶりに読み直してみたり.
Code Complete第2版〈上〉―完全なプログラミングを目指して Code Complete第2版〈下〉―完全なプログラミングを目指して
さて,『Code Complete』は歴史に名を残す本でしょうし,書かれた時代背景を考えれば間違いなく名著なのでしょうが,そろそろこの本の持つ (ようになった) おっさんホイホイ的な性質についても一言書いておきますかね.

Code を Complete するには若すぎる人も居る

若い人が『Code Complete』を読んで何がおいしいかというと,この本に書いてあることを実践することでおっさん受けが非常に良いコードを書けるようになることです.なんというかこう,楽に年を取れる.ただこれには暗黒面を感じることもあって,この本の内容を実践することは,未来を作ると言うよりは過去の「みんな」に同化してるだけじゃないか,という不安ががが.
『Generative Programming』の真反対というか.
ぶっちゃけ「人間には環境(言語)は変えられない*1」タイプの人には向いているように思うのですが,そうじゃない人にとってはどうなんでしょうね,この本*2.もちろん,人のコードを読む上で,辞書代わりに使うのにはとても良い本です.でも 21 世紀における人生設計としてはどうなのかなー 用例辞典だって時代と共に変わりますよ? みたいな.
"Coding Horror" をおもしろおかしく紹介するのは良いのですが,でも,ねぇ,なぜ環境の方を変えようとしないのかな?
もしあなたの近くにこの本があれば,試しに第 4 部 (第14章から第19章) を読み直して下さいませ.『Generative Programming』が C++ の構文自由度の先に未来を見ていたのなら,『Code Complete』の第 4 部はさしずめ郷土資料館じゃなかろうかと.プログラマとしてのキャリアに役立つのはまあ間違いないのですが,かといってこのやり方で続けるべきだと思い込むのもどうかとねと.
同書の 6 章 は Web で読むこともできます.『Code Complete 第2版 上 - @IT BOOK Preview』.個人的にはなんだか憂鬱がデジャビュなんですが皆様いかがでしょうか?

Guard 再考

もうひとつの本題.

を読んだ後に,こちらをご覧下さい.

どうでしょう? oxy くんが書いた Haskell のコードに "guard" の文字を見つけられましたか?

リストモナドで非決定性計算

以上のリストモナドの性質を使うと、総当たりのプログラム、格好よくいうと非決定性の計算を行うことができます。 SICPから次のような問題を借りることにします。

Baker, Cooper, Fletcher, MillerとSmithは五階建てアパートの異なる階に住んでいる。Bakerは最上階に住むのではない。Cooperは最下階に住むのではない。Fletcherは最上階にも最下階にも住むのではない。MillerはCooperより上の階に住んでいる。SmithはFletcherの隣の階に住むのではない。FletcherはCooperの隣の階に住むのではない。それぞれはどの階に住んでいるか。

さて、この問題を解くことにしましょう。次に示すコードを見ると、まるで問題の条件を並べているだけのように見えるかもしれませんが、実はこれで可能な解をちゃんと求めることができます。

import Control.Monad.List

solve = do baker <- [1, 2, 3, 4, 5]
           cooper <- [1, 2, 3, 4, 5]
           fletcher <- [1, 2, 3, 4, 5]
           miller <- [1, 2, 3, 4, 5]
           smith <- [1, 2, 3, 4, 5]
           guard $ distinct [baker, cooper, fletcher, miller, smith]
           guard $ baker /= 5
           guard $ cooper /= 1
           guard $ fletcher /= 1 && fletcher /= 5
           guard $ miller > cooper
           guard $ abs (smith - fletcher) /= 1
           guard $ abs (fletcher - cooper) /= 1
           [baker, cooper, fletcher, miller, smith]

distinct :: Eq a => [a] -> Bool
distinct [] = True
distinct (x:xs) = all (/=x) xs && distinct xs

main :: IO()
main = print solve
実行結果: [3,2,4,5,1]

いやこれ guard なんですよ.まさに正しく命名されている.guard 句をシンタックス的に考えていると分からないかもしれませんが,計算戦略的なノリで「ああこれは guard だな」と.
対応する C# 版も示してましたよね? C# 3.0 で,Haskell の (リストモナドにおける) guard に対応するのは何でしたか?

using System;
using System.Linq;

class Program
{
    static void Main(string[] args)
    {
        var answers =
            from baker in Enumerable.Range(1, 5)
            from cooper in Enumerable.Range(1, 5)
            from fletcher in Enumerable.Range(1, 5)
            from miller in Enumerable.Range(1, 5)
            from smith in Enumerable.Range(1, 5)
            where new []{ baker, cooper, fletcher, miller, smith }.Distinct().Count() == 5
            where baker != 5
            where cooper != 1
            where fletcher != 1 && fletcher != 5
            where miller > cooper
            where Math.Abs(checked(smith - fletcher)) != 1
            where Math.Abs(checked(fletcher - cooper)) != 1
            select new { baker, cooper, fletcher, miller, smith };

        foreach (var answer in answers)
            Console.WriteLine(answer);
    }
}
{ baker = 3, cooper = 2, fletcher = 4, miller = 5, smith = 1 }

そう,C# 3.0 では where こそが guard です.ループにおける guard 句が大好きな人は,そのやり方を LINQ でもそのまま使えるのですよ.
私ももう十分おっさんの域ですが,こういうことを考えている間だけは,少し若返った気がするようなしないような.まあ気のせいでしょうなー

*1:自分の経験の枠組みは自分で変えられるか? - アンカテ

*2:まあ本の最初の方にも,この本の内容全てに同意する必要はないと書かれていたかと思いましたが