首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >Common Lisp:在非常大的列表上使用这个过滤函数的缺点是什么?

Common Lisp:在非常大的列表上使用这个过滤函数的缺点是什么?
EN

Stack Overflow用户
提问于 2010-07-27 12:55:18
回答 4查看 1.3K关注 0票数 4

我想从list 'b中过滤掉list 'a‘的所有元素,并返回过滤后的'b’。这是我的函数:

代码语言:javascript
运行
复制
(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‘是怎么做的,这个过滤器会在什么时候运行?

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2010-07-27 15:33:27

有两种方法可以找到答案:

  • 您可以使用data
  • 测试它您可以分析您的源代码

让我们看一下源代码。

  • list是由链接的cons单元格构建的,
  • length需要遍历一次list
  • 对于每一个递归调用的过滤器,你计算一个的长度。坏!

(使用ENDP instead.)

  • REMOVE需要对每个递归调用遍历一次list

  • ,计算删除两次:糟糕!

(不是在a__上使用REMOVE,而是与其余部分一起递归。)

  • 对FILTER的调用不一定是优化的尾部调用。在一些实现中,你可能需要告诉编译器你想要优化尾部调用,而在一些实现中,尾部调用优化是不可用的。如果不是,那么在足够长的列表上就会出现堆栈溢出。

(当为applicable.)时,使用循环结构,如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

票数 10
EN

Stack Overflow用户

发布于 2010-07-27 15:05:52

Common Lisp不支持tail-call optimization (根据标准),您可能会因为一个糟糕的调用堆栈(取决于实现)而耗尽内存。

票数 1
EN

Stack Overflow用户

发布于 2010-07-30 03:11:21

我不会写这个函数,因为作为Rainer Joswig says,标准已经提供了SET-DIFFERENCE。尽管如此,如果我必须提供该函数的实现,我会使用以下方法:

代码语言:javascript
运行
复制
(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中的元素可能会执行得更好。

此外,使用像MAPREMOVE-IF这样的通用序列函数意味着该函数可以与字符串和向量以及列表一起使用,这比标准的SET-DIFFERENCE函数更有优势。这种方法的主要缺点是如果您希望使用:TEST参数扩展函数,该参数允许用户提供默认EQL以外的相等谓词,因为CL哈希表只使用少量预定义的相等谓词(准确地说是EQEQLEQUALEQUALP )。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/3340837

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档