Schemeの課題 --- (6) 27-30

Copyright(C)1995,2006,2007 by 桜川貴司
Copyright(C)1998-2005 by 日置尋久

closure

関数(λ式)を評価すると,システムは関数本体とそれを評価するための環境を合わせたclosure(クロージャ)を生成する.関数の環境は,それが評価されたときの環境であり,例えばある局所変数のスコープの元で関数が評価された(そしてクロージャが生成された)場合,その局所変数は,評価時に捨てられてしまうのではなく,関数呼び出しによって,いつでも参照できる.

	==> (define x 0)
	==> (define incx (let ((x 0)) (lambda () (set! x (+ x 1)) x)))
	incx
	==> (incx) ;; let内の局所変数の値を増やす
	1
	==> (incx)
	2
	==> (incx)
	3
	==> x ;;大域変数xの値
	0

この場合,大域変数のxの値は変化していないことに注意.

        ==> (define new-counter 
               (lambda (initial)
                 (let ((x initial))
                   (lambda (method . a)
                     (cond ((eq? method 'get) x)
                           ((eq? method 'set) (set! x (car a)))
                           ((eq? method 'add) (set! x (+ x (car a))))
                           ((eq? method 'sub) (set! x (- x (car a))))
                           ((eq? method 'reset) (set! x initial)))))))
        new-counter
	==> (define c1 (new-counter 0))
  	==> (define c2 (new-counter 10))
  	==> (c1 'add 1)
	==> (c1 'get)
	1
	==> (c2 'sub 1)
	==> (c2 'get)
	9
	==> (c1 'reset)
	==> (c1 'get)
	0

c1,c2はそれぞれ独立して局所変数xを持っている.また局所変数xの対する操作は, 必ず引数methodで指定される内部の手続きを通して行われる. このように外部から独立したデータとそれを扱う手続きを持ち, 自律的に計算を行うものをオブジェクトと呼ぶ.

[kadai27opt]:選択問題(必ずしもやらなくてよい)

ある人のデータとそのデータを操作する手続きを合わせて,以下のようなpersonというオブジェクトを作ることを考える.

personのデータ:
名前(シンボル),年齢(整数),職業(シンボル),性別(シンボル),趣味(シンボルのリスト),友人(personのリスト)
personの手続き:
get-{name,age,occupation,sex,hobbies,friends} : それぞれののデータ をそのまま返す.get-friendsにより,personという種類のオブジェクトのリス トが返る.
get-friend-names : 友人の名前のリストを返す(シンボルのリストである)
set-{name,age,occupation,hobbies,friends} : それぞれのデータに新しい値を代入する
add-hobbies,delete-hobbies(趣味の追加と削除),add-friends,delete-friends(友達を増やす,減らす)

上の6つのデータをもらって新しいpersonを返す関数new-personを定義せよ.

	;; 他にpersonが定義されていない場合,友人のリストの初期値は空リストになる.
	==> (define honda (new-person 'taro '20 'student 'male '(tennis basket) '()))
	#<unspecified>
	==> (honda 'get-name)
	taro
	==> (honda 'set-age 21)
	21
	==> (honda 'get-hobbies)
	(tennis basketball)	
	==> (honda 'add-hobbies 'programming 'cardgame)
	(tennis basketball programming cardgame)
	==> (honda 'delete-hobbies 'tennis)	
	(basketball programming cardgame)
	==> (define williams (new-person 'ellen 19 'student 'female '() '()))
	#<unspecified>
	==> (define jordan (new-person 'eddie 20 'student 'male '() '()))
	#<unspecified>
	==> (honda 'add-friends williams jordan)
	#<unspecified>
	==> (honda 'get-friend-names)
	(ellen eddie)
[kadai28opt]:選択問題(必ずしもやらなくてよい)
personを引数にとって,友達の友達は皆友達というのを前提に,その全部の友人のつながりのリストを返す関数all-friendsを定義せよ.返すリストの中では,友人はその名前で表すものとする.ここで,次のような友達の関係があるとする.

	Aの友達がB,C
	Bの友達がD
	Cの友達はなし
	Dの友達がF
	Fの友達はなし

このときAの友達のつながりは,次のように表される.

	((A B) (A C) (B D) (D F))

基本的には,Aの友人のそれぞれについてその友人を調べ,またそれらの友人の友人を調べる,というように再帰的に友人を調べていけばよい.ただし友人関係に「BはAの友達,CはBの友達,AはCの友達」のようなループがある場合,単純な再帰呼び出しでは,無限に呼び出しが繰り返される.ここでは,上の例のように友人関係にはループがないものと仮定してよい.

余裕があれば,友人関係に「BはAの友達,CはBの友達,AはCの友達」のようなループがある場合に挑戦してみよ.


継続

[kadai29opt]:選択問題(必ずしもやらなくてよい)
kadai09(3)で定義した関数number-list?, すなわち,与えられたリストが0個以上の数値要素だけからなるリストであれば真(#t), そうでなければ偽(#f)を返す1引数関数を,継続を利用して大域脱出を行うように変更せよ.

[kadai30opt]
  1. 与えられた任意個のリストをつなぎ合わせる関数,すなわちappendを作成してみよ. 関数名はmyappendとする.継続を利用する必要はない.
  2. 大域変数resume-withを用意して,myappendの実行後に,(resume-with x)を実行することで,myappendを実行した結果にxをappendして,残りの計算を続けることができるようにmyappendを変更した関数myappend-contを作成せよ.
    	;; 動作例
    	==> (define resume-with '())
    	==> (myappend-cont '(a b) '(c d) '(e f))
    	(a b c d e f)
    	==> (resume-with '(g h i))
    	(a b c d e f g h i)
    	==> (define resume-with1 resume-with)
    	==> (myappend-cont '(1 2 3 4 5) '(6 7))
    	(1 2 3 4 5 6 7)
    	==> (resume-with '(8))
    	(1 2 3 4 5 6 7 8)
    	==> (resume-with1 '(8))
    	(a b c d e f 8)
    	==> (apply + (myappend-cont '(1 2) '(3 4)))
    	10
    	==> (resume-with '(8))
    	18
    	==> (resume-with '(8 9))
    	27
    
kadai23-kadai26のページへ