Scheme 为什么会返回一个列表';(5) 而不是数字5?
我正在处理SICP,我正在处理的练习要求一个过程返回列表中的最后一个元素。我实现了执行此操作的过程Scheme 为什么会返回一个列表';(5) 而不是数字5?,scheme,racket,sicp,mit-scheme,Scheme,Racket,Sicp,Mit Scheme,我正在处理SICP,我正在处理的练习要求一个过程返回列表中的最后一个元素。我实现了执行此操作的过程last pair,但我不明白为什么它返回的是列表而不是数字: (define (last-pair alist) (cond ((null? (cdr alist)) (car alist)) ; still happens if this is just "car alist)" (else (last-pair (cdr a
last pair
,但我不明白为什么它返回的是列表而不是数字:
(define (last-pair alist)
(cond ((null? (cdr alist))
(car alist)) ; still happens if this is just "car alist)"
(else
(last-pair (cdr alist)))))
当我在从1到5的整数列表中调用它时,我得到了输出'(5):
我期望5
,比如(car(list 1 2 3 4 5))
将如何返回1
而不是”(1)
为什么我得到”(5)
而不是5
?
我正在使用DrRacket 5.3.3和Racket方案 编辑1:MIT Scheme似乎没有这样做
最后一对
返回5
而不是”(5)
。哪个是正确的
编辑2:有趣的是,在DrRacket(不在MIT方案中)中,如果第二行(cond((null?(cdr-alist))
缩进两个空格,则调用过程时,它返回”(5)
。但是,当第二行没有缩进时,它返回5
。这是一个小故障吗?我相信方案口译员应该遵循的是括号,对吗
编辑3:我开始认为这是DrRacket中的一个小故障。当我将过程定义放在“定义”窗口(通常是顶部的编辑器窗格)中时,无论缩进如何,过程都将返回5
。但是,如果我在界面窗口中定义它,缩进会影响编辑2中所述的结果。(编辑4)不管缩进是什么,它都会返回”(5)
- 在MIT方案中,
返回(最后一对(列表1 2 3 4 5))
,其中5
是在上面定义的。不考虑缩进最后一对
- 在DrRacket中,当定义窗口中定义了
最后一对
过程,然后我单击“运行”时,
返回(最后一对(列表1 2 3 4 5))
,而不考虑缩进5
- 在DrRacket中,当界面窗口(REPL)中定义了
过程时,最后一对
”(5)。不考虑缩进(最后一对(列表1 2 3 4 5))返回
(列表1 2 3 4 5)
返回(cons 1(cons 2(cons 3)(cons 4)(cons 5’(щщ))
最后一对是(cons 5’())
在函数中,将chnage((null?(cdr-alist))(car-alist))
更改为((null?(cdr-alist))alist)
,以便重新运行最后一对(而不是最后一对的汽车)
编辑:
这解释了您在定义和交互窗口中看到的结果之间的差异。造成混淆的主要原因是last pair
是内置的。如果使用名称my last pair
,您将在两个窗口中看到相同的结果
在定义窗口中,(define(last pair…
)被解释为意味着您希望重新定义一个内置函数。因此,last pair
递归地引用您自己对last pair
的定义。在您的示例中,这最终给出了结果5
在交互窗口中,对最后一对
的递归调用指的是内置版本。因此,当使用列表(2 3 4 5)
调用最后一对
时,内置版本返回最后一对,即(cons 5'())
,该值打印为(5)
简言之:混乱是由于在交互窗口中重新定义了内置函数。重新定义在定义窗口中按预期进行处理。尽管混乱,但交互窗口的行为方式背后有一些原因(解决此问题反过来会在其他地方造成混乱).最好不要使用内置名称
最后一对
。我建议使用更具描述性的名称,例如最后一个元素
重命名函数时,请确保始终对其进行重命名;也就是说,不仅在定义站点更改函数名称,而且在每个调用站点更改函数名称,当然包括在递归调用函数的函数体内部。重命名必须认真执行,否则很容易引入新错误
我猜你进来的时候
(define (last-pair alist) ;; definition
(cond ((null? (cdr alist))
(car alist))
(else
(last-pair (cdr alist))))) ;; call
在REPL中,调用站点上的最后一对仍然引用了“外部”环境中的内置定义,因此此调用不是递归的。如果是这种情况,REPL确实重新定义了内置定义,只是调用不是递归的
我希望使用explicitletrec
创建一个内部定义应该可以修复它,即使在REPL中输入:
(define (last-pair alist)
(letrec ((last-pair (lambda (alist) ;; internal definition
(cond ((null? (cdr alist))
(car alist))
(else
(last-pair (cdr alist))))))) ;; recursive call
(last-pair alist))) ;; first call
因为第一个调用现在显式地调用递归内部版本,在letrec
表单中。或者它也会被搞砸,但如果它真的被搞砸了,我会非常惊讶的。:)将没有内部定义的define
s转换为简单的lambda
表单是一回事;在显式letrec
中弄乱是另一回事
如果这真的有效,这将意味着Racket REPL将简单的定义,如(定义(f x)…body…
转换为简单的lambda
形式,(定义f(lambda(x)…body…)
,而不是letrec
形式,(定义f(lambda(x)…body…)形式)
。而且,Racket REPL上的定义
不会改变全局环境中的旧绑定,而是在旧绑定的基础上添加一个新绑定,以隐藏旧绑定
这表明了在REPL上“修复它”的另一种方法-设置!
:
> (define f #f)
> (set! f (lambda(x) ...body...)) ; alter the old binding explicitly
> (f x)
我不认为这是list
返回的结果,t
> (define f #f)
> (set! f (lambda(x) ...body...)) ; alter the old binding explicitly
> (f x)