計算した値を取っておくには?


今まで実際に Scheme でプログラムを書いてきて、 ストレスを感じる事もあったでしょう。 たとえば、「一度計算した値をもう一度計算し直すなんで、馬鹿らしい」 と思った事はありません? C だったら、変数に代入してとっておけたのに、 なんて思うでしょう。 今回はそれを解決します。 お題は let と scoping rule です。

でも、let を使う前に、DrScheme では Beginner Mode から離れて、 Intermediate or Advanced Mode に移る必要があります。 モードの設定を変更してください。


同じ値を何回か使いたいんだけど?

まず、以下のような問題を考えてみましょう。

「ある変数 x y に対し、 x3 (= x * x * x) y3 (= y * y * y) を計算し、 x3 - y3 y3 の大きい方の値を返す関数 bar」

とりあえず以下の様に記述することで、求めたい関数を記述することは出来ます。

(define (bar x y)
    (max (- (* x x x) (* y y y))
	 (* y y y)))
但し、この場合 (* y y y) の計算を二度繰り返すことになります。 実際にプログラミングを行う場合は、 どのような計算が行われているかを意識しているわけで、 これはかなり問題です。

そんなときには、 let を使おう

Scheme では、この場合、一度計算した値を変数名に対応させて取っておくことができます。 let 構文を使います。 下の様な syntax を持っています。また、(var-i expr-i)節(clause)と呼びます。
	(let ((var-1  expr-1)
	      ....
              (var-n  expr-n))
             body)
expr-1,..., expr-n をそれぞれ評価し、その値に対してそれぞれ var-1,...,var-n という変数名を対応(束縛: binding) させます。 その上で、最後に body の評価をおこない body の値を let 式の値として返します。 その際、body の中に、変数 var-1 ,..., var-n が現われた場合、 その評価値は、さきほど束縛した値になります。

使用例は以下のとおり。

(define (bar2 x y)
    (let ((x3 (* x x x))
	  (y3 (* y y y)))
      (max (- x3 y3) y3)))

実は、 body の所には複数の式を並べて書くことが出来ます。 この場合の意味は (begin exprs) という式を書いていたときと同じです。

ところで、 let は代入と何が違うの? と思うかも知れません。 おおざっぱにいうと、 let は計算した値をいつでも使えるように名前(変数)を割り当てただけです。 (純粋な)関数型プログラミングでは、変数の値を後から変更する事はない、 つまり一度決まればそれっきりです。 一方、手続き型では変数は値を格納する場所で、入る値が途中で更新(代入)されたり するわけです。 詳しくは、次のページで説明。


束縛変数・自由変数

束縛変数(bounded variable)というのは、 式の中で束縛されている変数のことです。 逆に、束縛されていない変数のことを自由変数(free variable)といいます。 例を見ましょう。
    (let ((x 3))  (+ x y)) 
この式の中で x let によって束縛されています(つまり束縛変数である)が、 y はこの式の中では束縛されていません(つまり自由変数です)。

99.10.6/ Tomio KAMADA: kamada@cs.kobe-u.ac.jp