为什么Common-Lisp Lambda表达式是有效的函数名?

时间:2019-01-16 03:59:14

标签: lambda common-lisp function-call

所以说我想调用一些函数。如果我已使用defun定义了函数,则只需在列表的开头使用函数名称,后跟它的参数即可(在示例中,我将使用“ =>”来显示输入代码的输出进入CLisp REPL):

(defun f (a) (lambda (b) (+ a b))) => F
(f 12) => #<FUNCTION :LAMBDA (B) (+ A B)>

足够简单。列表的第一个元素必须是函数,特殊运算符或宏的名称。除此之外,以下内容也有效:

((lambda (a) (+ a 12)) 1) => 13

一个快速的Google搜索显示LAMBDA既是符号又是宏。尝试扩展宏类:

(macroexpand '(lambda (a) (+ a 12))) => #'(LAMBDA (A) (+ A 12))

这没有帮助。我无法区分宏LAMBDA和符号LAMBDA,因此我不清楚为什么可以使用lambda表达式作为函数名,但不能使用#'f,据我所知,应该以与#'(LAMBDA(A)(+ A 12))相同的方式对函数F求值为有效的函数标识符,

(#'f 12) => *** - EVAL: #'F is not a function name; try using a symbol instead

LAMBDA是硬设置规则的特殊例外,否则硬设置规则是:求值表达式的第一个元素必须是某些操作的名称,还是我误解了一些更一致的规则集?

4 个答案:

答案 0 :(得分:3)

取自Practical Common Lisp, Chapter 5

  

思考LAMBDA表达式的一种方法是将其作为一种特殊的   函数名称,其中名称本身直接描述了   功能呢。这说明了为什么可以在其中使用LAMBDA表达式   函数名称带有#'的位置。

     

(funcall #'(lambda (x y) (+ x y)) 2 3) ==> 5

     

您甚至可以将LAMBDA表达式用作函数中的函数的“名称”   函数调用表达式。如果需要,您可以编写上一个   FUNCALL表达式更加简洁。

     

((lambda (x y) (+ x y)) 2 3) ==> 5

     

但这几乎从未完成;值得一提的是   为了强调可以使用LAMBDA表达式而合法   可以使用普通函数名称的任何地方。

关于您的问题:

  

LAMBDA是原本硬性规定的特殊例外,   计算表达式的第一个元素必须是某些表达式的名称   操作,还是我有一些更一致的规则集   误会?

技术上,因为lambda表达式 合法函数名称

Edit :Lambda表达式不是 函数名称,但是您可以将它们视为函数名称,其中“名称本身直接描述了函数的功能。”

另请参见Why #' is used before lambda in Common Lisp?

(#'f 12)不起作用的原因是因为

  • 您将函数存储在符号f的值单元格中,并且
  • 读者期望符号在其功能单元格中带有值,而#'f不是

答案 1 :(得分:3)

Lambda表达式和函数名称

lambda表达式不是函数名称。 Common Lisp中的功能名称被定义为符号(setf符号) lambda表达式基本上是用于描述匿名函数的内置语法。

请注意,lambda表达式本身在Common Lisp中没有意义。它们仅以 lambda形式(见下文)出现,并在带有特殊运算符function的表单中出现。

列为表单

  

LAMBDA是硬设置规则的特殊例外吗?硬设置规则是,求值表达式的第一个元素必须是某个操作的名称,还是我误会了一些更一致的规则集?

Common Lisp规范定义只有四个基于列表的 forms form 是有效的Lisp代码段。

  • 特殊形式(该形式以特殊运算符开头)
  • 宏表格(该表格以宏运算符开头)
  • 函数表单(表单以函数运算符开头)
  • lambda形式(该形式以lambda表达式开头)

请参见Common Lisp HyperSpec:Conses as Forms

请注意,Common Lisp中没有机制可以扩展它。只有四种基于列表的表单。人们可能会想到扩展:数组作为函数,CLOS对象作为函数,不同类型的函数,例如fexprs,变量等... Common Lisp语法都不支持基于列表的表单,也没有可移植的机制来添加这些扩展

LAMBDA

LAMBDA在Common Lisp中有两个不同的用途:

  • 它是 lambda表达式的头。
  • 作为宏LAMBDA。这会将(lambda ....)扩展为(function (lambda ....))

在第一个语言定义CLtL1之后,将宏LAMBDA添加到Common Lisp,以方便编写(lambda (x) x)而不是(function (lambda (x) x))#'(lambda (x) x)。因此,它是特殊操作符形式的缩写,使代码看起来更简单,更像方案

答案 2 :(得分:1)

我认为问题出在lisp打印机上,实现向我们展示了两件事完全相同的一面,而从内部看,它实际上是一个列表而不是一个函数。

通常且令人困惑的模式显示:

<i>[hao@wendy:~]$ ecl
;;; Loading "/nix/store/j48bf40ssnzgil3qmdc759y0navk079d-ecl-readline/lib/init.lsp"
n;;; Loading "/nix/store/j48bf40ssnzgil3qmdc759y0navk079d-ecl-readline/lib/ecl-readline.fas"
ix-;;; Loading "/nix/store/j48bf40ssnzgil3qmdc759y0navk079d-ecl-readline/lib/ecl-completions.fas"
sECL (Embeddable Common-Lisp) 16.1.3 (git:UNKNOWN)
Copyright (C) 1984 Taiichi Yuasa and Masami Hagiya
Copyright (C) 1993 Giuseppe Attardi
Copyright (C) 2000 Juan J. Garcia-Ripoll
Copyright (C) 2016 Daniel Kochmanski
ECL is free software, and you are welcome to redistribute it
under certain conditions; see file 'Copyright' for details.
Type :h for Help.  
Top level in: #<process TOP-LEVEL>.
h+CL-USER[1]> (macroexpand '(lambda (a) (+a 12)))
#'(LAMBDA (A) (+A 12))
T
+CL-USER[2]> (equal (macroexpand '(lambda (a) (+ a 12))) '(lambda (a) (+ a 12)))
NIL
+CL-USER[3]> (map 'list #'type-of (list (macroexpand '(lambda (a) (+ a 12))) '(lambda (a) (+ a 12))))
(CONS CONS)
+CL-USER[4]> (map 'list #'functionp (list (macroexpand '(lambda (a) (+ a 12))) '(lambda (a) (+ a 12))))
(NIL NIL)
+CL-USER[5]> (map 'list #'car (list (macroexpand '(lambda (a) (+ a 12))) '(lambda (a) (+ a 12))))
#'LAMBDA

似乎唯一的区别只是hashquote

+CL-USER[8]> (ignore-errors (list *print-escape* *print-readably* *print-case* *print-circle* *print-level* *print-length* *print-pretty*))
(T NIL :UPCASE NIL NIL NIL T)
+CL-USER[9]> (let ((*print-readably* t)) (list (macroexpand '(lambda (a) (+ a 12))) '(lambda (a) (+ a 12)))))
(#'(LAMBDA (A) (+ A 12)) (LAMBDA (A) (+ A 12)))
;;; Warning: Ignoring an unmatched right parenthesis.
+CL-USER[10]> (setq *print-pretty* nil)
NIL
+CL-USER[11]> (setq the-funs (list (macroexpand '(lambda (a) (+ a 12))) '(lambda (a) (+ a 12))))
(#'(LAMBDA (A) (+ A 12)) (LAMBDA (A) (+ A 12)))
+CL-USER[12]> *print-pretty* 
NIL

我试图强迫打印机明确显示定义[22]的(function (lambda ...))部分,但我不记得上一次我做同样的事情试图找出为什么巧妙地出现的原因只是开始对Lisp信任时

+CL-USER[21]> (type-of (car the-funs)))
CONS
;;; Warning: Ignoring an unmatched right parenthesis.
+CL-USER[22]> (car (car the-funs))
FUNCTION

我们实际上要使用的是列表/缺点(下面)和功能(上面)

+CL-USER[23]> (values #1=(cadr the-funs) (type-of #1#))
(LAMBDA (A) (+ A 12))
CONS

也许差异不只是视觉上的印刷字符;而是这是SBCL:

<i>[hao@wendy:~]$ sbcl
This is SBCL 2.0.0.nixos, an implementation of ANSI Common Lisp.
More information about SBCL is available at <http://www.sbcl.org/>.

SBCL is free software, provided as is, with absolutely no warranty.
It is mostly in the public domain; some portions are provided under
BSD-style licenses.  See the CREDITS and COPYING files in the
distribution for more information.
* (setq fs (list (macroexpand '(lambda (x) (1+ x))) '(lambda (x) (1+ x))))
; in: SETQ FS
;     (SETQ FS (LIST (MACROEXPAND '(LAMBDA # #)) '(LAMBDA (X) (1+ X))))
; 
; caught WARNING:
;   undefined variable: COMMON-LISP-USER::FS
; 
; compilation unit finished
;   Undefined variable:
;     FS
;   caught 1 WARNING condition
(#'(LAMBDA (X) (1+ X)) (LAMBDA (X) (1+ X)))

* (let (*print-pretty*) (print (lambda (x) (1+ x))))

#<FUNCTION (LAMBDA (X)) {52B1CABB}> 
#<FUNCTION (LAMBDA (X)) {52B1CABB}>
* (let (*print-pretty*) (print ' (lambda (x) (1+ x))))

(LAMBDA (X) (1+ X)) 
(LAMBDA (X) (1+ X))

报价在这里更明确地显示

#<FUNCTION (LAMBDA (X)) {52B1CABB}> 
(LAMBDA (X) (1+ X))

树和类型与ECL所说的有些相似。

* (mapcar #'functionp fs) 
(NIL NIL)
* (mapcar #'type-of fs)
(CONS CONS)
* (mapcar #'car fs)
#'LAMBDA
* (values (car (car fs)) (car (cdr fs)))
FUNCTION
(LAMBDA (X) (1+ X))

最后,(不要带走)穿过树林的随机树确认我们可以遍历列表,但是该功能只是封闭

* (defun walk-tree (fun tree)
  (subst-if t
            (constantly nil)
            tree
            :key fun))
WALK-TREE
* (walk-tree 'print (lambda (x) (1+ x)))

#<FUNCTION (LAMBDA (X)) {52B1CD5B}> 
#<FUNCTION (LAMBDA (X)) {52B1CD5B}>
* (walk-tree 'print '(lambda (x) (1+ x))))

(LAMBDA (X) (1+ X)) 
LAMBDA 
((X) (1+ X)) 
(X) 
X 
NIL 
((1+ X)) 
(1+ X) 
1+ 
(X) 
X 
NIL 
NIL 
(LAMBDA (X) (1+ X))
* (defun walk-tree-atoms (fun tree)
  (tree-equal tree tree
              :test (lambda (element-1 element-2)
                      (declare (ignore element-2))
                      (funcall fun element-1)
                      t)))
WALK-TREE-ATOMS
* (walk-tree-atoms 'print (lambda (x) (1+ x)))

#<FUNCTION (LAMBDA (X)) {52B1CF9B}> 
T
* (walk-tree-atoms 'print '(lambda (x) (1+ x)))

LAMBDA 
X 
NIL 
1+ 
X 
NIL 
NIL 
T
* (quit)

<i>[hao@wendy:~]$ 

步行功能来自LispTips,可悲的是,它似乎已被删除,当我读到这篇文章时,我只是有个回弹,您仍然可以在这里查看档案: https://web.archive.org/web/20191204131626/https://lisptips.com/post/43404489000/the-tree-walkers-of-cl

您还可以查看整个会话,我将其粘贴到了要点上: https://gist.github.com/LaloHao/fd6499b68cc98cf440aad6447ebd9b89

答案 3 :(得分:0)

人们可以想象以下翻译正在发生,以评估一个函数(即汽车未命名宏或特殊运算符)表达式:

(foo arg1 arg2 ...)
~~~>
(funcall (function foo) arg1 arg2 ...)

并且将function理解为特殊运算符,它将将表达式的第一个元素中出现的内容转换为一个可以调用的实际函数运算符。 function会将lambda表达式(通常在编译时)转换为可以调用的闭包。

最后请注意,#'foo(function foo)的简写。

这不是事物在实际中或在规范中的实际工作方式,因为(function #'(setf foo))完全有效,并且可以评估为存在一个函数,但是((setf foo) arg1 arg2 ...)不能是有效的函数调用。 / p>

但这确实解释了为什么像(#'f x)这样的表达式无效,原因是(function (function f))不是有效的表达式。