常见的lisp中的map的递归定义

时间:2014-08-20 20:45:28

标签: function recursion common-lisp function-pointers

我期待着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是一个函数。这与#'有什么关系吗?

1 个答案:

答案 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