HOME もうひとつの Scheme 入門 書き込む

6. 局所変数


1. 初めに

前回までの説明で、一応関数を定義できるようになったと思います。 しかし、まだ、関数内で一時的な変数を定義する方法を説明してないので、プログラムが 冗長になってしまいます。そこで、今回は局所変数の定義の仕方について述べます。

2. let 式

局所変数は let 式を使って定義します。 書式は以下の通りです。
(let binds body )
binds の部分で、変数を宣言し、初期値を設定します。 body には任意の個数の式を書くことができます。
binds の部分の書式は、以下の通りです。
[binds] → ((p1 v1) (p2 v2) ...)
変数 p1, p2 ... を宣言し、初期値をそれぞれ v1, v2 ... にセットします。 変数の有効範囲は body の部分です。

例 1:局所変数 i, j を宣言し、初期値をそれぞれ 1, 2 にセットし、それらを足し合わせる。

> (let ((i 1) (j 2))
    (+ i j))
3

let 式は入れ子にすることができます。 例 2:局所変数 i, j を宣言し、初期値をそれぞれ 1, i+2 にセットし、それらを掛け合わせる。

> (let ((i 1))
    (let ((j (+ i 2)))
      (* i j)))
3
変数の有効範囲は定義の body だけですから、次のように書くとエラーになります。 これは、j の初期化式内に現れる i が定義されていないからです。
> (let ((i 1) (j (+ i 2)))
    (* i j))
reference to undefined identifier: i

 === context ===
D:\WBIN\MzScheme\collects\scheme\private\misc.ss:68:7
let* を使えば、直前に定義した変数を参照することができます。
> (let* ((i 1) (j (+ i 2)))
    (* i j))
3

例 3: 二次方程式の解を求める関数 quadric-equation
係数 a, b, c (a x2 + b x + c = 0) を引数にとり、解のリストを(実数の範囲)で返します。 let を使ってこまめに局所変数を定義すると無駄な計算をせずに解が求まります。

;;;変数 d,e,f の有効範囲はそれぞれの背景色の範囲です。

(define (quadric-equation a b c)
  (if (zero? a)      
      'error                                      ; 1
      (let ((d (- (* b b) (* 4 a c))))            ; 2
(if (negative? d) '() ; 3 (let ((e (/ b a -2))) ; 4
(if (zero? d) (list e) (let ((f (/ (sqrt d) a 2))) ; 5
(list (+ e f) (- e f)))))))))
> (quadric-equation 3 5 2)  ; solution of 3x2+5x+2=0
(-2/3 -1)
  1. もし、2次の係数 a が 0 なら、'error を返します。
  2. ゼロ以外なら判別式 (b2 - 4ac) を局所変数 d にセットします。
  3. もし、d が負なら、空リストを返します。
  4. そうでなければ、-b/2a を変数 e とします。
  5. d がゼロなら重根なので、e のリストを作って返します。
  6. d が正なら、√d/2a を f にセットし、(+ e f), (- e f) のリストを作って返します。

実は letlambda 式を使って次のように表されます。

(let ((p1 v1) (p2 v2) ...) exp1 exp2 ...)
⇒
((lambda (p1 p2 ...)
    exp1 exp2 ...) v1 v2)
lambda 式は関数なので、変数の有効範囲を制限する働きがあります。

練習問題 1

Scheme 入門 4 の練習問題を1つの関数で書いてみてください。
つまり、初速度 v, 角度 a 度で投げたボールの飛ぶ距離を求める関数を書いてください。

2. 終わりに

今回は、局所変数を定義する let 式について解説しました。 let 式は実は lambda 式の構文糖衣です。 let 式や lambda 式を使うと、変数の有効範囲が制限されます。 Scheme では、この制限が、ソースコードに書いてある通りに起こります。 これをレキシカルクロージャと呼びます。

次回は繰り返しについて説明します。繰り返しができればとりあえず、普通にプログラムが書けるようになります。

練習問題の解答

練習問題 1

(define (throw v a)
  (let ((r (/ (* 4 a (atan 1.0)) 180)))
    (/ (* 2 v v (cos r) (sin r)) 9.8)))