我期待着Scheme的方法:
(defun mmap (f xs)
(if (equal xs NIL)
'()
(cons (f (car xs)) (mmap f (cdr xs)))))
但是我得到了编译器警告
;Compiler warnings for ".../test.lisp" :
; In MMAP: Undefined function F
我可以看到我需要让编译器意识到f
是一个函数。这与#'
有什么关系吗?
答案 0 :(得分:1)
Common Lisp是一个LISP2。这意味着在函数名称空间中计算表单第一个元素中的变量,而从变量名称空间计算其他所有变量。基本上它意味着您可以在参数中使用与函数相同的名称:
(defun test (list)
(list list list))
这里在函数名称空间中查找第一个list
,在变量名称空间中查找其他两个参数。首先,其余的变成两个不同的值,因为它们是从不同的地方取出的。如果你在Scheme中做同样的事情,那就是LISP1:
(define (test list)
(list list list))
传递一个不是带有两个参数的过程的值,然后你会得到一个错误,说list
不是一个过程。
函数名称空间中名为x
的函数可以使用特殊形式(function x)
从其他位置获取。与quote
function类似,语法糖等效,因此您可以编写#'x
。您可以将函数存储在变量名称空间(setq fun #'+)
中,然后将其传递给更高阶函数,如mapcar
(CL版本的Scheme map
)。由于表单中的第一个元素是特殊的,因此当函数绑定在变量名称空间中时,可以使用原始函数funcall:(funcall fun 2 3) ; ==> 5
(defun my-mapcar (function list)
(if list
(cons (funcall function (car list))
(my-mapcar function (cdr list)))
nil))
(my-mapcar #'list '(1 2 3 4)) ; ==> ((1) (2) (3) (4))
使用循环的版本,这是确保不在CL中打击堆栈的唯一方法:
(defun my-mapcar (function list)
(loop :for x :in list
:collect (funcall function x)))
(my-mapcar #'list '(1 2 3 4)) ; ==> ((1) (2) (3) (4))
当然,mapcar可以使用多个列表参数,函数map就像mapcar,但它适用于所有序列(列表,数组,字符串)
;; zip
(mapcar #'list '(1 2 3 4) '(a b c d)) ; ==> ((1 a) (2 b) (3 c) (4 d))
;; slow way to uppercase a string
(map 'string #'char-upcase "banana") ; ==> "BANANA"
CL中的函数接受函数作为参数也接受符号。例如。 #'+
是一个函数,而'+
是一个符号。会发生的是它通过在全局命名空间中获取由#'+
表示的函数来检索+
。因此:
(flet ((- (x) (* x x)))
(list (- 5) (funcall #'- 5) (funcall '- 5))) ; ==> (25 25 -5)
Trivia :我认为LISP1和LISP2中的数字是指名称空间而不是版本的数量。第一个LISP interpereter只有一个命名空间,但双命名空间是LISP 1.5的一个特性。在商业版本接管之前,这是他最后的LISP版本。 LISP2计划但从未完成。 Scheme最初是作为MacLisp中的解释器编写的(它基于LISP 1.5并且是LISP2)。肯特皮特曼compared the two approaches in 1988。