我已经在Lambda微积分上挣扎了一段时间。有很多资源可以解释如何减少嵌套的lambda表达式,但是资源很少,因此无法指导我编写自己的lambda表达式。
我正在尝试使用纯lambda演算(即单参数函数,教堂数字)在Racket中编写一个递归的Fibonacci解决方案。
这些是我一直在使用的教堂数字的定义:
(define zero (λ (f) (λ (x) x)))
(define one (λ (f) (λ (x) (f x))))
(define two (λ (f) (λ (x) (f (f x)))))
(define three (λ (f) (λ (x) (f (f (f x))))))
(define four (λ (f) (λ (x) (f (f (f (f x)))))))
(define five (λ (f) (λ (x) (f (f (f (f (f x))))))))
(define six (λ (f) (λ (x) (f (f (f (f (f (f x)))))))))
(define seven (λ (f) (λ (x) (f (f (f (f (f (f (f x))))))))))
(define eight (λ (f) (λ (x) (f (f (f (f (f (f (f (f x)))))))))))
(define nine (λ (f) (λ (x) (f (f (f (f (f (f (f (f (f x))))))))))))
这些是我一直尝试合并的单参数函数:
(define succ (λ (n) (λ (f) (λ (x) (f ((n f) x))))))
(define plus (λ (n) (λ (m) ((m succ) n))))
(define mult (λ (n) (λ (m) ((m (plus n)) zero))))
(define TRUE (λ (t) (λ (f) t)))
(define FALSE (λ (t) (λ (f) f)))
(define COND (λ (c) (λ (x) (λ (y) ((c x) y)))))
(define iszero (λ (x) (x ((λ (y) FALSE) TRUE))))
(define pair (λ (m) (λ (n) (λ (b) (((IF b) m) n)))))
(define fst (λ (p) (p TRUE)))
(define snd (λ (p) (p FALSE)))
(define pzero ((pair zero) zero))
(define psucc (λ (n) ((pair (snd n)) (succ (snd n)))))
(define pred (λ (n) (λ (f) (λ (x) (((n (λ (g) (λ (h) (h (g f))))) (λ (u) x))(λ (u) u))))))
(define sub (λ (m) (λ (n) ((n pred) m))))
(define leq (λ (m) (λ (n) (iszero ((sub m) n))))) ;; less than or equal
(define Y ((λ (f) (f f)) (λ (z) (λ (f) (f (λ (x) (((z z) f) x))))))) ;; Y combinator
我首先在球拍中写了递归斐波那契:
(define (fib depth)
(if (> depth 1)
(+ (fib (- depth 1)) (fib (- depth 2)))
depth))
但是在我的多次尝试中,我一直没有使用纯Lambda演算编写它。即使是开始,这也是一个挣扎。
(define fib
(λ (x) ((leq x) one)))
我打给谁(例如):
(((fib three) add1) 0)
这至少有效(正确地返回教堂零或一),但是添加超出此范围的所有内容都会破坏一切。
我对球拍非常缺乏经验,而Lambda演算是一个让人头疼的东西,因为直到最近才真正拿起它的人。
我想了解如何构建此功能,以及将递归与Y组合器合并。我特别希望对任何代码进行解释。使其与fib(zero)
到fib(six)
一起使用就足够了,因为我担心以后会扩展Church的定义。
编辑:
我的iszero
函数在我的实现中是一个隐藏的破坏者。这是一个正确的版本,其中包含来自Alex回答的更新后的布尔值:
(define iszero (λ (x) ((x (λ (y) FALSE)) TRUE)))
(define TRUE (λ (t) (λ (f) (t))))
(define FALSE (λ (t) (λ (f) (f))))
有了这些更改,并合并了重击,一切都按预期运行!
答案 0 :(得分:4)
如果您使用的是急切(而不是惰性)的语言,例如Racket,则需要注意如何编码分支形式(如COND
函数)。
您现有的布尔值和条件定义为:
(define TRUE (λ (t) (λ (f) t)))
(define FALSE (λ (t) (λ (f) f)))
(define COND (λ (c) (λ (x) (λ (y) ((c x) y)))))
它们适用于像这样的简单情况:
> (((COND TRUE) "yes") "no")
"yes"
> (((COND FALSE) "yes") "no")
"no"
但是,如果“未采用分支”将产生错误或无限循环,则良好的分支形式将“短路”以避免触发它。一个好的分支形式应该只评估它需要采取的分支。
> (if #true "yes" (error "shouldn't get here"))
"yes"
> (if #false (error "shouldn't trigger this either") "no")
"no"
然而,您的COND
评估两个分支,仅是因为 Racket的函数应用程序评估了所有参数:
> (((COND TRUE) "yes") (error "shouldn't get here"))
;shouldn't get here
> (((COND FALSE) (error "shouldn't trigger this either")) "no")
;shouldn't trigger this either
以一种热切的语言(例如,无需切换到#lang lazy
)来教我解决此问题的方法是将重击传递给这样的分支形式:
(((COND TRUE) (λ () "yes")) (λ () (error "doesn't get here")))
但是,这需要对布尔值的定义进行一些细微的调整。之前,布尔值有两个值可供选择,然后返回一个。现在,布尔值将接受两个 thunks 进行选择,并且会调用一个。
(define TRUE (λ (t) (λ (f) (t)))) ; note the extra parens in the body
(define FALSE (λ (t) (λ (f) (f)))) ; same extra parens
可以像以前一样定义COND
格式,但是您将不得不不同地使用它。要在您撰写之前翻译(if c t e)
,请执行以下操作:
(((COND c) t) e)
现在有了您将要编写的布尔值的新定义:
(((COND c) (λ () t)) (λ () e))
我将(λ () expr)
缩写为{expr}
,以便我可以这样写:
(((COND c) {t}) {e})
现在以前由于错误而失败的内容,将返回正确的结果:
> (((COND TRUE) {"yes"}) {(error "shouldn't get here")})
"yes"
这使您可以编写条件语句,其中一个分支是停止的“基本情况”,而另一个分支是可以继续进行的“递归情况”。
(Y (λ (fib)
(λ (x)
(((COND ((leq x) one))
{x})
{... (fib (sub x two)) ...}))))
如果没有多余的(λ () ....)
和新的布尔值定义,由于Racket渴望(而不是偷懒)进行参数评估,这将永远循环。