ラベル the little schemer の投稿を表示しています。 すべての投稿を表示
ラベル the little schemer の投稿を表示しています。 すべての投稿を表示

2011年7月26日火曜日

Scheme手習い Chapter 9



気付き感想等




  • 全関数と部分関数


    • keep-lookingはlatの一部分に対して再帰しているのではない


      • 不自然な再帰

      • 部分関数

      • これまでに見てきた関数は全関数





  • ゴールに到達しない関数でもっと短いもの


    • (define eternity (lambda (x) (eternity x)))

    • いつまでたっても終わらない、最も不自然な再帰で、最も部分的な関数



  • alignは第7の戒律に違反している


    • 第2の質問(a-pair? (first para))の後、alignに(shift para)の値を渡している。

    • 第7の戒律





「性質を同じくするすべての構成部分について再帰すべし。すなわち、
*リストのすべての部分リストについて
*算術式のすべての部分式について」




Yコンビネータ


9章の最後に来てYコンビネータ。


手順を追って書いていく。



;; defineがなかったらlengthは定義出来るか?

;; 空リストの長さを決めるが他に何も決めない。
;; 名前を付けるとしたらlength0
(lambda (l)
(cond ((null? l) 0)
(else (add1 (eternity (cdr l))))))

;; 要素が1以下のリストの長さを決めるlength<=1はこんな感じ
(lambda (l)
(cond ((null? l) 0)
(else (add1 (length0 (cdr l))))))

;; length0の名前を付けられない。
;; length<=1の中のlength0の部分をlength0の定義で置き換え
(lambda (l)
(cond ((null? l) 0)
(else (add1 ((lambda (l)
(cond ((null? l) 0)
(else (add1 (eternity (cdr l))))))
(cdr l))))))

;; もしもlength<=3、length<=4、length<=5のような形で無限の関数を書ければlength∞が書ける。
;; それは無理なのでパターンを探す。
;; lengthに似た関数があるので、それを抽象化する。
;; length0を作る関数を書く
((lambda (length)
(lambda (l)
(cond ((null? l) 0)
(else (add1 (length (cdr l)))))))
eternity)

;; 同じようにlength<=1
((lambda (length)
(lambda (l)
(cond ((null? l) 0)
(else (add1 (length (cdr l)))))))
((lambda (length)
(lambda (l)
(cond ((null? l) 0)
(else (add1 (length (cdr l)))))))
eternity))

;; length<=2
((lambda (length)
(lambda (l)
(cond ((null? l) 0)
(else (add1 (length (cdr l)))))))
((lambda (length)
(lambda (l)
(cond ((null? l) 0)
(else (add1 (length (cdr l)))))))
((lambda (length)
(lambda (l)
(cond ((null? l) 0)
(else (add1 (length (cdr l)))))))
eternity)))

;; まだ繰り返しがある。lengthに似た関数を返す関数に名前をつける。
;; length0
((lambda (mk-length)
(mk-length eternity))
(lambda (length)
(lambda (l)
(cond ((null? l) 0)
(else (add1 (length (cdr l))))))))

;; length<=1
((lambda (mk-length)
(mk-length (mk-length eternity)))
(lambda (length)
(lambda (l)
(cond ((null? l) 0)
(else (add1 (length (cdr l))))))))

;; length<=2
((lambda (mk-length)
(mk-length (mk-length (mk-length eternity))))
(lambda (length)
(lambda (l)
(cond ((null? l) 0)
(else (add1 (length (cdr l))))))))

;; 再帰とは無限にmk-lengthの適用が続いてるようなもの
;; eternityを渡さないでも誰も気にしない。
((lambda (mk-length)
(mk-length mk-length))
(lambda (mk-length)
(lambda (l)
(cond ((null? l) 0)
(else (add1 (mk-length (cdr l))))))))

;; lengthと似た関数を作る関数を1回適用するとlength<=1が出来る。
((lambda (mk-length)
(mk-length mk-length))
(lambda (mk-length)
(lambda (l)
(cond ((null? l) 0)
(else (add1 ((mk-length eternity) (cdr l))))))))

