[英]How to make recursion using only lambdas in Racket?
我需要一些帮助来试图弄清楚如何只使用 lambdas 使下面的代码递归。
(define (mklist2 bind pure args)
(define (helper bnd pr ttl lst)
(cond [(empty? lst) (pure ttl)]
[else (define (func t) (helper bnd pr (append ttl (list t)) (rest lst)))
(bind (first lst) func)])
)
(helper bind pure empty args))
给定一个样本fact
口头程序 -
(define fact
(lambda (n)
(if (= n 0)
1
(* n (fact (- n 1)))))) ;; goal: remove reference to `fact`
(print (fact 7)) ; 5040
上面的fact
是(lambda (n)...)
,当我们调用fact
时,我们要求的是这个 lambda,所以我们可以用新的 arguments 重新应用它。 lambda
是无名的,如果我们不能使用顶级define
绑定,绑定变量的唯一方法是使用 lambda 的参数。 想象一下 -
(lambda (r)
; ...lambda body...
; call (r ...) to recur this lambda
)
我们只需要something
来让我们的(lambda (r)...)
表现得这样 -
(something (lambda (r)
(print 1)
(r)))
; 1
; 1
; 1
; ... forever
介绍你
这something
非常接近U
组合器 -
(define u
(lambda (f) (f f)))
(define fact
(lambda (r) ;; wrap in (lambda (r) ...)
(lambda (n)
(if (= n 0)
1
(* n ((r r) (- n 1))))))) ;; replace fact with (r r)
(print ((u fact) 7))
; => 5040
现在通过使用参数进行递归,我们可以有效地删除所有define
绑定并仅使用lambda
编写它 -
; ((u fact) 7)
(print (((lambda (f) (f f)) ; u
(lambda (r) ; fact
(lambda (n)
(if (= n 0)
1
(* n ((r r) (- n 1)))))))
7))
; => 5040
当你可以 Y 时为什么要 U?
U 组合器很简单,但必须在 function 中调用((rr)...)
很麻烦。 如果您可以直接调用(r...)
来重现,那就太好了。 这正是 Y 组合器所做的 -
(define y
(lambda (f)
(f (lambda (x) ((y f) x))))) ;; pass (y f) to user lambda
(define fact
(lambda (recur)
(lambda (n)
(if (= n 0)
1
(* n (recur (- n 1))))))) ;; recur directly
(print ((y fact) 7))
; => 5040
但是看看y
如何有一个按名称递归定义? 我们可以使用u
删除按名称引用并使用lambda
参数来代替。 和我们上面做的一样——
(define u
(lambda (f) (f f)))
(define y
(lambda (r) ;; wrap in (lambda (r) ...)
(lambda (f)
(f (lambda (x) (((r r) f) x)))))) ;; replace y with (r r)
(define fact
(lambda (recur)
(lambda (n)
(if (= n 0)
1
(* n (recur (- n 1)))))))
(print (((u y) fact) 7)) ;; replace y with (u y)
; => 5040
我们现在可以只使用lambda
来编写它 -
; (((u y) fact) 7)
(print ((((lambda (f) (f f)) ; u
(lambda (r) ; y
(lambda (f)
(f (lambda (x) (((r r) f) x))))))
(lambda (recur) ; fact
(lambda (n)
(if (= n 0)
1
(* n (recur (- n 1)))))))
7))
; => 5040
需要更多参数?
通过使用柯里化,如果需要,我们可以扩展我们的函数以支持更多参数 -
(define range
(lambda (r)
(lambda (start)
(lambda (end)
(if (> start end)
null
(cons start ((r (add1 start)) end)))))))
(define map
(lambda (r)
(lambda (f)
(lambda (l)
(if (null? l)
null
(cons (f (car l))
((r f) (cdr l))))))))
(define nums
((((u y) range) 3) 9))
(define squares
((((u y) map) (lambda (x) (* x x))) nums))
(print squares)
; '(9 16 25 36 49 64 81)
并作为单个lambda
表达式 -
; ((((u y) map) (lambda (x) (* x x))) ((((u y) range) 3) 9))
(print (((((lambda (f) (f f)) ; u
(lambda (r) ; y
(lambda (f)
(f (lambda (x) (((r r) f) x))))))
(lambda (r) ; map
(lambda (f)
(lambda (l)
(if (null? l)
null
(cons (f (car l))
((r f) (cdr l))))))))
(lambda (x) (* x x))) ; square
(((((lambda (f) (f f)) ; u
(lambda (r) ; y
(lambda (f)
(f (lambda (x) (((r r) f) x))))))
(lambda (r) ; range
(lambda (start)
(lambda (end)
(if (> start end)
null
(cons start ((r (add1 start)) end)))))))
3) ; start
9))) ; end
; => '(9 16 25 36 49 64 81)
懒惰的
看看这些很酷的y
使用惰性实现
#lang lazy
(define y
(lambda (f)
(f (y f))))
#lang lazy
(define y
((lambda (f) (f f)) ; u
(lambda (r)
(lambda (f)
(f ((r r) f))))))
#lang lazy
(define y
((lambda (r)
(lambda (f)
(f ((r r) f))))
(lambda (r)
(lambda (f)
(f ((r r) f))))))
为了回应@alinsoar 的回答,我只是想表明,如果您使用Rec
类型放置正确的类型注释,Typed Racket 的类型系统可以表达 Y 组合子。
U 组合器的参数需要一个Rec
类型:
(: u (All (a) (-> (Rec F (-> F a)) a)))
(define u
(lambda (f) (f f)))
Y 组合器本身的类型不需要Rec
:
(: y (All (a b) (-> (-> (-> a b) (-> a b)) (-> a b))))
但是,Y 组合器的定义需要在其中使用的函数之一上使用Rec
类型注释:
(: y (All (a b) (-> (-> (-> a b) (-> a b)) (-> a b))))
(define y
(lambda (f)
(u (lambda ([g : (Rec G (-> G (-> a b)))])
(f (lambda (x) ((g g) x)))))))
仅使用 lambda 的递归可以使用定点组合器完成,最简单的是Ω 。
但是,请考虑到这样的组合器具有无限长的类型,因此如果您使用类型进行编程,则该类型是递归的并且具有无限长。 并非每个类型检查器都能够计算递归类型的类型。 Racket 的类型检查器我认为它是Hindley-Miller ,我记得键入的球拍它不能运行定点组合器,但不确定。 您必须禁用类型检查器才能使其正常工作。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.