Schemeのプロシージャ呼び出し補足
Schemeのプロシージャ呼び出し時のプロシージャ及び引数の評価順序についての補足です。
情報科学基礎実験のWebページには、式の評価順序についてイカのように書かれています。
評価戦略
上の例で、((apply-and-add square 1) 3)を評価していますが、実は、この式の全体が評価される前に、(apply-and-add square 1)という部分が評価されています。関数適用では、先ず最初に最初の要素が評価され、次に引数の部分が評価され、最後に全体が評価されます。これを確かめるには、以下のようなプログラムを実行してみるとよいでしょう。(define (apply-and-add f x) (display "first\n") (lambda (y) (+ (f y) x))) (define (square x) (display "third\n") (* x x)) ((apply-and-add square 1) (begin (display "second\n") 3))
このプログラムをGaucheで実行された方は気づかれたかと思いますが、結果は
second first third 10
などと、想定した通りではなく、順序がめちゃくちゃになったかと思います。
実は、上記の説明は誤りです。イカにRevised^5 Report on the Algorithmic Language Scheme 4.1.3 Procedure callsの文章を引用します。
syntax: (<operator> <operand1> ...)
A procedure call is written by simply enclosing in parentheses expressions for the procedure to be called and the arguments to be passed to it. The operator and operand expressions are evaluated (in an unspecified order) and the resulting procedure is passed the resulting arguments.
これの日本語訳を「プログラミング言語SCHEME」(ピアソンエデュケーションジャパン)より引用します。
(<演算子> <オペランド_1> ...) シンタックス
プロシージャ呼び出し(procedure call)は呼び出すプロシージャとそれに引き渡す引数の式を単に括弧で囲んで記述する。演算子とオペランド式が評価され(その順序は規定されていない)、結果として得られたプロシージャに結果として得られた引数が引き渡される。
つまり、プロシージャとその引数の評価順序は規定されていないのです。
これを上の例に当てはめますと、
(apply-and-add square 1):プロシージャ
(begin (display "second\n") 3):引数
でありますから、これらはどちらが先に実行されてもよいのです。
先程の例では、たまたまGaucheが引数を先に評価したため、secondが先に表示されたわけです。これをGuileで実行すると、プロシージャが先に評価されて、firstが先に表示されます。
このように評価の順序は処理系に依存しますし、また、プロシージャ呼び出しごとに異なってもよいとされていますので、同じ処理系で同じプロシージャを呼び出しても順序が違うということも起こり得ます。(※実際にGaucheやGuileでどのように実装されているかは知らないので詳しい方にパス。)
したがって、プロシージャを呼び出す際には、評価順序に依存するようなプログラムを書かないようにしましょう。順序を指定したい場合はbegin形式を使用してください。