;; mk-length自身を渡す事で再帰が出来る(!)
((lambda (mk-length)
(mk-length mk-length))
(lambda (mk-length)
(lambda (l)
(cond ((null? l) 0)
(else (add1 ((mk-length mk-length) (cdr l))))))))

;; mk-lengthの部分を取り出して、それにlengthという名前を付ける事が出来る。
;; だけど引数に(mk-length mk-length)を渡すと引数を評価してるとこで再帰する。
;; なのでlambdaで包んで評価しないようにしてみる。
((lambda (mk-length)
(mk-length mk-length))
(lambda (mk-length)
((lambda (length)
(lambda (l)
(cond ((null? l) 0)
(else (add1 (length (cdr l)))))))
(lambda (x)
((mk-length mk-length) x)))))

;; 後はlengthの部分だけを取り出す
((lambda (le)
((lambda (mk-length)
(mk-length mk-length))
(lambda (mk-length)
(le
(lambda (x)
((mk-length mk-length) x))))))
(lambda (length)
(lambda (l)
(cond ((null? l) 0)
(else (add1 (length (cdr l))))))))

;; このlengthを生成してる関数はYコンビネータというらしい。
(define Y
(lambda (le)
((lambda (f)
(f f))
(lambda (f)
(le (lambda (x) ((f f) x)))))))


すっきり。





2011年6月29日水曜日

Scheme手習い Chapter 8



さて、いよいよ後半になって難しくなってきた。


8章ではカリー化、収集子(継続とも呼ばれる)、収集子を渡すスタイルの関数の書き方。


第10の掟「同時に2つ以上の値を集める際には関数を作るべし。」が出てきた。


自分なりまとめ




  • 関数はリストとアトムを返せる


    • 関数自身を返す事も出来る


      • これは関数が第一級オブジェクトだから?

      • 第一級ってどんな事かっていうと


        • 関数を引数にとれる

        • 関数を変数に代入出来る

        • 関数に名前をつける必要がない



      • よく聞く関数が第一級市民というのはWikipedia見ると『この言葉は1960年代にChristopher Stracheyによって「functions as first-class citizens」という文脈で初めて使われた。』らしいのでそれから来てるのかな。





  • カリー化


    • 『カリー化 (currying) とは、計算機科学分野の技法の一つ。複数の引数をとる関数を、引数が「もとの関数の最初の引数」で戻り値が「もとの関数の残りの引数を取り結果を返す関数」であるような関数にすること。』(Wikipediaより)




気付きとか




  • func.scmが700行越えてきて見通し悪い


    • なんか前もこんな事書いた気がする


      • 1章1ファイルにまとめるとすっきりしそう

      • テストも1つのファイルにまとめられたりするかな?





  • insertR-fとinsertL-fの内側のlambdaの引数の名前


    • insertRやinsertLのときは(new old lat)だったのが(new old l)になってる。

    • コピペでinsertR-fとinsertL-fを作ったのでlatのままにしてたのに気付き、後から修正 orz

    • test?を取るのでlat以外の構造も比較出来る。だからlatじゃなくてl、なのかな?

    • 引数の名前付けって大事っすね…

    • でもinsert-gだとeq?で比較してるのにlだ。latじゃないのかな…謎。



  • 前に定義した関数を定義しなおした時ってテストどうしよ


    • とりあえず1.scmから順番に全部通るのを確認してみてる



  • こないだ作ったf-testマクロの都合で関数を返す関数のテストが出来ないかも

  • 無名関数を渡すようにすると覚えておく名前が少なくてすむ(p135)便利!

  • 関数を取って関数を返す関数は怖くない!

  • multirember&coみたいに、名前に&がつくのには、最後に?がつくのは述語!のように決まりがあるのだろうか

  • 継続渡しスタイル面白い!

  • even?の定義


    • 自分で作ったo+ o* o/を使わずにSchemeに元からある+ * /を使って定義すると奇数でも#tになるので注意。



  • p148のevens-only*&coの結果は間違ってるらしい



  • evens-only*&coは一応書けた。やっぱり一番最後の収集子が難しい。


    • 一応かけたけど何をしてるかって聞かれるとまだ説明しづらい。




感想


収集子面白い!


lambdaでくるんで渡して後続の計算から呼び出されるところとか


クロージャで前の計算の値を保持してるとことか