3

我从网站http://www.ccs.neu.edu/home/shivers/newstyle.html看到一段代码:

> (defun element-generator ()
    (let ((state '(() . (list of elements to be generated)))) ;() sentinel.
      (let ((ans (cadr state)))       ;pick off the first element
        (rplacd state (cddr state))   ;smash the cons
        ans)))
ELEMENT-GENERATOR
> (element-generator)
LIST
> (element-generator)
OF
> (element-generator)
ELEMENTS
> (element-generator)
TO
> (element-generator)
BE
> (element-generator)
GENERATED

我不明白该功能如何记住状态。state每次函数运行时不是重新定义为整个列表吗?为什么有两层let(这是必要的)?如果有人能够解释此功能的工作原理,将不胜感激。

4

4 回答 4

7

statein的值(let ((state '(() . (list of elements to be generated)))) ...)是带引号的文字,并且正在修改(如本答案中所述,这是未定义的行为)。这种行为已经讨论过其他问题,例如:

于 2013-07-25T18:50:30.670 回答
0
'(() . (list of elements to be generated))

那是一个静态列表,但 rplacd 无论如何都会更改它...修改函数定义,以便下次运行时列表更短。如果您查看调用之间的函数定义,您会发现它越来越短。一些 lisp 实现比其他实现更擅长显示函数定义。

您应该只在刚刚创建列表时使用破坏性列表操作(或者您真正了解自己在做什么)。

于 2013-07-26T04:06:43.883 回答
0

它超出了 CL 标准的范围。一些 CL 实现可能会做这些奇怪的事情,但其他可能不会。我记得我在学习 CL 时发现了类似的东西。我的版本是这样的:

(defun counter1 (&optional (x '(-1)))
   (rplaca x (+ (car x) 1))
   (car x))

(counter1) ; ==> 0
(counter1) ; ==> 1
(counter1) ; ==> 2

这里和您的代码中发生的事情是可选的xstate是常量。它只存在其中之一,如果您更改它,您将永久更改它。在我的例子中;如果默认值是(cons -1 ()),如果我不提供,它将始终返回 0 x。为什么你有两个嵌套的 let 是(cadr state)因为rplacd. 我敦促您将其删除并替换ans(cadr state)以查看差异。

如果您想要标准中的相同功能,以下是示例代码的外观:

(defun element-generator ()
  (let ((state (list () 'list 'of 'elements 'to 'be 'generated))); Make closure
    #'(lambda ()                        ;create function
        (let ((ans (cadr state)))       ;pick off the second element
          (rplacd state (cddr state))   ;mutate the list in the closure
          ans))))                       ;return element

(setf (symbol-function 'gen1) (element-generator))
(setf (symbol-function 'gen2) (element-generator))
(gen1) ; ==> LIST
(gen1) ; ==> OF
(gen1) ; ==> ELEMENTS
(gen2) ; ==> LIST
(gen2) ; ==> OF
(gen1) ; ==> TO

在这里,每个元素生成器实例都会获得一个新的闭包,当您调用它返回的函数时,您将从该闭包中获取元素。查看返回的两个函数如何拥有自己的列表版本。由于我们没有破解标准,我们不妨将其重写为 mutate state 而不是列表:

(defun element-generator ()
  (let ((state (list 'list 'of 'elements 'to 'be 'generated))) ; create closure
    #'(lambda ()                       ;create function
        (let ((ans (car state)))       ;pick off the first element
          (setf state (cdr state))     ;mutate state to point to next element
          ans))))                      ;return first element

它将表现相同。

于 2013-07-25T21:52:49.293 回答
0
(defun element-generator ()

上面定义了一个函数。

  (let ((state '(() . (list of elements to be generated)))) ;() sentinel.

这里定义了一个局部变量。代码中有文字数据。想想 Lisp 函数本身的数据,部分代码就是这个带有列表的构造。在 Common Lisp 标准中,当一个人试图修改这个数据对象时会发生什么是未定义的。

由于它是文字数据对象,因此只有一个。该变量没有绑定到新的 consed 数据。所以在每次调用时,这里的变量都指向相同的文字数据。

    (let ((ans (cadr state)))       ;pick off the first element

上面创建了一个临时变量并保存了第一个元素。

      (rplacd state (cddr state))   ;smash the cons

在上面的代码中,第一个元素被从列表中删除。如前所述,这不是推荐的做法,因为列表是文字数据。

      ans)))

以上返回保存的值。

这个LET成语在 Common Lisp 中已经由宏提供PROG1

于 2013-07-25T20:34:10.893 回答