2013-03-30 135 views
1

我正在通过SICP工作,并且我正在进行的练习要求提供返回列表中最后一个元素的过程。我实现了程序last-pair要做到这一点,但我很困惑,为什么它返回一个列表,而不是一个数字:为什么这会返回一个列表'(5)而不是数字5?

(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):

> (last-pair (list 1 2 3 4 5)) 
'(5) 

我期待5,怎么样(car (list 1 2 3 4 5))将返回1'(1)

为什么我得到'(5)而不是5


我使用DrRacket 5.3.3和球拍方案。

编辑1:麻省理工学院计划似乎没有这样做。 last-pair返回5而不是'(5)。哪个是对的?!?

编辑2:有趣的是,在DrRacket(未在MIT-计划),如果第二线(cond ((null? (cdr alist))缩进两个空间,当程序被调用时,它返回'(5)。但是,当第二行不缩进时,它将返回5。这是一个小故障?我相信所有这些Scheme解释器都应该遵循的是括号,对吗?

编辑3:我开始认为这是DrRacket中的一个小故障。当我将过程定义放置在定义窗口(通常是最上面的编辑器窗格)时,无论是否缩进,过程将返回5。但是,如果我在界面窗口,中定义它,则缩进会影响编辑2中所述的结果(编辑4),不管缩进是多少,它都会返回'(5)

< 用一些关于缩进差异的代码缩小了前缀部分;现在的问题就是程序定义的地方,参见编辑4 >

编辑4:好的我简化了这个问题。

  • 在MIT-方案,(last-pair (list 1 2 3 4 5))返回5,其中last-pair如上所定义。不管缩进。
  • 在DrRacket中,当在定义窗口中定义last-pair过程时,然后单击“运行”,(last-pair (list 1 2 3 4 5))返回5。不管缩进。
  • 在DrRacket中,当last-pair程序在界面窗口(REPL)(last-pair (list 1 2 3 4 5)) returns'(5)中定义时。不管缩进。

以下是截图: Racket gives different results for same function defined in different windows

+0

你有一个屏幕截图? – soegaard

+1

尚无法复制。请注意,如果您在交互中输入定义,则可能会与Racket语言中定义的现有'last-pair'定义发生混乱:http://docs.racket-lang.org/reference/pairs .html?#%28def ._%28%28lib._racket%2Flist..rkt%29._last-pair%29%29将所有定义保留在“定义”窗格中并将“交互”窗格视为一个探索这些定义的地方。 – dyoo

+0

另请注意,Racket中的注释符号使用分号而不是散列。你真正的程序是否有哈希? – dyoo

回答

0

最好不要使用内置名称last-pair。我建议使用更多的描述性内容,例如last-elem

重命名你的函数时,一定要重新命名它;即不仅在定义站点,而且在每个呼叫站点改变功能的名称,当然包括它的身体内部,其递归地被称为。重命名必须勤勉地执行,否则很容易引入新的错误。


关于REPL的奇怪行为。我的猜测是,当你进入

​​

在REPL中,last-pair在仍被称为调用点内置的从“外部”环境的定义,所以这个电话并非递归。如果是这样的话,REPL确实重新定义了内置函数,只是这个调用不是递归的。

我期望做有明确letrec内部定义应该修复它,即使在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内搞乱是另一回事。


如果这确实是工作这将意味着球拍REPL翻译像(define (f x) ...body...)简单lambda形式,(define f (lambda(x) ...body...)),还不如letrec形式,(define f (letrec ((f (lambda(x) ...body...))) f))简单的定义。此外,Racket REPL上的define不会更改全局环境中的旧绑定,但会在旧绑定上添加新绑定,而旧绑定会隐藏旧绑定。

这表明另一种方法来“修复”的REPL —与set!

> (define f #f) 
> (set! f (lambda(x) ...body...)) ; alter the old binding explicitly 
> (f x) 
+0

对不起,如果我不清楚,我试图编辑我的问题,以反映REPL不再有任何奇怪的行为。唯一的问题是我定义过程的位置,以及我为过程命名的位置。感谢您的详细解答。 – kalaracey

+0

“REPL不再有奇怪的行为” - 在你重新命名函数后?但我想也找到原因,并检查解决方案修复它,而不重命名(他们两个)。 –

3

由于(list 1 2 3 4 5)返回(cons 1 (cons 2 (cons 3 (cons 4 (cons 5 '())))))最后一对是(cons 5 '())

在你的功能,以retun最后一对(而不是最后一对的车chnage ((null? (cdr alist)) (car alist))((null? (cdr alist)) alist)

编辑:

这说明你在看到结果之间的差异定义和交互窗口混乱的主要原因是last-pair是内置的。如果你使用的名称my-last-pair你会看到两个窗口相同的结果。

在定义窗口(define (last-pair ...被解释为你想重新定义一个内建函数。因此last-pair递归地指向您自己的last-pair的定义。这最终在您的示例中给出结果5

在交互窗口中,对last-pair的递归调用引用了内置版本。所以当last-pair与列表(2 3 4 5)一起被调用时,内建版本将返回最后一对,即(cons 5 '()),并且该值将打印为(5)

简而言之:混淆是由于在交互窗口中重新定义了内建函数。重新定义在定义窗口中按预期处理。尽管混淆交互窗口行为背后的原因(解决这个问题,反过来会导致其他地方混淆)。

+1

我不认为这就是'list'返回的结果,虽然'cons'的重现可能是如何产生返回值的。 'list'返回一个(在本例中)五个元素的列表,加上null。当'(null?(cdr alist))'成立时,'alist'是一个列表,例如'(5)'。然后,'(汽车列表)'应该返回'5'。 – kalaracey

+0

有趣的是,如果第二行'(cond((null?(cdr alist))'缩进了两个空格,那么在调用该过程时,它会返回''(5)'在DrRacket中(不在MIT-Scheme中)。但是,当第二行没有缩进时,它返回'5',这是一个小故障吗?我相信Scheme解释器应该遵循的所有括号是正确的吗? – kalaracey

+1

如果alist是(5),那么值为5是列表的最后一个元素,列表中的最后一个元素(又名cons-cell)是(cons 5'())。由于该函数被称为last-pair而不是last-element ,我希望它返回(缺点5'()),并且该值被打印为(5)。 – soegaard

相关问题