我想从列表'b 中过滤掉列表'a 的所有元素并返回过滤后的'b。这是我的功能:
(defun filter (a b)
"Filters out all items in a from b"
(if (= 0 (length a)) b
(filter (remove (first a) a) (remove (first a) b))))
我是 lisp 的新手,不知道 'remove 是怎么做的,这个过滤器会在什么时间运行?
我想从列表'b 中过滤掉列表'a 的所有元素并返回过滤后的'b。这是我的功能:
(defun filter (a b)
"Filters out all items in a from b"
(if (= 0 (length a)) b
(filter (remove (first a) a) (remove (first a) b))))
我是 lisp 的新手,不知道 'remove 是怎么做的,这个过滤器会在什么时间运行?
有两种方法可以查到:
你可以用数据测试它
你可以分析你的源代码
让我们看一下源代码。
列表由链接的 cons 单元格构成
length 需要遍历一个列表
对于 FILTER 的每次递归调用,您都会计算 a 的长度。坏的!
(改用 ENDP。)
REMOVE 需要遍历一个列表
对于每个递归调用,您计算 REMOVE 两次:不好!
(而不是使用 REMOVE on a
,使用 REST 递归。)
对 FILTER 的调用不一定是优化的尾调用。在某些实现中,它可能,在某些实现中,您需要告诉编译器您要针对尾调用进行优化,在某些实现中,没有可用的尾调用优化。如果没有,那么您会在足够长的列表上得到堆栈溢出。
(如果适用,请使用 DO、DOLIST、DOTIMES、LOOP、REDUCE、MAPC、MAPL、MAPCAR、MAPLIST、MAPCAN 或 MAPCON 等循环结构,而不是递归。)
摘要:这是非常幼稚的代码,性能很差。
Common Lisp 内置了这个:SET-DIFFERENCE 应该做你想做的事。
http://www.lispworks.com/documentation/HyperSpec/Body/f_set_di.htm#set-difference
Common Lisp 不支持尾调用优化(根据标准),并且您可能会因为糟糕的调用堆栈而耗尽内存(取决于实现)。
我不会写这个函数,因为正如Rainer Joswig 所说,标准已经提供了SET-DIFFERENCE
. 尽管如此,如果我必须提供该函数的实现,这就是我会使用的:
(defun filter (a b)
(let ((table (make-hash-table)))
(map 'nil (lambda (e) (setf (gethash e table) t)) a)
(remove-if (lambda (e) (gethash e table)) b)))
这样做有几个好处,最重要的是它只遍历b
一次;如果很长,使用哈希表来跟踪其中的元素a
可能会执行得更好。a
此外,使用通用序列函数(如MAP
和)REMOVE-IF
意味着该函数可以与字符串和向量以及列表一起使用,这甚至优于标准SET-DIFFERENCE
函数。这种方法的主要缺点是,如果您想使用:TEST
允许用户提供除默认值之外的相等谓词的参数来扩展函数EQL
,因为 CL 哈希表仅适用于少量预定义的相等谓词(EQ
, EQL
,EQUAL
准确地说EQUALP
)。
(defun filter (a b)
"Filters out all items in a from b"
(if (not (consp a)) b
(filter (rest a) (rest b))))