情報科学概論IIA 電子・情報工学系 新城 靖 <yas@is.tsukuba.ac.jp>
このページは、次の URL にあります。
http://www.hlla.is.tsukuba.ac.jp/~yas/coins/Joka2a-1998/1998-06-23
あるいは、次のページから手繰っていくこともできます。
http://www.hlla.is.tsukuba.ac.jp/~yas/coins/
http://www.hlla.is.tsukuba.ac.jp/~yas/index-j.html
先週の課題(教科書84ページ〜86ページの「8.3演習問題」)のチェック・ ポイント
define や if では適当に改行してください。横幅80桁で見やすいように。
(define (f x) (if ... xxx ...))括弧の対応がとれていない状態で途中でリターンを打っても大丈夫です。
再帰のプログラムでは、自分自身を信じること。自分が完成したと思って、 (f (car x)) とか (f (cdr x)) のように呼び出します。
そして、それができたと思って、1ステップ進める手続きを かきます。後は、再帰が止る所の条件を考えます。
線形再帰と末尾再帰に注意。教科書の例題の多くは、線形再帰だが、末尾再帰 にならないもの。
末尾再帰ではない線形再帰 (define (f x y) ..... (cons ..... (f (cdr ... ) ... )))
末尾再帰 (define (f x y) ..... (f ..... (cons (cdr ... ) ... )))末尾再帰ではない線形再帰の例。 教科書56ページのcopy参照。
(define (copy x) (if (null? x) '() (cons (car x) (copy (cdr x)))))
◆let
先週の話を参考。◆myappend(破壊的代入を使わない)
myappend の考え方ですが、「x の cdr と y を myappend したものに xの car を consする」です。x を car, cdr でばらしていくと、そのうちに x が null? になる時がきます。その時の対応は、(myappend '() ' (a b c)) の結 果は、何になるべきか、'() と何かを myappend すると、何になるべきかを考 えます。myappend の中で 標準の append を使ってはいけません。
myappend では、普通は、revserse は使いません。
myappend は、線形再帰(教科書56ページ)ですが、末尾再帰(教科書57ペー ジ)には普通は、なりません。
myappend では、結局、後ろの方の y は、あまりいじらなくてもいいわけです。 逆に y をいじって、x をそのままという考え方もできなくはないですが、こ れはだめです。リストの性質上、car とcdr が対称形でないので。
(myappend '() '(a b c)) が動かない回答がたくさんありました。 (myappend '() なんとか) は、「なんとか」をそのまま返せばいいです。 car, cdr をとる前に、null? のチェック、または pair? のチェックを するように。
◆myappend!(破壊的代入を使うmyappend)
考え方としては、最後の cdr が '() であるようなconsセルを探して、その cdr 部分を set-cdr! で y にします。最後のconsセルを探す手続きを別に書 くと簡単になります。
リターン・バリューは、別途考えます。 下の begin 参照。
set! は、define や let の変数の束縛を変えるものです。myappend! では、 consセルの内容を書き換える必要があるので、set-cdr! を使います。
myappend! では、cons は使わないはずです。revserse も不可。reverse の中 で cons を使っているので。破壊的代入を使うものは、myappend 以外のもの でも cons を使いません。
◆begin
set!, set-car!, set-cdr! は、値を返しません。値を返すには、begin を使 うといいでしょう。(begin (set-cdr! ....) (set-cdr! ....) 10 )こうすると、最後の式(この例では10)が値として返ります。begin は、if の then, else 部分を書く時につかいます。define や lambda のトップレベルで は、begin を省略してもいいようです。(define (f x) (if ... ) (set! .... ) x)最後に x の値を返す例。
◆set!とset-cdr!の使い分け
set! は、define や let の変数の「束縛」を変えるものです。 cons セルの内容を変える時には、set-cdr! や set-car! を使います。リスト操作で、破壊的代入を使うものは、set-car!, set-cdr! を使います。
do ループなどで、let の変数の値を変える時には、set! を使います。define で定義された変数(特に大域変数)の値を変えるには、set! を使います。
◆insert
(insert n x y) とは、x の cdr の n-1 番目に y を insert した ものと x の car を cons したもの。n=1 の時には、y と x cons 。◆insert(破壊的代入)
たぶん、consセルを一つは増やさないと、挿入はできないでしょう。◆delete
(delete x y) とは、まず y の cdr から x を delete したもの(これをzと置 く)と、y の car (これを y1とおく)を考え、y1 が x と eq? なら、y1 を z とcons したもので、eq? でないなら、単に z である。(define (delete x y) .... (let ((y1 (car y)) (z (delete (cdr y)))) (if .... (cons y1 z) z ))) let に行く前に、(car y) に備えて、null? とか pair? で条件を跳ねること。◆subst
(subst a b x) とは、まず、x の cdr について a を b で substすることを してそれと x の car をなんとかする。◆myreverse
教科書58ページににもあるように、append を使わなくてもでき ます。この問題では、append を使うようにというヒントが出ていますが、こ れは次のように考えてなさいという意味です。(myreverse x) とは、x の cdr 部分を myreverse したものとx の car 部分を、なんとかしてリストにしたものを append する。
「なんとかしてリストにする」には、list 手続きか cons を使います。
◆random-number
(random-number) という手続きは、引数はありません。実行するたびに、違う 値を返します。下の (counter) では、呼び出すたびに、結果が1つずつ増え ていきますが、(random-number)では、呼び出すたびにまったくでたらめに見 える数が返されます。
n番目の乱数を作るのは、直前に作った乱数(n-1番目の乱数)に、教科書にある ような計算をして、作ります。ですから、一つの前の乱数をどこかに覚えてお く必要があります。記憶場所としては、しばしば大域変数が使われますが、 がんばれば局所変数でも可能です。 しかも、呼ばれるたびにその内容を更新する必要がありま す。この更新を、set! などを使ってやります。
乱数を生成する時には、教科書の式で pやaは、慎重に選ぶ必要があります。 そうでないと、よい疑似乱数は得られません。よい乱数とは、髭が長く(繰り 返さない部分が大きい)、周期が長い(繰り返したとしても、周期が長い)も のです。
コンピュータで作る疑似乱数は、かならず繰り返します。
◆counter
先週示したカウンタの例です。乱数を作る時に参考にしてください。(define n 0) (define (counter) (set! n (+ n 1)) n)最後に n と書いてあるので、n の値が返されます。(上の begin の所で説明 したように、define や lambda のトップレベルでは、begin を省略してもよ い。)
大域変数を使わない、ちょっとおしゃれなカウンタ。
(define counter (let ((n 0)) (lambda () (set! n (+ n 1)) n)))■今日の課題
教科書84ページ〜86ページの「8.3演習問題」や、教科書89ページ〜91ペー ジの「9.3演習問題」や、の練習問題のいくつかをやりなさい。 時間内にできたものについて、プログラムと実行結果を次のような電子メール で送りなさい。To: yas Subject: [joka2a] enshuu-8.3 enshuu-9.3
◆sq-list(mapの例)
sq-list は、引数として数のリストを取り、結果としてそれぞれの数を2乗し たリストを返す手続きである。 普通の定義では、次のようになる。
(define (sq-list l) (define (square x) (* x x)) (if (null? l) () (cons (square (car l)) (sq-list (cdr l)))))末尾再帰ではない線形再帰。(sq-list l) とは、まず (sq-list (cdr l)) を 計算して、(car l) の square を cons する。map を使うと、次のようになる。
(define (sq-list l) (define (square x) (* x x)) (map square l))square を define するのがめんどうな時には、map の引数に lambda を直接 書く。
(define (sq-list l) (map (lambda (x) (* x x)) l))◆filter2
実は、4. の filter2 があれば、1.〜3. は、簡単。(define (filter i l) (define (is-baisuu-i x) (= (modulo x i) 0)) (filter2 is-baisuu-i l))このような回答は、レポートでのカウントは、1。注意:23日に示した時には、間違いがありました。
誤: (define (is-baisuu-i x) (modulo x i))正: (define (is-baisuu-i x) (= (modulo x i) 0))◆filter
1. の filter のプログラムは、普通は、map を使わないで、上の最初の sq-list のように、(末尾再帰ではない)線形再帰で書きます。map が使えな い理由は、map では、要素の数が変わらないからです。この場合、filter2 が map に相当します。
Last updated: 1998/06/30 11:19:49
Yasushi Shinjo / <yas@is.tsukuba.ac.jp>