以下是OnLisp文本中的一些示例代码。我的问题是为什么使用lambda函数很麻烦,
`(funcall (alrec,rec #‘)(lambda (),base),@lst)
作为在cdrs定义中的第二个论点?
如果我只定义它而不使用lambda,有什么区别呢?
`(功能(alrec,rec,base),@lst)
(defun lrec (rec &optional base)
(labels ((self (lst)
(if (null lst)
(if (functionp base)
(funcall base)
base)
(funcall rec (car lst)
#'(lambda ()
(self (cdr lst)))))))
#'self))
(defmacro alrec (rec &optional base)
"cltl2 version"
(let ((gfn (gensym)))
`(lrec #'(lambda (it ,gfn)
(symbol-macrolet ((rec (funcall ,gfn)))
,rec))
,base)))
(defmacro on-cdrs (rec base &rest lsts)
`(funcall (alrec ,rec #'(lambda () ,base)) ,@lsts))发布于 2014-04-18 01:03:09
您没有说明这是如何被调用的,这段代码有点混乱,所以我一眼就不能说它应该如何工作。不过,我可以回答你的问题。
首先,让我说
(if (functionp base) (funcall base) base)是糟糕的编程风格。这实际上在语义空间中放置了一个整体,创建了与作为对象的其他事物完全不同的函数处理方式。在通用Lisp中,函数应该是可以选择传递的对象。如果你想要调用它,你应该这样做,但你不应该只是对某人说“如果我给你一个函数,你应该调用它,否则你不应该。”(当你读到这件事的时候,你会发现这很重要。)
第二,正如Barmar指出的那样,如果你写的话,你基本上是在说“拿起代码并插入到这里进行计算”。如果你写
#'(lambda () ,base) 您是说将代码放入函数中,以便延迟其执行。现在,您将它传递给一个函数,当它接收到该函数时,该函数将调用它。而且,调用它将在调用者的词法环境中评估它,并且在动态状态中不存在任何干预变化。因此,您可能会认为这与在呼叫站点上对其进行评估是一样的(除了稍微增加一些开销)。然而,有一种情况是不同的。
如果您输入的基本参数位置是一个变量(假设X)或一个数字(假设3),那么您将要么执行(lrec .X)或(lrec 3),否则你会做的
(lrec ... #'(lambda () X)) 或
(lref ... #'(lambda () 3))到目前一切尚好。如果它到达调用者,它会说:“哦,您只是指X(或3)的值。”但还有更多..。
如果您使用一个表达式来表示在调用on -cdr或调用alrec的基参数位置上生成一个函数,那么您将得到不同的结果,这取决于您是编写、base还是#'(lambda (),base)。例如,您可能会将
#'f 或
#'(lambda () x) 或者更糟的是,
#'(lambda (x) x)在基参数位置。在这种情况下,如果您使用了base,那么在将参数传递给lrec之前,将立即计算该表达式,然后lrec将接收一个函数。然后,它将被称为第二次(这可能不是宏用户所期望的那样,除非文档非常清楚这种不优雅,并且宏的用户已经足够仔细地阅读了文档)。在第一种情况下,它将返回3,在第二种情况下,它将返回x的值,在第三种情况下,会出现错误情况,因为它将使用错误的参数被调用。
如果您将其实现为
#'(lambda () ,base)然后,lrec将收到评估
#'(lambda () #'f)或
#'(lambda () #'(lambda () 3))或
#'(lambda () #'(lambda (x) x))这取决于你从上面的例子中给出的论点。但是在任何情况下,lrec得到的都是一个参数的函数,在计算时,它将返回评估其身体的结果,即返回一个函数。
最重要的措施是:
我希望这能帮助你看清不同之处。它很微妙,但它是真实的。
所以用
#'(lambda () ,base)保护宏不受可能产生函数的基的双重评估,但另一方面,不好的样式是不应该发生的(在我看来)。我的建议是删除对基的条件函数调用,使其始终或从不将基作为函数调用。如果你让它永远不调用这个函数,调用者就应该明确地使用base。如果总是调用函数,调用方肯定会包含lambda包装器。这将使评价的数量具有确定性。
此外,作为一个纯粹的实际问题,我认为它更符合Common的风格,只是使用它,而不是使用这个闭包,除非表达式要做的不仅仅是跨越函数调用边界,而是立即调用。这是对时间和精力的浪费,也许是额外的奉承,因为它实际上并没有达到任何有趣的目的。如果lrec函数的唯一用途是支持此功能,则尤其如此。如果lrec有一个独立的理由来拥有它那样的契约,那就另当别论了,也许您会编写自己的宏来适应。
在像Scheme这样的函数式语言中更常见的是,它有着不同的美学,它有一个规则的函数作为任何宏的替代,并且让这个函数使用这样的零参数函数作为参数,以防一些用户不喜欢使用宏。但是大多数情况下,通用Lisp程序员并不在意,您的问题是关于Common的,所以我的大部分写作都偏向于这种方言。
https://stackoverflow.com/questions/23001817
复制相似问题