3. その結果を表示する
3.8 手続き
> (eq? (list 'pooh 'piglet) (list 'pooh 'piglet))
#f
> (define animal (list 'pooh 'piglet))
#<unspecified>
> (eqv? animal animal)
#t
> (eq? '() '())
#t
手続きequal? は、対やベクトルなどの比較のときには、要素それぞれを比較しますが 、
さらに対やベクトルがあればそれらの要素についても、さらに等しいかど うかを調べます。
(
ベクトルは、6.1.2
にて学びます。)
その他(
数や記号など)
に対しては、eqv?と同じ方法で同じかど うかを調べます。
(
直観的な説明ですが 、2
つのデータが同じに表示されるとき は、equal? は #tを返す場合が多いです。)
> (equal? '(a b) '(a b))
#t
> (equal? (list 'a 'b) (list 'a 'b))
#t
> (equal? '(a b (c d)) '(a b (c d)))
#t
> (equal? "my home" "my home")
#t
> (equal? 13 13)
#t
> (equal? 3.14 3.14)
#t
> (equal? 1 1.0)
#f
の例では引数に
1
を加えた数を返す手続き add1を定義しています。> (define (add1 n) (+ n 1))
#<unspecified>
|
defineが返す値> (add1 1) 2
> (add1 99) 100
手続き呼び出しで与えられた
ひきすう
引数 のことを、
じつひきすう
実引数
(actual argument)
といいます。上での (add1 1) の場合だと 1が実引数です。手続き add1 の定義の中での nは、add1が 呼び出されたときの実引数の値が入れられ 、add1の本体部分である (+ n 1) が実行され ます。nは手続きadd1の
かりひきすう
仮引数
(formal argument)
と呼ばれます。nが実引数の値を持つのは、手続き add1の内部だけです。add1 の外で nが値を持って いてもadd1の中では実引数の値が優先され 、外での n の値は隠されてます。nが実引数 の値を持つのは手続き add1の内部だけで、add1 の実行が終了したら元の値に戻ります。
> (define n 10)
#<unspecified>
> n 10
> (add1 1) 2
> n 10
この例では、まず変数n の値を 10 にしています。そして手続き add1を呼び出していま すが 、nの値を 10 にしたことは影響していません。add1 の実行が終了したのちに変数n の値を調べていますが 、add1の実行によって値が 1になっておらず、前のままの 10であ ることがわかります。
次は、
2
つの引数を持った手続きの例です。> (define (distance x y) (sqrt (+ (* x x) (* y y))))
#<unspecified>
> (distance 1 1) +1.414213562373095
> (distance 2 4) +4.47213595499958
一般的に手続き定義は次の形をしています。
(define (h手続き名i h仮引数のならびi)
h式1i
h式2i
...
h式
n
i )ここで h手続き名i は変数で 、h仮引数のならび i は変数を任意個ならべたものです。仮 引数のならびには、同じ名前の変数が現れてはいけません。
手続き呼び出しは、 一般的には次の形をしています。
(h手続き名i h実引数のならびi)
以前に出てきた変数の定義では(define count 0)という書き方をしていました。手続 き定義の場合では、定義する手続き名と仮引数が括弧で囲まれていて、同じ define なの に変数のときと使い方が違っています。上で紹介した手続き定義の方法は、実は簡略記法 です。簡略でない記法で add1の定義を書くと次のようになります。
(define add1
(lambda (n) (+ n 1)))
ここで (lambda ) は 、ラムダ式
( expression)
と呼ばれるものです。これが評価されると 手続き
(procedure)
という型のデータが返されます。これでわかるように、手続き定義は変数定義とまったく同じです。定義する変数の値が手続きデータであるというだけ です。
} (lambda h仮引数i h本体i)
|
h仮引数i をパラメータとして持ち、本体の式のならびがh本体i である手続き を作ります。} (procedure? hデータi)
|
hデータiが手続きデータなら真#t を、そうでなければ偽 #fを返します。ラムダ式は名前のない手続きともみなせます。たとえば与えられた数に
1
を加える手続きは
(lambda (n) (+ n 1))
と表せます。ですので
1
を加えた数を求めるのに 、define を使って新しい手続きを定義 する必要はありません。> ((lambda (n) (+ n 1)) 10) 11
この例ではまず(lambda (n) (+ n 1))が評価されて、「引数に
1
を加える手続きデータ」が作られます。次に引数が評価されて 10 を得ます。最後に 10 が手続きデータに渡され て、実行されます。
こんどは次の場合を考えてみましょう。
> (add1 10) 11
この場合も上の場合と同じです。まず add1が評価されます。この変数の値は、「引数に
1
を加える手続きデータ」です。次に引数が評価されて 10 を得ます。最後に 10 が手続き データに渡されて実行されます。
手続きもひとつのデータなので 、手続きを他の手続きに引数として与えることもでき ます。
> (define (calc x op y) (op x y))
#<unspecified>
> (calc 3 + 4) 7
> (calc 2 - 8) -6
> (calc 3 * 6) 18
+
,
-,
*はそれぞれ 、「加算をする手続き」、「減算をする手続き」、「乗算をする手続き」を 値として持っています。手続き calc の中で op は 、引数で与えられた手続きデータを値 として持っています。式(op x y)の評価は、opが値として持っている手続きデータに x と yを渡して実行するようになっていて、その結果が calc の値となっています。これまで説明したラムダ式の引数は、(x op y)のような変数のリストで、取り得る「引 数の数」は決まっていました。そのために、式 (calc 2)を評価しようとすると、引数の 数が異なるためにエラーとなります
:
> (calc 2)
Error: Wrong number of args to ...
in excpression: (... calc 2) in top level environment.
; Evaluation took 50msec ...
たとえば足し算をする手続き+ は、任意の数の引数をとることができます。このように あるときの呼び出しでは
2
つの引数で、またあるときの呼び出しでは4
つの引数で、というように、手続き呼び出しのたびに引数の数を変える方法を紹介します。
任意の数の引数を手続きに渡すのに、(foo (list a b c d e))として引数をすべて
1
つのリスト入れるのもひとつの方法です。すると手続き fooは、
1
引数の手続きとして作ればよいことになります。ですがいちいちリストにまとめるのも大変です。
手続きが可変数の引数を受けとるには、次のようにします。
(define h手続き名i (lambda h引数名i
h式のならび i ))
ここで h引数名iは記号のリストではなく、ひとつの記号であることに注意して下さい。
たとえば (define foo
(lambda s
(if (null? s) '()
(list-ref s (- (length s) 1)))))
は、fooの最後の引数を返す手続きです。(foo 1 2 3 4)の評価において仮引数sは、値
(1 2 3 4) を持つことになります。
> (foo 1 2) 2
> (foo 1 2 3 4 5) 5
> (foo) ()