且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

使用来自函数的lambda值作为列表的第一个元素

更新时间:2022-12-03 07:51:00

这是一个很好的问题,Common Lisp可能会使您非常困惑.事实是,由于历史原因,Common Lisp具有两个名称空间-一个用于函数,另一个用于值.为此,对于函数应用程序的头位置和其余部分有两种不同的评估规则-第一种将符号作为函数名称进行评估,第二种将符号作为变量引用进行评估.显然,在某些情况下,该值实际上是一个函数-例如,如果编写mapcar函数,则需要执行类似

This is a good question, and one where Common Lisp can be pretty confusing. The thing is that due to historical reasons, Common Lisp has two namespaces -- one for functions and another for values. To make this happen, there are two different evaluation rules for the head position of a function application and for the rest -- the first will evaluate a symbol as a function name, and the second will evaluate a symbol as a variable reference. There are obviously cases when the value is actually a function -- for example, if you write a mapcar function, you'll want to do something like

(defun my-mapcar (f l)
  (if (null l)
    '()
    (cons (f (car l)) (my-mapcar f (cdr l)))))

,但是这行不通-它会抱怨f是一个未知函数.在这些情况下,有一个名为funcall的特殊函数,该函数接收该函数的函数和参数,并将像往常一样应用该函数-并且由于funcall是普通函数,因此其参数均按常规方式进行评估(作为值).因此,应通过使用以下方法解决上述问题:

but this won't work -- it will complain about f being an unknown function. For these cases there is a special function called funcall, which receives a function and argument for the function, and will apply the function as usual -- and since funcall is a plain function, its arguments are all evaluated as usual (as values). So the above should be fixed by using it:

(defun my-mapcar (f l)
  (if (null l)
    '()
    (cons (funcall f (car l)) (my-mapcar f (cdr l)))))

您现在可能会怀疑,有一些镜像情况-您想将某些东西作为函数求值,而将 not 当作值来求值.例如,这不起作用:

As you probably suspect now, there are the mirror cases -- where you want to evaluate something as a function and not as a value. For example, this doesn't work:

(my-mapcar 1+ '(1 2 3))

因为它引用1+变量而不是函数.对于这些情况,有一种称为function的特殊形式,该形式将其内容作为函数求值并作为值返回:

because it refers to the 1+ variable and not the function. For these cases there is a special form called function that evaluates its contents as a function and returns it as a value:

(my-mapcar (function 1+) '(1 2 3))

,并且可以缩写为#':

(my-mapcar #'1+ '(1 2 3))

这还不是故事的结局-举几个例子:

That's not the end of this story -- to give a few examples:

  • 在某些情况下,可以使用简单的带引号的名称作为函数-例如,在上一个示例中的'1+可以使用-但这是一种黑客,只能看到全局绑定的名称,因此#'几乎总是更好

  • in some cases a simple quoted name can work as a function -- for example '1+ in the last example works -- but this is a kind of a hack that can see only globally bound names, so #' is almost always better

类似的hack可以与lambda一起使用-因此您可以使用(my-mapcar '(lambda (x) (1+ x)) '(1 2 3)),实际上,您可以使用(list 'lambda '(x) '(1+ x)),它甚至更糟(并且是IIRC,不可携带),但是使用(lambda (x) (1+ x))有效,因为它隐式包装在#'中(尝试将lambda形式扩展为宏,您会看到它).相关的技巧可以很好地将lambda表达式用作函数应用程序的头(这是您尝试过的事情之一).

a similar hack can be used with lambda -- so you can use (my-mapcar '(lambda (x) (1+ x)) '(1 2 3)), in fact, you could use (list 'lambda '(x) '(1+ x)) which is even worse (and IIRC, non-portable), but using (lambda (x) (1+ x)) works since it's implicitly wrapped in a #' (try expanding a lambda form as a macro and you'll see it). A related hack makes it fine to use a lambda expression as the head of a function application (which is one of the things you've tried).

let等绑定局部值,在某些情况下,您将想绑定局部函数-为此,有新的绑定结构:fletlabels

let etc bind local values, and in some cases you'll want to bind local functions instead -- for this, there are new binding constructs: flet and labels

如果所有这些看起来都怪异和/或过于复杂,那么您并不孤单.这是Common Lisp和Scheme之间的主要区别之一. (然后,差异会导致两种语言的通用习语发生变化:方案代码比起普通Lisp代码,倾向于更频繁地使用高阶函数.像往常一样,对于此类宗教问题,有些人主张CL的用法,声称高阶函数令人困惑,因此它们喜欢显式的代码内提示.)

If all of this looks weird and/or overly complicated, then you're not alone. It's one of the main differences between Common Lisp and Scheme. (The difference then leads to changes in common idioms in both languages: Scheme code tends to use higher order functions much more frequently than Common Lisp code. As usual with these kind of religious questions, some people argue in favor of what CL does, claiming that higher order functions are confusing so they like the explicit in-code reminder.)