Lambda 呼び出し引数末尾再帰呼出し < 教えて,Schemer! < Viivi の小部屋 < 入り口 / Entrance


[解決済み] Lambda 呼び出しを引数にもつ末尾再帰呼出しのスコープについて

Lambda 呼び出しを引数にもつ末尾再帰呼出しが 期待どおり動かなかった事象について本ページで質問させていただいておりましたが, 問題が解決しましたのでご報告いたします.
この修正は Viivi 第 00.22.00 版に反映されます.

引数の束縛をclosure がもっている束縛環境そのものに登録してしまっていたため, 正しい評価が行われませんでした.
基本的なまちがいで,全くお恥ずかしい限りです.
修正した版で走らせた3つのコードのトレース出力を掲載しておきます.
数学的には,再帰処理の終端条件は 0! = 1, すなわち, 「(= n 0) で 1 を返す」 というのが正しいのかもしれませんが,ここでは説明を明快にするために, 1! = 1, すなわち, 「(= n 1) で 1 を返す」 を使うことにします.
その理由は,もし数学的に正しい条件を適用すると, n=0 のときと n=1 のときの両方でシンボル result の束縛値が同じ 1 になってしまい, result の束縛関係の更新を明確に示すことができずわかりにくくなってしまうからです.


[1] (末尾再帰ではない) 普通の再帰呼び出しの場合

階乗計算を行う Lisp の再帰プログラムのうち,最初に, 「末尾再帰ではない」「普通の」再帰呼び出しを行うプログラム

:::::::::::::::::::
:: factorial.scm ::
:::::::::::::::::::
   1:   (define !
   2:     (lambda (n)
   3:       (if (= n 1)
   4:         1
   5:         (* n (! (- n 1))))))
   6: 
   7:   (! 2)
   8: 
についてのトレース出力を示します.
Scheme ソースコードにはファイル名と行番号も表示しています.
Viivi は正しく評価することができ,期待される結果 2 を返します.

	$ 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


[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 は正しく評価することができ,期待される結果 2 を返します.
Viivi トレース出力はつぎのようになります:
	$ 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


[3] Lambda 呼び出しを引数にもつ末尾再帰呼出しの場合

[おわび]
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 もこのコードを正しく評価できるように修正されましたので, 以下にトレース出力を示します.
	$ 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.