関数型言語の勉強にSICPを読もう - (59) 4章 - 超言語的抽象(220ページ) C++でSchemeインタプリタを作ろう8

実装というか設計に迷いが。

(begin
  (define func (lambda (a) "test"))
  (func 3)
)

まず前提として、上の式を評価すると、"test" が返ることが期待されます。(Gaucheで試しました)


SICPによれば(func 3)は以下の順に評価されます。
1. (予約語でない語句 数値)という形なので関数適用と判断される
2. なのでまず func が evalされます。
3. 次に 3が evalされます。数値は self-evaluatingなので評価されても3のままです。
4. 2, 3の evalの結果をapplyで実行します。
具体的なコードは

(apply 2の結果 3の結果)


で、今戸惑っているのは 2の部分。
SICPのコード例によれば func は予約語でないため変数扱いであるから、funcをevalすると(lambda (a) "test")をあらわすオブジェクトが返ります。
Frameのリストから一致する変数の値がlook-up-variableで返されるというわけです。


で、このまま3, 4のステップに突入すると

(apply (lambda (a) "test") 3)

という形になります。


applyでは (lambda (a) "test")をあらわすオブジェクトは、evalSequenceに渡されて eval されます。
lambdaをあらわすオブジェクトは eval されると procedure をあらわすオブジェクトになるのですが、ここで eval がひとつ足りないことに気づきます。


applyに渡る時点で procedure になっていないといけないのに lambda のオブジェクトとなっています。つまり eval が1段階で抜けています。
ちょっと説明が分かりづらいと思うのですが、今のロジックは以下のコードでは適切に動きますが、funcという変数を介していると eval が足りなくなってしまうという話です。

(begin
  ((lambda (a) "test"  3)
)


今思いつく解決策は apply の直前にオペレータを複数回 eval して、 procedureオブジェクトになったら apply を callするという方法なのですが

  • 構文エラーでそもそも一生 procedure オブジェクトにならないときに無限ループになる
  • SICPのコードを忠実にC++に落としていたが何か間違っているに違いない

などがとても気になります。


※「SICPを読もう」の目次はこちら


計算機プログラムの構造と解釈
ジェラルド・ジェイ サスマン ジュリー サスマン ハロルド エイブルソン 和田 英一
ピアソンエデュケーション (2000/02)
売り上げランキング: 56,404