Hatena::ブログ(Diary)

hchbaw記

2010-09-27

“The Why of Y”のトレース、というかおいしいとこだけ…

不動点演算子がどこからきたのか知りたい、と思ったので、そのものずばりの文書を見る。印象に残ったところだけでも書きとめておこうと思いました。

こちら、

The Why of Y / Richard P. Gabriel / Lucid, Inc. and Stanford University

http://www.dreamsongs.com/Files/WhyOfY.pdf

です。

ここでいうYはZとも呼ばれているそうです。

注意点!!

あたしの勝手に抱いたイメージがそのまま投影されちゃっていますので、原文を先に読まれることを強く強くおすすめします。m(__)m

#そもそも、原文抜きでは意味不明ですね…

#そういった意味でちゃんと邦訳するというのも良いのかも…と思った…

感想

S式をこねくりまわしていたらば、いつのまにか目的のYが発見できちゃったね、という流れに見えちゃいました。この“いつのまにか”てきなところが印象的でした。

あとは、Lispになっているのをじーっと見ていたらばklop関数が怖くなくなった(^^

ここでいうklop関数といったのは、不動点コンビネータの“ジャン・ウィレム・クロップによって組み立てられた”のものです。

以下本文です。

原文を先に読まれることを強く強くおすすめします。m(__)m

気分を高揚させるためにまずこの美しい写真でイメージを膨らませましょう、

http://weitz.zenfolio.com/ilc2009/h2807fbc9#h2807fbc9

イメージイメージイメージ…(^^ 心の準備はできましたか?

以下、#のところはあたしのさえずりです。

以下本当の本文です。

ポイントはこれ。

To derive Y, I will start with an example recursive function, factorial. In the derivation I will make use of three techniques. The first is to pass an additional argument to avoid using any self-reference primitives from Scheme. The second is to convert multiple-parameter functions to nested single-parameter functions in order to separate manipulation of the self-reference parameters from manipulation of ordinary parameters. The third is to introduce functions through abstraction.

“Yを導き出すために、例として再帰関数階乗を取り上げます。その中で、3つの技法を扱います。

1.追加の引数を渡すことで、Schemeの自己参照機能を利用するのを避けます。

2.複数の引数を取る関数を入れ子になった複数の1引数関数に変換することで、自己参照の引数とそれ以外の引数の操作を区別します。

3.関数による抽象化

考えるのは階乗関数です。

(define fact
  (lambda (n)
    (if (< n 2)
      1
      (* n (fact (- n 1))))))

1.追加の引数でもってSchemeの自己参照プリミティブの利用を避ける。

引数にhを追加します。

#っと、いきなり山場なんだけれども…。ここでg自体を渡しちゃう!

(let ((g (lambda (h n)
           (if (< n 2)
             1
             (* n (h h (- n 1)))))))
  (g g 10))

2.自己参照のための引数を他の引数の操作と区別するために、複数の引数関数を入れ子になった1引数関数群へと変換する。

このような手法を“curry(ing)”と呼ぶ。例えばこんなものですね。

(letrec ((f (lambda (n m)
              (if (< n 2)
                m
                (f (- n 1) (* m n))))))
  (f 10 1))
;; ↓
(letrec ((f (lambda (n)
              (lambda (m)
                (if (< n 2)
                  m
                  ((f (- n 1)) (* m n)))))))
  ((f 10) 1))

curry(ing)”とは、どんな関数も1つのみ引数を持つことにしちゃって、複数の引数を渡すことを、入れ子になった関数の適用として達成されるようにすること。

#日本語がが…

例えば、

((f (- n 1)) (* m n))

では、

まず適用すべき関数が計算されてから、その結果が正しい引数の下で評価されます。

階乗関数でいうと、こんな具合です。

(let ((g (lambda (h)
           (lambda (n)
             (if (< n 2)
               1
               (* n ((h h) (- n 1))))))))
  ((g g) 10))

(さて)

自己参照の基本機能を得るには、関数をそれ自体に適用してしまえばよい。そうすれば外側のg(自分自身)をhとして掴まえたクロージャを得られる。再帰的に呼び出したければ、hを引数hで呼べばよい。ああそうそう、ここでいうhはletで定義された関数gのことだね。

#“h's”の“'s 〜”ということなんだろうけども、“〜”が何を意味しているのかちょっとわからない…“hの定義”ということになるのかなあ(T^T

#そしてだんだんと3へ。

(h h)やnへと及んじゃっているif式を抽象化しよう。ねらいは2つ。

結果となる関数は、周囲の束縛とは独立させること。

制御用の引数の操作を数値の引数とは独立させること。

#“abstract `if` expression over `(h h)` and `n`.”のイミがわからないヒー><

#(h h)を計算して(qとして)渡し、渡される関数の方では数値の計算を行なうだけにする、というようにすることで“区別する”、ということになるのかなあ(T^T

結果は以下。

(let ((g (lambda (h)
           (lambda (n)
             (let ((f (lambda (q n)
                        (if (< n 2)
                          1
                          (* n (q (- n 1)))))))
               (f (h h) n))))))
  ((g g) 10))

そしたらば、“curry”だね。

(let ((g (lambda (h)
           (lambda (n)
             (let ((f (lambda (q)
                        (lambda (n)
                          (if (< n 2)
                            1
                            (* n (q (- n 1))))))))
               ((f (h h)) n))))))
  ((g g) 10))

fがgの奥深くへ埋め込まれている必要はないよね。階乗を計算する主たる部分を、他の部分から抽出しよう。

(let ((f (lambda (q)
           (lambda (n)
             (if (< n 2)
               1
               (* n (q (- n 1))))))))
  (let ((g (lambda (h)
             (lambda (n)
               ((f (h h)) n)))))
    ((g g) 10)))

気づいた?

fは変数化された階乗を計算する主たる部分なんだってことと、fの中に隠れちゃっていたこの↓式を抽象化できたんだってこと。

(define Y (lambda (f)
            (let ((g (lambda (h)
                       (lambda (x)
                         ((f (h h)) x)))))
              (g g))))

フォッフォッフォッ。Yのできあがりじゃよ(^^

#“abstract”の意味がとてもむつかしい><

Y'(チューリングの不動点演算子)

計算論 計算可能性とラムダ計算 (コンピュータサイエンス大学講座)』の問2.1.5なのだけれども、

Y' ≡ X X (ただしY' ≡ λ xy.y (x x y))のとき、

すべてのMについて Y' M →→β M (Y' M) が成り立つことを示せ。

Y' M  ≡ (λ xy.y (x x y)) X M
     →β (λ y.y (x x y)) [x := X] M
      ≡ (λ y.y (X X y)) M
     →β (y (X X y)) [y := M]
      ≡ M (X X M) ≡ M (Y' M)

こうですか?すごくよくわかっていません本当にごめんなさい><