PrologとHaskellの挙動の違い

foo(0)とfoo(N)を用意しておいて、Nの時にはなにか処理をしてからfoo(N - 1)を再帰呼び出しする、というコーディングパターンをPrologで使う際に、Haskellの発想で書くと罠にハマるのでメモ。

まずHaskellで挙動を確認してみる。

foo 0 = do{ print "rule1"; print 0 }
foo n = do{ print "rule2"; print n }

main = do
  foo (1 - 1)
  foo 1
  foo 0
"rule1"
0
"rule2"
1
"rule1"
0

foo(1 - 1)でfoo 0の定義が呼ばれていることがわかる。またfoo 0でfoo 0の定義だけが呼ばれていることがわかる。(何を当たり前のことを、と思うかもしれないけども、その思い込みにハマった)

Prologで同じような書き方をしてみよう。

foo(0) :- print('rule1: '), print(0).
foo(N) :- print('rule2: '), print(N).
?- foo(1 - 1).
rule2: 1-1
true.

?- foo(1).
rule2: 1
true.

?- foo(0).
rule1: 0
true ;
rule2: 0
true.

Prologでは、まずfoo(1 - 1)はfoo(0)にマッチしないのでrule2になっている。Haskellではパターンマッチのタイミングで1-1の評価が強制されて0になるが、Prologではされない。またfoo(0)の時に、rule1にマッチした後、rule2にもマッチしている。

Prologでフィボナッチを計算している例などを見ると、うまくこの落とし穴を避けるような書き方をしてある。出かけないといけないので続きはまた今度。ヒントはカットとis。