Lambda 呼び出し引数末尾再帰呼出し < 教えて,Schemer! < Viivi の小部屋 < 入り口 / Entrance
Lambda 呼び出しを引数にもつ末尾再帰呼出しが
期待どおり動かなかった事象について本ページで質問させていただいておりましたが,
問題が解決しましたのでご報告いたします.
この修正は Viivi 第 00.22.00 版に反映されます.
引数の束縛をclosure がもっている束縛環境そのものに登録してしまっていたため,
正しい評価が行われませんでした.
基本的なまちがいで,全くお恥ずかしい限りです.
修正した版で走らせた3つのコードのトレース出力を掲載しておきます.
数学的には,再帰処理の終端条件は
0! = 1
,
すなわち,
「(= n 0)
で 1 を返す」
というのが正しいのかもしれませんが,ここでは説明を明快にするために,
1! = 1
,
すなわち,
「(= n 1)
で 1 を返す」
を使うことにします.
その理由は,もし数学的に正しい条件を適用すると,
n=0
のときと n=1
のときの両方でシンボル
result
の束縛値が同じ 1 になってしまい,
result
の束縛関係の更新を明確に示すことができずわかりにくくなってしまうからです.
階乗計算を行う Lisp の再帰プログラムのうち,最初に, 「末尾再帰ではない」「普通の」再帰呼び出しを行うプログラム
:::::::::::::::::::
:: factorial.scm ::
:::::::::::::::::::
1: (define !
2: (lambda (n)
3: (if (= n 1)
4: 1
5: (* n (! (- n 1))))))
6:
7: (! 2)
8:
についてのトレース出力を示します. $ viivi -K factorial.scm
...
4: ┌─ (! 2)
4: │┌─ !
5: │└─ #<closure:[(n)] [(if (= n 1) 1 (* n (! (- n 1))))]>
5: │┌─ 2
6: │└─ 2
6: │ * #<closure:[(n)] [(if (= n 1) 1 (* n (! (- n 1))))]>
6: │ ○ n → 2
6: │┌─ (if (= n 1) 1 (* n (! (- n 1))))
6: ││┌─ if
7: ││└─ #<procedure:if>
7: ││ * #<procedure:if>
7: ││┌─ (= n 1)
7: │││┌─ =
8: │││└─ #<procedure:=>
8: │││┌─ n
9: │││└─ 2
9: │││┌─ 1
10: │││└─ 1
10: │││ * #<procedure:=>
11: ││└─ #f
11: ││┌─ (* n (! (- n 1)))
11: │││┌─ *
12: │││└─ #<procedure:*>
12: │││┌─ n
13: │││└─ 2
13: │││┌─ (! (- n 1))
13: ││││┌─ !
14: ││││└─ #<closure:[(n)] [(if (= n 1) 1 (* n (! (- n 1))))]>
14: ││││┌─ (- n 1)
14: │││││┌─ -
15: │││││└─ #<procedure:->
15: │││││┌─ n
16: │││││└─ 2
16: │││││┌─ 1
17: │││││└─ 1
17: │││││ * #<procedure:->
18: ││││└─ 1
18: ││││ * #<closure:[(n)] [(if (= n 1) 1 (* n (! (- n 1))))]>
18: ││││ ● n → 1
18: ││││┌─ (if (= n 1) 1 (* n (! (- n 1))))
18: │││││┌─ if
19: │││││└─ #<procedure:if>
19: │││││ * #<procedure:if>
19: │││││┌─ (= n 1)
19: ││││││┌─ =
20: ││││││└─ #<procedure:=>
20: ││││││┌─ n
21: ││││││└─ 1
21: ││││││┌─ 1
22: ││││││└─ 1
22: ││││││ * #<procedure:=>
23: │││││└─ #t
23: │││││┌─ 1
24: │││││└─ 1
25: ││││└─ 1
26: │││└─ 1
26: │││ * #<procedure:*>
27: ││└─ 2
28: │└─ 2
29: └─ 2
同じ階乗計算を行うもので,末尾再帰呼び出しを用いたプログラムは
::::::::::::::::::::::
:: factorialTRC.scm ::
::::::::::::::::::::::
1: (define !
2: (lambda (n result)
3: (if (= n 1)
4: result
5: (! (- n 1) (* n result)))))
6:
7: (! 2 1)
8:
です. $ viivi -K factorialTRC.scm
...
4: ┌─ (! 2 1)
4: │┌─ !
5: │└─ #<closure:[(n result)] [(if (= n 1) result (! (- n 1) (* n result)))]>
5: │┌─ 2
6: │└─ 2
6: │┌─ 1
7: │└─ 1
7: │ * #<closure:[(n result)] [(if (= n 1) result (! (- n 1) (* n result)))]>
7: │ ○ n → 2
7: │ ○ result → 1
7: │┌─ (if (= n 1) result (! (- n 1) (* n result)))
7: ││┌─ if
8: ││└─ #<procedure:if>
8: ││ * #<procedure:if>
8: ││┌─ (= n 1)
8: │││┌─ =
9: │││└─ #<procedure:=>
9: │││┌─ n
10: │││└─ 2
10: │││┌─ 1
11: │││└─ 1
11: │││ * #<procedure:=>
12: ││└─ #f
12: ││┌─ (! (- n 1) (* n result))
12: │││┌─ !
13: │││└─ #<closure:[(n result)] [(if (= n 1) result (! (- n 1) (* n result)))]>
13: │││┌─ (- n 1)
13: ││││┌─ -
14: ││││└─ #<procedure:->
14: ││││┌─ n
15: ││││└─ 2
15: ││││┌─ 1
16: ││││└─ 1
16: ││││ * #<procedure:->
17: │││└─ 1
17: │││┌─ (* n result)
17: ││││┌─ *
18: ││││└─ #<procedure:*>
18: ││││┌─ n
19: ││││└─ 2
19: ││││┌─ result
20: ││││└─ 1
20: ││││ * #<procedure:*>
21: │││└─ 2
21: │ / #<closure:[(n result)] [(if (= n 1) result (! (- n 1) (* n result)))]>
21: │ ● n → 1
21: │ ● result → 2
21: │┌─ (if (= n 1) result (! (- n 1) (* n result)))
21: ││┌─ if
22: ││└─ #<procedure:if>
22: ││ * #<procedure:if>
22: ││┌─ (= n 1)
22: │││┌─ =
23: │││└─ #<procedure:=>
23: │││┌─ n
24: │││└─ 1
24: │││┌─ 1
25: │││└─ 1
25: │││ * #<procedure:=>
26: ││└─ #t
26: ││┌─ result
27: ││└─ 2
28: │└─ 2
29: └─ 2
[おわび]
10年ほど前にどこかで見つけたプログラムですが,引用元を失念してしまいました.
もしこのコードを書かれたご本人がいらっしゃいましたらたいへん失礼いたしました.
ご本人でなくても引用元をご存知の方がいらっしゃいましたら,
ご連絡いただければ正式な引用元として掲載させていただきます.
:::::::::::::::::::::::::
:: factorialLambda.scm ::
:::::::::::::::::::::::::
1: (define _fact
2: (lambda (n k)
3: (if (= n 1)
4: (k 1)
5: (_fact (- n 1)
6: (lambda (u)
7: (k (* n u)))))))
8:
9: (_fact 2 (lambda (x) x))
10:
上記の二例と同様に,期待される評価結果は 2 です. $ viivi -K factorialLambda.scm
0: ┌─ (define _fact (lambda (n k) (if (= n 1) (k 1) (_fact (- n 1) (lambda (u) (k (* n u)))))))
0: │┌─ define
1: │└─ #<procedure:define>
1: │ * #<procedure:define>
1: │┌─ (lambda (n k) (if (= n 1) (k 1) (_fact (- n 1) (lambda (u) (k (* n u))))))
1: ││┌─ lambda
2: ││└─ #<procedure:lambda>
2: ││ * #<procedure:lambda>
3: │└─ #<closure:[(n k)] [(if (= n 1) (k 1) (_fact (- n 1) (lambda (u) (k (* n u)))))]>
3: │ ○ _fact → #<closure:[(n k)] [(if (= n 1) (k 1) (_fact (- n 1) (lambda (u) (k (* n u)))))]>
4: └─ #<unspecified>
4: ┌─ (_fact 2 (lambda (x) x))
4: │┌─ _fact
5: │└─ #<closure:[(n k)] [(if (= n 1) (k 1) (_fact (- n 1) (lambda (u) (k (* n u)))))]>
5: │┌─ 2
6: │└─ 2
6: │┌─ (lambda (x) x)
6: ││┌─ lambda
7: ││└─ #<procedure:lambda>
7: ││ * #<procedure:lambda>
8: │└─ #<closure:[(x)] [x]>
8: │ * #<closure:[(n k)] [(if (= n 1) (k 1) (_fact (- n 1) (lambda (u) (k (* n u)))))]>
8: │ ○ n → 2
8: │ ○ k → #<closure:[(x)] [x]>
8: │┌─ (if (= n 1) (k 1) (_fact (- n 1) (lambda (u) (k (* n u)))))
8: ││┌─ if
9: ││└─ #<procedure:if>
9: ││ * #<procedure:if>
9: ││┌─ (= n 1)
9: │││┌─ =
10: │││└─ #<procedure:=>
10: │││┌─ n
11: │││└─ 2
11: │││┌─ 1
12: │││└─ 1
12: │││ * #<procedure:=>
13: ││└─ #f
13: ││┌─ (_fact (- n 1) (lambda (u) (k (* n u))))
13: │││┌─ _fact
14: │││└─ #<closure:[(n k)] [(if (= n 1) (k 1) (_fact (- n 1) (lambda (u) (k (* n u)))))]>
14: │││┌─ (- n 1)
14: ││││┌─ -
15: ││││└─ #<procedure:->
15: ││││┌─ n
16: ││││└─ 2
16: ││││┌─ 1
17: ││││└─ 1
17: ││││ * #<procedure:->
18: │││└─ 1
18: │││┌─ (lambda (u) (k (* n u)))
18: ││││┌─ lambda
19: ││││└─ #<procedure:lambda>
19: ││││ * #<procedure:lambda>
20: │││└─ #<closure:[(u)] [(k (* n u))]>
20: │ / #<closure:[(n k)] [(if (= n 1) (k 1) (_fact (- n 1) (lambda (u) (k (* n u)))))]>
20: │ ● n → 1
20: │ ● k → #<closure:[(u)] [(k (* n u))]>
20: │┌─ (if (= n 1) (k 1) (_fact (- n 1) (lambda (u) (k (* n u)))))
20: ││┌─ if
21: ││└─ #<procedure:if>
21: ││ * #<procedure:if>
21: ││┌─ (= n 1)
21: │││┌─ =
22: │││└─ #<procedure:=>
22: │││┌─ n
23: │││└─ 1
23: │││┌─ 1
24: │││└─ 1
24: │││ * #<procedure:=>
25: ││└─ #t
25: ││┌─ (k 1)
25: │││┌─ k
26: │││└─ #<closure:[(u)] [(k (* n u))]>
26: │││┌─ 1
27: │││└─ 1
27: │││ * #<closure:[(u)] [(k (* n u))]>
27: │││ ○ u → 1
27: │││┌─ (k (* n u))
27: ││││┌─ k
28: ││││└─ #<closure:[(x)] [x]>
28: ││││┌─ (* n u)
28: │││││┌─ *
29: │││││└─ #<procedure:*>
29: │││││┌─ n
30: │││││└─ 2
30: │││││┌─ u
31: │││││└─ 1
31: │││││ * #<procedure:*>
32: ││││└─ 2
32: ││││ * #<closure:[(x)] [x]>
32: ││││ ○ x → 2
32: ││││┌─ x
33: ││││└─ 2
34: │││└─ 2
35: ││└─ 2
36: │└─ 2
37: └─ 2
Lambda 呼び出し引数末尾再帰呼出し < 教えて,Schemer! < Viivi の小部屋 < 入り口 / Entrance
2022/03/02 開設
2024/08/13 更新
Copyright(C) 2003-2024 ilma <ilma@viivi.io> All rights reserved.