簡體   English   中英

為什么 Common-Lisp Lambda 表達式是有效的函數名?

[英]Why is a Common-Lisp Lambda expression a valid function name?

所以假設我想調用一些函數。 如果我用 defun 定義了函數,我只需在列表的開頭使用函數的名稱,然后使用它的參數,就像這樣(我將在示例中使用“=>”來顯示輸入代碼的輸出進入 CLisp REPL):

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

足夠簡單。 列表的第一個元素必須是函數、特殊運算符或宏的名稱。 除此之外,以下內容也有效:

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

快速谷歌搜索顯示 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 是其他硬設置規則的特殊例外,即評估表達式的第一個元素必須是某個操作的名稱,還是有一些我誤解的更一致的規則集?

Lambda 表達式和函數名稱

lambda 表達式不是函數名 Common Lisp 中的函數名稱被定義為符號(setf 符號) lambda 表達式基本上是描述匿名函數的內置語法。

請注意,lambda 表達式本身在 Common Lisp 中沒有意義。 它們僅以lambda 形式(見下文)和帶有特殊運算符function的形式出現。

列表形式

LAMBDA 是其他硬設置規則的特殊例外,即評估表達式的第一個元素必須是某個操作的名稱,還是有一些我誤解的更一致的規則集?

Common Lisp 規范定義只有四種基於列表的表單 表單是一段有效的 Lisp 代碼。

  • 特殊形式(形式以特殊運算符開頭)
  • 宏形式(形式以宏運算符開頭)
  • 函數形式(形式以函數運算符開頭)
  • lambda 形式(該形式以 lambda 表達式開頭)

參見 Common Lisp HyperSpec: Conses as Forms

請注意,Common Lisp 中沒有擴展此功能的機制。 只有這四種類型的基於列表的表單。 人們可以將擴展想象成:數組作為函數,CLOS 對象作為函數,不同類型的函數,如 fexprs、變量,...... Common Lisp 語法都不支持基於列表的表單,並且沒有可移植的機制來添加這些.

拉姆達

LAMBDA在 Common Lisp 中有兩個不同的用途:

  • 它是lambda 表達式的頭部。
  • 作為宏LAMBDA 這將(lambda ....)擴展為(function (lambda ....))

在第一個語言定義 CLtL1 之后,宏LAMBDA被添加到 Common Lisp 中,以便能夠編寫(lambda (x) x)而不是(function (lambda (x) x))#'(lambda (x) x) 因此它是函數特殊運算符形式的縮寫,使代碼看起來更簡單,更像Scheme

摘自《 實踐通用Lisp》第5章

想到LAMBDA表達式的一種方法是將其本身直接描述函數的功能,這是一種特殊的函數名稱。 這解釋了為什么可以在帶有#'的函數名稱的地方使用LAMBDA表達式。

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

您甚至可以在函數調用表達式中使用LAMBDA表達式作為函數的“名稱”。 如果需要,可以更簡潔地編寫以前的FUNCALL表達式。

((lambda (xy) (+ xy)) 2 3) ==> 5

但這幾乎從未完成; 只是值得一提的是,為了強調LAMBDA表達式可以在正常函數名稱可以使用的任何地方使用是合法的。

關於您的問題:

LAMBDA是否是否則為硬性規定的特殊例外,該硬性規定是被求值表達式的第一個元素必須是某個操作的名稱,還是我誤會了一些更一致的規則集?

從技術上講 不是 ,因為lambda表達式 合法的函數名

編輯 :Lambda表達式不是函數名稱,但是您可以將它們視為函數名稱,其中“名稱本身直接描述了函數的功能”。

另請參見為什么在Common Lisp中的lambda之前使用#'?

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

  • 您將函數存儲在符號f的值單元格中,並且
  • 讀者期望其功能單元格中帶有值的符號,而#'f不是

我認為問題在於 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))

報價顯示更明確here

#<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

可以想象以下轉換發生在評估一個函數(即汽車沒有命名宏或特殊運算符)表達式時:

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

並將function理解為特殊運算符,它將表達式第一個元素中的內容轉換為可以調用的實際函數運算符。 它是將 lambda 表達式(主要是在編譯時)轉換為可以調用的閉包的function

最后請注意#'foo(function foo)簡寫。

在實踐或規范中,這不是實際工作的方式,因為(function #'(setf foo))是完全有效的並且可以評估為存在的函數,但是((setf foo) arg1 arg2 ...)不能是一個有效的函數調用。

然而,這確實解釋了為什么像(#'fx)這樣的表達式是無效的,原因是(function (function f))不是一個有效的表達式。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM