1

在“Let Over Lambda”的第 6 章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。它有什么好处?如果有人可以举一个间接使用的例子,那将不胜感激。

4

1 回答 1

1

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

(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))))))
(multiple-value-bind (state cycle)
    (get-state-fn-buggy)
  (list (funcall state)
        (progn (funcall cycle) (funcall state))
        (progn (funcall cycle) (funcall state))
        (progn (funcall cycle) (funcall state))))
;=> (state-1 state-1 state-1 state-1)
; uh oh, we wanted different states

更好的实现返回一个使用间接作为状态函数的新函数,以便“幕后”修改state-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))))))
(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
于 2014-06-06T17:32:46.920 回答