第9回 高階関数(6月11日;6月12日)

今日の課題

□ 前回のプリントの続き

数を要素とするリストを入力し、合計値を返す関数を作りたい。この関数を作るには、2つの方法がある。

この2つめの方法のように、最後の結果がそのまま返り値となる再帰呼出しをしている関数は、末尾再帰関数と言われている。末尾再帰関数は、プログラムを書くときは再帰的であるが、Schemeでの実行時の処理は再帰処理ではなく繰り返し処理となる。

練習1

sum-a と sum-b の関数をそれぞれ作成せよ。実行方法は次の通りである。
> (sum-a (list 1 2 3 4))
10
> (sum-b (list 1 2 3 4) 0)
10

■ 引数に関数をとる関数

引数に関数をとる関数は、高階関数の一種である。コンビネーションの最左部分式に、関数の代入された引数名を記述することで、その関数を使用できる。

例1

数列の和を求めるΣと、数列の積を求めるΠとは、数列に足し算をするのか、掛け算をするのかという違いである。そこで、計算方法(関数)、初期値、および、数列(数を要素とするリスト)を引数にとり、計算方法や初期値を変更するだけでΣやΠの計算ができる関数 inject を作ってみよう。
(define inject
  (lambda (func rslt lst)
    (if (null? lst)
        rslt
        (inject func (func rslt (car lst)) (cdr lst)))))

; Σ としての実行例
(inject + 0 (list 1 2 3 4))

; Π としての実行例
(inject * 1 (list 1 2 3 4))

下線部にてコンビネーションが記述されており、最左部分式に関数である引数 func が配置されている。

練習2

関数 func、および、リスト lst を引数にとる関数 collect を作成せよ。ここで、func は、引数を1つとるものとする。collectは、lst の各要素を func で計算し、その結果を格納したリストを返すこととする。たとえば、(define twice (lambda (x) (* x 2))) と定義されているとき、(collect twice (list 1 2 3 4)) は (2 4 6 8) を返す。

⇒答えはここ

■ 返り値に関数をとる関数

返り値に関数をとる関数も、高階関数の一種である。ラムダ式において最後の式の評価結果が関数である場合である。

例2

選択肢を入力し、対応する関数を返す関数 op4 を作成しよう。選択肢には、'add、'sub、'multi、'div があり、それぞれ、+、-、*、/ という演算子を返すものとする。
(define op4
  (lambda (s)
    (cond ((equal? s 'add) +)
          ((equal? s 'sub) -)
          ((equal? s 'multi) *)
          ((equal? s 'div) /))))

; 実行例
((op4 'add) 1 2 3 4)   ; (+ 1 2 3 4) と同じ
((op4 ') 1 2 3 4)   ; (+ 1 2 3 4) と同じ

練習3

面積の計算式を返す関数 size-fig を作成せよ。size-fig は、引数を1つとり、以下のように振舞う。

動作確認は次のように行う。
> (size-fig 'circle)
#<procedure>   ..... 関数が返されていることがわかる。
> ((size-fig 'circle) 4)
50.265472 .... π = 3.141592 とした場合
> ((size-fig 'triangle) 4 5)
10
> ((size-fig 'rectangle) 4 5)
20

ヒント:例2では、返り値に演算子を配置したが、練習3では、lambda 式を配置する。

■小レポート

下記のプログラムおよび実行結果に手書きでコメントを添えて作成せよ。

1. 再帰関数の練習問題

1-1) 二つの正の整数 n, m を入力すると、n 以上 m 以下の全ての整数を昇順に並べたリストを作成する関数 (list-numbers n m) を作成せよ。

1-2) リスト lst の n 番目の要素を返す関数 (list-refer lst n) を作成せよ。なお、リストの先頭要素を 1 番目とする。

1-3) リスト lst の n 番目の要素を d に置き換えたリストを返す関数 (list-set lst n d) を作成せよ。

2. 高階関数の練習問題

2-1) 平均値を求める関数を作ろう。まず、相加平均を a-mean、相乗平均を g-mean、調和平均を h-mean という関数名でそれぞれ作成せよ。ただし、例1で作成した inject を用いること。ここで、リストlstの要素数は (length lst) で求まる。xn乗根は (expt x (/ 1 n)) で求まる。

2-2) 次に、平均の計算方法と数値の列で構成されるリストを要素とするリストを入力として、各リストを指定通りに計算し、全結果をまとめたリストを返す関数 list-mean を作成せよ。ただし、練習2で作成した collect を用いること。

実行結果には、少なくとも、以下の動作結果を含めること。
> (list-refer (list-numberlist-numbers 2 5) 2)
3
> (list-set (list-numberlist-numbers 2 5) 2 6)
(2 6 4 5)
> (a-mean (list 2 3 4 5))
3 1/2   ← 3と2分の1
> (g-mean (list 2 3 4 5))
3.309750919646873
> (h-mean (list 2 3 4 5))
3 9/77  ← 3と77分の9
> (list-mean
       (list (list a-mean 2 3 4 5)
             (list g-mean 2 3 4 5)
	     (list h-mean 2 3 4 5)))
(3 1/2  3.309750919646873  3 9/77) 
  ↑ 3 1/2 と 3 9/77 は帯分数表示

□ 休講と補講のお知らせ

6月18日(木)、19日(金)の情報工学演習3は休講です。18日(木)のぶんは、6月23日(火)2限目に補講します。19日(金)のぶんは、今から相談して決めます。


(c) 2009.6.9 by tokuhisa