如何进行替换?我试图追查,但我真的不知道发生了什么...... 代码:
(define (repeated f n)
(if (zero? n)
identity
(lambda (x) ((repeated f (- n 1)) (f x)))))
f是一个函数,n是一个整数,给出了我们应该应用f的次数。
....有人可以帮助我解释它。我知道它返回了几个程序,我想相信它会f(f(f(x)))
(define (repeated f n)
(if (zero? n)
identity
(lambda (x) ((repeated f (- n 1)) (f x)))))
其中n是正整数,f是任意函数:方案如何对此代码进行操作,假设我们给出(repeated f 2)
。会发生什么?这就是思考:
(f 2)
(lambda (x) ((repeated f (- 2 1)) (f x))))
(f 1)
(lambda (x) ((lambda (x) ((repeated f (- 1 1)) (f x)))) (f x))))
(f 0)
> (lambda (x) ((lambda (x) (identity (f x)))) (f x))))
> (lambda (x) ((lambda (x) ((f x)))) (f x))))
这里是我被卡住了我想要它去(f(f(x))但现在我会得到(lambda x((fx)(fx)),括号肯定是错的,但我认为你理解我的意思。关于解释器如何工作的论点有什么问题
答案 0 :(得分:2)
你有一个函数,它接受一个函数f
和一个非负整数n
并返回函数 f n ,即, f(f(f(... f(n)...)。根据您对递归的看法,这可以通过两种方式直接实现。在这两种情况下,如果 n 为0,那么你只需要一个返回其参数的函数,该函数就是identity
函数。(这有点像惯例,就像x 0一样 = 1.当它被更深入地考虑时确实有意义,但这可能超出了这个问题的范围。)
如何处理递归案例是您有一些选择的地方。第一种选择是将 f n (x)视为 f(f n-1 (x)),你用 x 调用 f 并调用 f n-1 的结果:
(define (repeated f n)
(if (zero? n)
identity
(lambda (x)
(f ((repeated f (- n 1)) x)))))
另一种选择是将 f n (x)视为 f n-1 (f(x)) 其中_f n-1 被调用,其结果为 f(x)。
(define (repeated f n)
(if (zero? n)
identity
(lambda (x)
((repeated f (- n 1)) (f x)))))
在任何一种情况下,这里要注意的重要一点是在Scheme中,像
这样的形式(function-form arg-form-1 arg-form-2 ...)
通过评估function-form
来生成值函数值(应该是函数)并评估每个arg-form-i
以生成值 arg-value来评估-i
,然后使用 arg-values 调用 _function-value_。由于(repeated ...)
生成一个函数,因此它适合作为function-form
:
(f ((repeated f (- n 1)) x))
; |--- f^{n-1} ------|
; |---- f^{n-1}(x) ------|
;|------f(f^{n-1}(x)) ------|
((repeated f (- n 1)) (f x))
; |--- f^{n-1} ------|
;|---- f^{n-1}(f(x))--------|
根据Will Ness的评论,值得指出的是,虽然这些是解决这个问题的一些自然方式(即,基于等式f n (x)= f n- 1 (f(x))= f(f n-1 (x))),它不一定是效率最高的。这些解决方案都需要计算一些中间函数对象来表示需要相当大量存储的f n-1 ,然后进行一些计算。直接计算f n (x)非常简单有效,例如repeat
:
(define (repeat f n x)
(let rep ((n n) (x x))
(if (<= n 0)
x
(rep (- n 1) (f x)))))
更高效的repeated
版本,只是简单地讨论x
的{{1}}参数:
repeat
这应该比其他任何实现都具有更好的运行时性能。
答案 1 :(得分:2)
您的实现实际上会延迟进一步的递归并返回一个过程,该过程的主体将创建自身的副本以在运行时完成任务。
EG。 (repeated double 4)
==&gt; (lambda (x) ((repeated double (- 4 1)) (double x)))
因此,在调用((repeated double 4) 2)
时,它会运行((repeated double (- 4 1)) (double 2)))
其中操作数部分的计算结果为(lambda (x) ((repeated double (- 3 1)) (double x)))
,等等在运行时进行闭包,因此评估等于此,但在运行时分阶段进行..
((lambda (x) ((lambda (x) ((lambda (x) ((lambda (x) ((lambda (x) (identity x)) (double x))) (double x))) (double x))) (double x))) 2)
编写相同功能的另一种方式是这样的:
(define (repeat fun n)
(lambda (x)
(let repeat-loop ((n n)
(x x))
(if (<= n 0)
x
(repeat-loop (- n 1) (fun x))))))
(define (double x) (+ x x))
((repeat double 4) 2) ; ==> 32
答案 2 :(得分:0)
丹尼。我认为如果我们使用小值n(0,1和2)工作repeated
将能够看到函数如何转换为f(f(f(...(x)))。我假设identity
的实现是(define (identity x) x)
(即按原样返回其唯一参数),if
的“then”部分应为(identity f)
。
(repeated f 0) ;should apply f only once, no repetition
-> (identity f)
-> f
(repeated f 1) ;expected result is f(f(x))
-> (lambda (x) ((repeated f 0) (f x)))
-> (lambda (x) (f (f x))) ;we already know that (repeated f 0) is f
(repeated f 2) ;expected result is f(f(f(x)))
-> (lambda (x) ((repeated f 1) (f x)))
-> (lambda (x) (f (f (f x)))) ; we already know that (repeated f 1) if f(f(x))
......等等。
答案 3 :(得分:0)
平等推理在这里会非常有用。想象一下基于lambda calculus的语言,使用类似Haskell的语法,几乎是combinatory calculus。
这里,括号仅用于表达式的分组(不是函数调用,根本没有语法 - 只是并置):f a b c
与((f a) b) c
相同,与Scheme的{相同{1}}。像(((f a) b) c)
这样的定义相当于f a b = ...
((define f (lambda (a) (lambda (b) ...)))
的快捷方式是(lambda (a) ...)
。
Scheme的语法只是模糊了这里的图片。我不是指括号,而是被迫使用显式的lambdas而不仅仅是方程式并自由地改变论点:
(\a-> ...)
您的代码几乎相当于
f a b = \c -> .... === f a b c = .... ; `\ ->` is for 'lambda'
(将repeated f n x ; (define (repeated f n)
| n <= 0 = x ; (if (zero? n) identity
| otherwise = repeated f (n-1) (f x) ; (lambda (x)
; ((repeated f (- n 1)) (f x)))))
读作“何时”)。所以
|
上面的缩小序列省略了Scheme中环境框架创建和链接的细节,但这一切都非常直观。 repeated f 2 x = ; ((repeated f 2) x) = ((\x-> ((repeated f 1) (f x))) x)
= repeated f 1 (f x) ; = ((repeated f 1) (f x))
= repeated f 0 (f (f x)) ; = ((\y->((repeated f 0) (f y))) (f x))
= f (f x) ; = ((\z-> z) (f (f x)))
; = (f (f x))
与f
相同,f
为n-1 where n=2
,无论 我们执行减法等等。