以下是the Y-combinator in Racket:
#lang lazy
(define Y (λ(f)((λ(x)(f (x x)))(λ(x)(f (x x))))))
(define Fact
(Y (λ(fact) (λ(n) (if (zero? n) 1 (* n (fact (- n 1))))))))
(define Fib
(Y (λ(fib) (λ(n) (if (<= n 1) n (+ (fib (- n 1)) (fib (- n 2))))))))
以下是the Y-combinator in Scheme:
(define Y
(lambda (f)
((lambda (x) (x x))
(lambda (g)
(f (lambda args (apply (g g) args)))))))
(define fac
(Y
(lambda (f)
(lambda (x)
(if (< x 2)
1
(* x (f (- x 1))))))))
(define fib
(Y
(lambda (f)
(lambda (x)
(if (< x 2)
x
(+ (f (- x 1)) (f (- x 2))))))))
(display (fac 6))
(newline)
(display (fib 6))
(newline)
我的问题是:为什么Scheme需要apply
功能但Racket没有?
答案 0 :(得分:11)
对于大多数用途,球拍非常接近普通方案,对于这个例子,它们是相同的。但是这两个版本之间的真正区别在于需要一个延迟的包装器,它需要严格的语言(Scheme和Racket),而不是懒惰的(Lazy Racket,一种不同的语言)。
那个包装器放在(x x)
或(g g)
周围 - 我们对这个东西的了解是评估它会让你进入一个无限循环,我们也知道它会是结果(递归)函数。因为它是一个函数,我们可以使用lambda
延迟其评估:而不是(x x)
使用(lambda (a) ((x x) a))
。这工作正常,但它有另一个假设 - 包装函数只需一个参数。我们也可以用两个参数的函数来包装它:(lambda (a b) ((x x) a b))
但是在其他情况下也不行。解决方案是使用rest参数(args
)并使用apply
,因此使包装器接受任意数量的参数并将它们传递给递归函数。严格地说,它并不总是必需的,如果你想要能够产生任何arity的递归函数,它是“唯一的”。
另一方面,你有Lazy Racket代码,正如我上面所说,它是一种不同的语言 - 一种是需要语义调用的语言。由于这种语言是惰性的,因此不需要包装无限循环的(x x)
表达式,它是按原样使用的。由于不需要包装器,因此不需要处理参数的数量,因此不需要apply
。实际上,懒惰版本甚至不需要假设您正在生成函数值 - 它可以生成任何值。例如,这个:
(Y (lambda (ones) (cons 1 ones)))
工作正常并返回1
s的无限列表。要查看此内容,请尝试
(!! (take 20 (Y (lambda (ones) (cons 1 ones)))))
(注意,需要!!
来递归“强制”结果值,因为Lazy Racket默认情况下不会递归计算。另外,请注意使用take
- 没有它, Racket将尝试创建无限列表,这将无处可去。)
答案 1 :(得分:0)
Scheme不需要apply
功能。你使用apply来接受多个参数。
在阶乘案例中,这是我的实现,不需要apply
;;2013/11/29
(define (Fact-maker f)
(lambda (n)
(cond ((= n 0) 1)
(else (* n (f (- n 1)))))))
(define (fib-maker f)
(lambda (n)
(cond ((or (= n 0) (= n 1)) 1)
(else
(+ (f (- n 1))
(f (- n 2)))))))
(define (Y F)
((lambda (procedure)
(F (lambda (x) ((procedure procedure) x))))
(lambda (procedure)
(F (lambda (x) ((procedure procedure) x))))))