Gauche > Archives > 2013/02/22

2013/02/22 04:52:38 UTCiyanaha
#
発売間近の『Land of Lisp』ですが、何か所か誤りを見つけました。
#
p.312のadd-passing-move、p.313のattacking-moves、p.314のneighborsは大域変数を参照しているので、Functionalではないんじゃないでしょうか?
2013/02/22 05:30:23 UTCshiro
#
*num-players*とかはゲーム中変化することのない定数なんでいいんですよ。
2013/02/22 05:32:51 UTCiyanaha
#
まあそういえばそうですが...
#
揚げ足を取るようで申し訳ございませんが、
#
筆者は「同じ引数で呼び出したとき、同じ値を返すことが保証される」ということをFunctionalな関数の条件としています。
#
(let ((*num-players* 5))
  (add-passing-move ...))

(let ((*num-players* 6))
  (add-passing-move ...))
#
この二つを仮に同じ引数で呼び出したとしても、挙動が変わってしまいます。
#
defconstantで定義されていたならばエラーを返すのでよいのですが、defparameterを使って定義されているので、厳密にはFunctionalといえないのではないでしょうか?
2013/02/22 08:31:10 UTCshiro
#
それを言い出すと、Common Lispの関数はいつでも再定義できるので、グローバルに定義された関数を呼び出す関数は全て厳密にはFunctionalと言えなくなってしまいます。
#
CLを使ってFunctional Programmingする場合はある程度conventionに依存するところがあり、今回の場合「ゲームのバージョンを変えてゆく際には変更するけど、単一のゲーム内で変えない値」というのを暗黙に定数とみなしているわけです。言語によって強制されるものではないので、おっしゃるとおり*num-players*をrebindすれば挙動は変わります。実際、それによって抽象化が破れている箇所があります (ゲーム盤の大きさを変えた後、メモ化を一度リセットしないとならない、というところ)。
#
で、特に明示したり注釈をつけずとも、現在のコードで関数型プログラミングの精神は文脈からわかると思うのですがいかがでしょうか。
2013/02/22 09:54:07 UTCiyanaha
#
回答ありがとうございます。理解しました。揚げ足を取って申し訳ございません。
2013/02/22 09:57:54 UTCshiro
#
確かに、厳密さを欠く記述がちょくちょくあって、どうしようかなと思ったのですが結局それも本書の持ち味だろうということであまりこだわらないことにしました。(ただ、原書でリテラルリストを平気で変更してるコードがあったので、それについては著者に確認の上直してあります。リテラルの変更は噛みつかれると痛いので。)
#
(噛みつかれると痛い、というのは読者が噛みつくってことじゃなくハマりどころって意味です。it bites hardのつもりが日本語にすると主語が不明確になってしまった。)