値としての関数

整数 a から b までの和を求めるプログラムを考えてみよう。

(define (sum-integers a b)
  (if (> a b)
      0
      (+ a (sum-integers (+ a 1) b))))

次に、

    π/8 = 1/(1・3) + 1/(5・7) + 1/(9・11) + … 

を利用して π/8 の近似値(bの大きさによる)を求めるプログラムを考えてみよう。

(define (pi-sum a b)
  (if (> a b)
      0
      (+ (/ 1 a (+ a 2)) (pi-sum (+ a 4) b))))

これらはいずれも次のような共通のパターンを持っている。ただし、上の二つの関数と照らし合わせてみると、(NEXT a)は基準となる整数aがどのように変化するかを表し、(TERM a)はそれぞれの項をそのaで表したものである。

(define (NAME a b)
  (if (> a b)
      0
      (+ (TERM a) (NAME (NEXT a) b))))

そこで、ひとつ共通の関数を作り、上のどちらにでも使えるようにしてみよう。

(define (sum term a next b)
  (if (> a b)
      0
      (+ (term a) (sum term (next a) next b))))

ここで、引数 term と next で受けとるものは関数(lambda(x) x)と関数(lambda (x) (+ x 1))である。

(define (sum-integers2 a b)
  (sum (lambda (x) x) a (lambda (x) (+ x 1)) b))
>(sum-integers2 1 4)
10
(define (pi-sum2 a b)
  (define (pi-term x) (/ 1 x (+ x 2)))
  (define (pi-next x) (+ x 4))
  (sum pi-term a pi-next b))
> (pi-sum2 1.0 1000.0)
.39244908194872286

関数 sum は、term と next を受けとって、新たな sum のバリエーション(関数)を返すもの、と見ることもできる。

そこで、次のようにしてみる。補助関数 loop の中で必要な term と next は loop の実行中に変化しないので、sum2 の引数を直接参照していることに注意。

(define (sum2 term next)
  (define (loop a b)
    (if (> a b)
        0
        (+ (term a) (loop (next a) b))))
  loop)

ここで sum2 は、中で定義された関数 loop をそのまま返している。

これを使うと、sum-integers は下のようになる。ここで、1 と 4 はloopの引数aとbに渡されている。

(define sum-integers3
  (sum2 (lambda (x) x) (lambda (x) (+ x 1))))
> (sum-integers3 1 4)
10

このように Scheme などの「関数型言語」では関数を引数として渡したり受け取ったりすることができる(高階関数)。


一覧 前へ 次へ