老潘已转到http://www.panxingzhi.net/。所有旧文章如有改动,此处将不再更新。谢谢。 I've moved to http://www.panxingzhi.net/. Updates on old posts are not applied here. Thanks.

SICP-2.6 solution

老潘早就知道丘奇数不好惹,反正第一次看的时候就觉得很费脑子。但这次终于绕不过去了必须得弄明白。。。真不知道我提前知道了一些lambda calculus是好还是不好,因为我实际上不用推那个add-1就知道one应该怎么写。。。我就当这是好事了~毕竟这道题并不是让你理解substitution model,而是理解“用过程表示数据”。啊!我突然明白大家说Lisp里面代码就是数据原来还有这层意思,太tm狡猾了!!!

; *zero* is a (higher-order) function "(lambda (f) (lambda (x) x)" that
; takes a function as the sole argument (f),
; and returns another function "(lambda (x) x)"
;     which applies f on its sole argument (x) 0 times
; we can see x is actually a dummy...
(define zero
  (lambda (f) (lambda (x) x)))

; we clearly see that, the approach is to represent numbers by functions
; if we apply *number* functions to a function f,
; we get a function that repeatly applies f to its argument *number* times.
; that is, (n f) evaluates to a function which repeatly applies f to its argument n times.
; so ((n f) x) is the result of repeatly applying f to x n times
; (f ((n f) x)) is the result of repeatly applying f to x (n + 1) times

; *add-1* is a (higher-order) function that
; takes a function (representing a number) as the sole argument (n),
; and returns another function (representing a number) "(lambda (f) (lambda (x) (f ((n f) x))))",
;     which takes a function as the sole argument (f),
;     and returns another function "(lambda (x) (f ((n f) x)))",
;         which actually applies f on x (n + 1) times
; so *add-1* returns the function representing n + 1
(define (add-1 n)
  (lambda (f) (lambda (x) (f ((n f) x)))))

; if we take the advice and use the substitution model...
; one is (add-1 zero)
(add-1 zero)
(add-1 (lambda (f) (lambda (x) x)))
(lambda (f) (lambda (x) (f (((lambda (f) (lambda (x) x)) f) x))))
(lambda (f) (lambda (x) (f ((lambda (x) x) x))))
(lambda (f) (lambda (x) (f x)))

; so one is...
(define one
  (lambda (f) (lambda (x) (f x))))

; and two...
(define two
  (lambda (f) (lambda (x) (f (f x)))))

; and plus...
(define (+ a b)
  (lambda (f) (lambda (x) ((b f) ((a f) x)))))

核心思想是用function表示正整数。

从0开始:0是一个高阶函数,它的唯一参数是函数f,f亦只有一个参数(虽然我们单从这里看不出来,但没办法我就是知道!),输出是另一个函数out(是个lambda!),out还是只有一个参数x,其作用是把x作为f的参数,执行零次。有0就有1。1唯一的不同就是最后那下执行一次。有1就有2,2就是最后那下执行两次。。。注意这里面的执行是把前一次执行得到的结果作为后一次执行的参数,就好比(f (f (f ... (f x)...)))。

注意 使用这种方式,正整数n这个信息就隐藏或者说保存在执行“输出函数out”时,执行f的次数中。。。比如我们想知道这个信息,我们可以先执行n,这样就得到了out。然后我们执行out,假如我们执行n时传入的f有记录自己被执行了多少次的功能(比如每执行一次就把一个全局变量加1),我们就能extract这个信息出来,对不?你看你看:

(define count 0)

(define two
  (lambda (f) (lambda (x) (f (f x)))))

((two (lambda (x) (set! count (+ 1 count)))) 'x)

(print count)

当然我们一般不需要这么做。这么做相当于一种信息格式的转换 - 把用函数表示的数,转成了我们一般认知的数。信息自然是没丢,但要是你所有用来操作数的过程都使用“函数”这个格式(比如lambda calculus里),你转它做什么?

说句题外话,其实lambda calculus里所有的东西,包括数,都是函数;而所有的函数都只有一个参数,你要不服只能用curry这个技术。





1 条评论:

  1. 老潘, 2008-03-26 15:20:56  [回复]
    关于f记录自己执行次数这事,其实每次执行f后都会把结果作为下一次执行f的参数,于是我们用这个东西(非常自然的)保存执行次数就行了。于是我们就有了这个版本的丘奇数->数转换器:
    (define (cn-to-n cn)
    ((cn (lambda (i) (+ i 1)))0))

    这是从ocaml china上看来的。当然了,还要呈上我自创的加强版数->丘奇数转换器:
    (define (n-to-cn n)
    (define (repeat f x n)
    (if (= n 0)
    x
    (repeat f (f x) (- n 1))))
    (lambda (f) (lambda (x) (repeat f n x))))

    可以干一下这种事:
    (cn-to-n (n-to-cn 51242314))

添加评论