Macros ALET宏和间接寻址的使用

Macros ALET宏和间接寻址的使用,macros,lambda,lisp,common-lisp,indirection,Macros,Lambda,Lisp,Common Lisp,Indirection,在“Let Over Lambda”一词中,我发现了一个名为alet的回指宏。它的工作原理类似于let,但在alet主体的最后一种形式是lambda表达式时特别有用,因为它允许在lambda表达式实际出现在文本中之前使用它 这是宏的第一个版本,alet%: (defmacro alet% (letargs &rest body) `(let ((this) ,@letargs) (setq this ,@(last body)) ,@(butlast body)

在“Let Over Lambda”一词中,我发现了一个名为
alet
的回指宏。它的工作原理类似于
let
,但在
alet
主体的最后一种形式是lambda表达式时特别有用,因为它允许在lambda表达式实际出现在文本中之前使用它

这是宏的第一个版本,
alet%

(defmacro alet% (letargs &rest body)
  `(let ((this) ,@letargs)
     (setq this ,@(last body))
     ,@(butlast body)
     this))
到目前为止,一切顺利。但接下来,作者决定通过以下推理增强宏:

alet%
可以使其不返回其主体中的最后一个表单(我们预期为lambda表单),而是一个在let表单的词法范围内查找另一个函数的函数,然后调用该函数。这有时被称为间接寻址,因为我们不返回函数来执行某些操作,而是返回一个函数,该函数使用指针解引用查找函数,然后使用该函数。间接寻址是一个普遍存在于编程语言中的概念,这是有充分理由的。它允许我们在运行时更改在编译时固定的内容,而无需间接寻址

alet
定义为:

(defmacro alet (letargs &rest body)
  `(let ((this) ,@letargs)
     (setq this ,@(last body))
     ,@(butlast body)
     (lambda (&rest params)
       (apply this params))))

有什么区别?这个最终版本能做什么
alet%
做不到的事情
alet
返回lambda,该lambda将调用另一个lambda。它有什么好处?如果有人能给出一个使用间接寻址的例子,我们将不胜感激。

不同之处在于,使用间接寻址,您可以使用其他代码来更改主函数将返回的内容。考虑,例如,

(defun get-fn-1 ()
  (let ((fn (lambda () 42)))
    (prog1 fn
      (setq fn (lambda () 43)))))

(funcall (get-fn-1))
;=> 42
由于首先计算变量
fn
,然后返回值(返回42的函数),因此当调用
get-fn-1
的结果时,您会得到
42
,即使之后为
fn
分配了一个新函数。另一方面,如果返回调用
fn
的函数,则随后对
fn
的更改将反映在返回的函数中

(defun get-fn-2 ()
  (let ((fn (lambda () 42)))
    (prog1 (lambda () (funcall fn))
      (setq fn (lambda () 43)))))

(funcall (get-fn-2))
;=> 43
要了解差异真正有意义的地方,您需要一个更大的示例。这里有两个函数:一个是“状态函数”,告诉你处于什么状态,另一个是在状态之间“循环”。但这是一辆马车:

(defun get-state-fn-buggy ()
  (let* ((state-fns (list (lambda () 'state-1)
                         (lambda () 'state-2)
                         (lambda () 'state-3)))
         (state-fn (first state-fns)))
    ;; make the list circular, and then return as multiple values a
    ;; state function, and a function to cycle the state functions.
    (nconc state-fns state-fns)
    (values state-fn 
            (lambda () (setq state-fn (pop state-fns))))))
更好的实现使用间接寻址作为状态函数返回一个新函数,这样对
状态fn的“幕后”修改就会反映在我们得到的状态函数中

(defun get-state-fn ()
  (let* ((state-fns (list (lambda () 'state-1)
                          (lambda () 'state-2)
                          (lambda () 'state-3)))
         (state-fn (first state-fns)))
    ;; make the list circular, and then return as multiple values a
    ;; state function, and a function to cycle the state functions.
    (nconc state-fns state-fns)
    (values (lambda () (funcall state-fn))
            (lambda () (setq state-fn (pop state-fns))))))

我不确定
alet%
到底应该做什么。它看起来有点像
let
,但它首先计算最后一个身体形态,这非常违反直觉。例如,
(alet%((x1))(+xx)(setqx2))
变成
(LET((THIS)(x1))(setqthis(setqx2))(+xx)THIS)
。那似乎没那么有用。我希望
this
可能会绑定到
let
的变量列表中的最后一个变量。
alet%
的目的是什么?我认为
alet%
只有在其主体的最后一种形式是lambda时才有用。在其他情况下,它似乎不可用。好吧,再看一下文本,我看到这里的意图是最后一种形式应该是类似于词法闭包的东西(例如,lambda表达式),而之前的形式应该是某种“初始化形式”。这似乎仍然是一种奇怪的方式……问题是,如果我们已经要先计算最后一个表单,然后对它执行更多的操作,我们已经有了一个宏来完成这项工作,
prog1
。这实际上应该扩展到
(让绑定(prog1、@(最后一个主体)、@(但最后一个主体))
。要获得回指行为,只需将其更改为
(let(@,bindings this)(prog1(setq this,@(last body)),@(butlast body))
。当作者重新发明轮子时,总是有点困惑,因为它会导致难以理解的单一代码。我已经理解了它们之间的区别,但仍然难以想象实际的好处。@Mark我只是用一个稍微有用的例子进行了扩展。我同意这似乎有点做作。我认为这是一个在你真正拥有用例之前很难想象的用例。请注意,作者确实继续提供了一些更专门的示例
alet
可能只是更多讨论的一个“起点”。现在它不再那么模糊了。相当复杂的事情。。循环列表和弹出的技巧很优雅,谢谢!也许创建关于Lisp的聊天室是件好事。现有的版本中没有一个专门用于Lisp。我很好奇你认为Lisp是“第一语言”,我的意思是,出于教育目的。@Mark,现在有一个关于延续的问题,如果需要间接的话。返回一个函数,该函数只调用词法变量的值(在幕后重新定义)。它是一个生成器/迭代器的实现,所以它实际上有点实用。它不是CommonLisp,但是您可以在CommonLisp中使用相同的方法。
(defun get-state-fn ()
  (let* ((state-fns (list (lambda () 'state-1)
                          (lambda () 'state-2)
                          (lambda () 'state-3)))
         (state-fn (first state-fns)))
    ;; make the list circular, and then return as multiple values a
    ;; state function, and a function to cycle the state functions.
    (nconc state-fns state-fns)
    (values (lambda () (funcall state-fn))
            (lambda () (setq state-fn (pop state-fns))))))
(multiple-value-bind (state cycle)
    (get-state-fn)
  (list (funcall state)
        (progn (funcall cycle) (funcall state))
        (progn (funcall cycle) (funcall state))
        (progn (funcall cycle) (funcall state))))
;=> (state-1 state-1 state-2 state-3)
; good, now cycle has an effect on state