define



2. 名前を付ける

2.1. 算数の問題

ちょっとした算数の問題を考えてみましょう.

兄の太郎くんの大きなマグカップは,内法で,直径が10cm 高さが10cmの円筒形です. 一郎くんのマグカップの容量は何cc でしょう.円周率を3.14としましょう.

円筒の体積の公式は,

体積 = 底面積×高さ

底面積 = 半径×半径×円周率

半径 = 直径/2

ですからまず,半径を求めましょう.

gosh> (/ 10 2)
5

次に,底面積を求めましょう.

gosh> (* 5 5 3.14)
78.5

最後に容量です.

gosh> (* 78.5 10)
785.0

うわっ.でかい! ワイン一本がはいっていましますねぇ.

2.1.1. 値に名前をつける

さて,円周率や直径,高さの値を数字でいちいち覚えるのは面倒ですよね. 名前が付けられると覚えやすいですね.Scheme では,define をつかって 値に名前を付けることができます.たとえば,円周率 3.14 に pi という 名前をつけましょう.

gosh> (define pi 3.14)
pi

pi という名前のついた値を知りたければ,単に pi と書けばよいのです.

gosh> pi
3.14

直径と,高さにも名前をつけておきましょう.

gosh> (define d 10)
d
gosh> d
10
gosh> (define h 10)
h
gosh> h
10

2.1.2. 式に名前をつける

半径は,r にしましょう.Scheme では式にも名前がつけられます.

gosh> (define r (/ d 2))
r
gosh> r
5

これは式に名前をつけてはいますが,実際にはその式を計算(評価)して 得られた「値」に名前をつけているのと同じです.

こうして名前をつけておくと,底面積は,名前 r と pi を使ってあらわせますね. そして,その底面積も a とい名前を付けておきましょう.

gosh> (define a (* r r pi))
a
gosh> a
78.5

最後に容積にも taro-mag という名前を付けましょう.

gosh> (define taro-mag (* a h))
taro-mag
gosh> taro-mag
785.0

2.2. 関数

さて,太郎くんの大きなマグカップの容量はわかりましたね. 何 cc ですか?また,計算しなくていもいいですよね.taro-mag という名前を 付けましたから,taro-mag を評価してみればいいですね.

gosh> taro-mag
785.0

さて,太郎くんには,花子ちゃんという妹がいて,やっぱり大きなマグカップを もっています.でも,太郎くんのよりは小さいようです.

花子ちゃんのマグカップは,内法で,直径が8cm 高さが9cmの円筒形です. 花子ちゃんのマグカップの容量は何cc でしょう.円周率を3.14としましょう.

2.2.1 関数の定義

さきほどのような方法で,花子ちゃんのマグカップの容量を計算して,それに hanako-mag という名前をつけてもいいですが...きっと,次は,花子の弟が でてきそうですね.

そこで,すこし整理しましょう.「円周率」は太郎くんのときと同じです. 「直径」の方はというとこれは太郎くんのとは変化しています.また, 「高さ」も変化していますね.

「直径」の変化は何に影響しているでしょう.そうです.「半径」ですね. つまり,「半径」は直径によってその長さがかわります.そこで,「直径」から 「半径」を求める関数というものを考えます.「直径」をもらうと,それに 対応する「半径」返す仕組みです.どうすればいいでしょう.「直径」d が与えられたら,それを半分にした値を返す関数ですね.これは

(lambda (d) (/ d 2))

と書きます.これをλ(ラムダ)式といいます. このλ式をすこし説明しましょう.lambda のとなりにあるリスト (d) は 仮引数リスト「仮」というのは,関数内部で,参照する引数の「名前」である ことを意味します.(対応する言葉に「実引数」という言葉がありますが, これは,実際に関数を使うときに関数の外から与える実際の「値」のことです.) ここでは,仮引数が d のひとつだけであることがわかります. これが外から与えるデータです.仮引数リストのあとにあるのが,このλ式の 本体部分です.

実際に使うときには,(関数 引数)と書きます. たとえば,直径15(cm)のものの半径を求めてみましょう.

gosh> ((lambda (d) (/ d 2)) 15)
7.5

この場合,15 が実引数です.このとき,どのような計算が行われるかというと, 仮引数 d が実引数 15 に束縛されます.つまり,d = 15 になります. こうしておいて,本体部分 (/ d 2) が計算され,それが結果として返ります.

2.2.2. 関数に名前を付ける

いちいち,(lambda (d) (/ d 2)) と書くのは面倒なので,この式に簡単で 覚えやすい名前を付けましょう.たとえば,d2r としましょう.

gosh> (define d2r (lambda (d) (/ d 2)))
d2r

さて,もうひとつ,半径から円形の底面積を求める関数はどうなるでしょう. 名前は circle-area とでもしておきましょう.

gosh> (define circle-area (lambda (r) (* r r pi)))
circle-area

で,いいでしょう. 最初にもどって,底面の直径と,高さが与えられたら,マグカップの容量を計算 するという関数は,以下のように書きます.

(lambda (d h)
  (let* ((r (d2r d))
         (a (circle-area r))
         (v (* a h)))
    v))

となります.あれっ.見なれない,let* なんてのが出てきましたね.let* は 関数や define の定義の中だけで有効な名前を付けるための特別な構文です.

    (r (d2r d))
    (a (circle-area r))
    (v (* a h))

の部分はトップレベルで

(define r (d2r d))
(define a (circle-area r))
(define v (* a h))

と書くのに対応しています.let*構文は一般的には,

(let* ((v1 expr1)
       (v2 expr2)
       ...
      )
  body)

という形式になっており,expr1 の値に v1 という名前を,expr2 の値に v2 という名前を...つけた上で,body を計算した結果がこの let* 式の値に なります.

さて,底面の直径と高さから,マグカップの容積を求める関数に mag-cup という 名前を付けておきましょう.

(define mag-cup 
  (lambda (d h)
    (let* ((r (d2r d))
           (a (circle-area r))
           (v (* a h)))
      v)))

2.2.3. 関数の使い方

さて,これで,mag-cup という名前の関数ができました.どうやって使うのでしょう. これを使って,花子ちゃんのマグカップの容量を計算してみましょう.

gosh> (mag-cup 8 9)
452.16

おっ.結構はいりますねぇ.

このように,関数を使うときは,関数名 引数 ... と並べて括弧でかこみます. これは,前回でてきた四則演算子や string-append などの関数の使い方と同じです.

2.3. 関数定義のもうひとつの書法

Scheme では,

(define mag-cup 
  (lambda (d h)
    (let* ((r (d2r d))
           (a (circle-area r))
           (v (* a h)))
      v)))

と書くかわりに,

(define (mag-cup d h)
  (let* ((r (d2r d))
         (a (circle-area r))
         (v (* a h)))
    v))

のように省略して書くことができます.

2.4 練習問題

問題 2.1.

次の図形の表面積や体積の公式を関数で表現しなさい.

  1. 球の表面積 (引数:半径)
  2. 球の体積 (引数:半径)
  3. 円柱の表面積 (引数:底辺の半径,高さ)

解答

2.5 ちょっと小難しい用語

関数抽象(λ抽象)

共通の計算手順を、入力から出力への変換として抽象化して表現すること。

関数適用(Function Application)

関数に実際の入力データを与えて、出力を計算させること。

Powered by Kahua