在Common Lisp中编写lambda表达式

19

我目前正在阅读Paul Graham所写的《ANSI Common Lisp》,有一个关于编写lambda表达式的问题。

我们需要在lambda表达式前加上#'吗?如果我在REPL中这样写,它将正常工作。

> ((lambda (x) (+ x 1)) 1)
  2

那么这样做吧。

> (mapcar (lambda (x) (+ x x)) '(1 2 3 4))
  (2 4 6 8)

我知道#'表示函数。因此我的问题是,它是一种约定俗成的做法还是推荐的做法?如果我不用#'前缀来定义lambda表达式是否会出现问题?这取决于实现吗?


2
你可能想看一下我的旧回答 - danlei
1个回答

27

LAMBDA表达式

(lambda ...)在某些情况下被视为lambda表达式,例如在function表单中或作为函数调用的头部。Lambda表达式不会被评估。

(function              ; special operator FUNCTION
  (lambda () 'foobar)) ; <- this is a lambda expression


(                    ; a function call
 (lambda (foo) foo)  ; <- this is a lambda expression
 'bar                ; argument
)

但这里的(lambda ...)是一个宏形式而不是一个lambda表达式:

(funcall             ; calling a function via funcall
 (lambda (foo) foo)  ; this is not a lambda expressions, but the macro lambda
                     ;  as all arguments to FUNCALL it will be
                     ;    macro expanded and evaluated
                     ;  it expands to (function (lambda (foo) foo))
 'bar)               ; argument

LAMBDA宏

LAMBDA是一个宏。它将(lambda ...)扩展为(function (lambda ...)),这相当于#'(lambda ...))

CL-USER > (macroexpand '(lambda (foo) foo))
(FUNCTION (LAMBDA (FOO) FOO))

这个宏(macro)只是为了让你少写/读一点而已。在Common Lisp的第一个版本(CLtL1)中,没有LAMBDA宏。后来加入了这个宏,现在已经成为ANSI Common Lisp的一部分。

FUNCTION特殊运算符

FUNCTION是一个特殊运算符。它需要一个函数名或lambda表达式。因此,名称或lambda表达式不会被计算。事实上,lambda表达式根本无法被计算。在FUNCTION内部,lambda表达式不是宏形式,因此不会再次展开。FUNCTION的目的是返回由名称或lambda表达式表示的相应函数对象,并将其作为值返回。使用此特殊运算符,可以从全局函数和词法函数中访问函数对象。

在Common Lisp中,FUNCTION运算符是必需的,因为它具有单独的命名空间用于值、函数和其他一些东西。它是所谓的或甚至,具有两个或多个命名空间。

函数形式中的lambda表达式

((lambda (foo) foo) 10)由Common Lisp内置语法支持。请参见Lambda Forms

令人困惑

这都是合乎逻辑的,但很令人困惑。不用担心,你并不孤单,但在实践中这并不是大问题。


1
谢谢,我还是不明白为什么人们在lambda表达式前面明确地写#'。 - turingcomplete
3
这是一种风格问题,有些人喜欢它,有些人不喜欢。我喜欢它,因为它可以轻松地识别实际用作值的函数。它还使将函数替换为 fletlabels 函数更加容易。除了 lambda 函数之外,它还使得函数定义可以被查找。如果你不知道一个实现是否提前强制转换 函数指示器,这也许是可取的,例如 (mapcar 'frob my-list) 可能会在 my-list 中的每个元素上查找 frobsymbol-function - acelent
3
Doug Hoyte 提出了一个不使用 #' 语法的论点。Rainer 在这里给出了提示;事实是 ((lambda (foo) 是内置语法,并且有一个 (lambda 宏,这意味着在使用读取宏时,您可以利用这种语法的二元性来做一些有趣的事情。例如,请查看 Hoyte 的 cl-ppcre 读取宏扩展:http://letoverlambda.com/index.cl/guest/chap4.html#sec_4 - Clayton Stanley
1
关于claytonstanleys的评论:快速警告,“Let Over Lambda”包含一些宏技术,有些人认为这种风格和可维护性值得商榷。我喜欢这本书,并且衷心推荐购买它,但请注意其中存在深奥的魔法,你应该认真考虑未来会查看你代码的最终用户/编码人员。 - Baggers

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接