循环通过列表的神秘计划程序

时间:2011-12-25 11:02:03

标签: list scheme racket mutable circular-reference

有这个程序给我带来麻烦:

(define (pro lst)
  (define (inner l)
    (if (null? (mcdr l))
        (set-mcdr! l lst)
        (inner (mcdr l))))
  (inner lst)
  lst)

使用(mlist 1 2 3)作为参数我得到#0 = {1 2 3。 #0#}返回。 (mcdr (pro (mlist 1 2 3)))返回#0 = {2 3 1。 #0#}, (mcdr (mcdr (pro (mlist 1 2 3))))返回#0 = {3 1 2。 #0#}等等。

显然,这是通过返回一对列表和另一个过程来循环遍历列表。但这是如何工作的?我只看到它用参数lst替换最后的'(),但没有任何模糊的lambda函数... 无论如何,#0和#0#意味着什么?

2 个答案:

答案 0 :(得分:2)

#0=表示某些数据的共享标记,#0#表示对先前标记的数据的引用。请参阅http://docs.racket-lang.org/reference/shared.html底部的示例。你的函数不会返回一对东西,只返回一个值 - 定义正文中的最后一个表达式,即lst。但是,正如您可能已经意识到的那样,最后的lst可能已经发生了变异,因此它与最初传递给函数时的变化不同。 (你使用的术语是“循环”,这正是这个功能所做的,但不是你在你的问题中应用它的意义上的。)

答案 1 :(得分:2)

在表达式#0={1 2 3 . #0#}中,将#0=视为锚点,将#0#视为该锚点的链接 - 也就是说,在列表中前三个元素为{{1}但第四个元素是指向列表开头的指针,因此形成一个三元素循环列表。如果你继续推进该列表(通过连续的1 2 3),你将在循环模式mcdr中看到一个三元素列表,总是显示第四个元素跳回到第一个元素。

研究上述功能可以清楚地说明为什么会这样。 1 2 3 1 2 3 ...过程只需在pro参数上调用inner(一个正确的列表,一个以lst作为最后一个元素的列表),然后返回修改后的null ,有趣的部分是lst

中发生的事情
  • inner:如果我们在列表的最后一个非null元素上,则通过对第一个元素的引用替换下一个元素(在适当的列表中应该是(if (null? (mcdr l)))我们知道的元素在null参数中:lst
  • 否则,继续推进列表:(set-mcdr! l lst)

总结一下:(inner (mcdr l))过程将输入一个正确的列表并返回相同的列表,但最后一个元素指向其第一个元素 - 循环列表。