首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >通过谓词将列表过滤成两个部分

通过谓词将列表过滤成两个部分
EN

Stack Overflow用户
提问于 2013-08-08 02:25:17
回答 3查看 1.9K关注 0票数 5

我想做

代码语言:javascript
复制
(filter-list-into-two-parts #'evenp '(1 2 3 4 5))
; => ((2 4) (1 3 5))

根据谓词的计算结果是否为true,将列表拆分为两个子列表。很容易定义这样一个函数:

代码语言:javascript
复制
(defun filter-list-into-two-parts (predicate list)
  (list (remove-if-not predicate list) (remove-if predicate list)))

但是我想知道在Lisp中是否有一个内置函数可以做到这一点,或者是否有一种更好的方法来编写这个函数?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2013-08-08 02:34:37

我不认为有内置的,而且您的版本是次优的,因为它遍历列表两次,并在每个list元素上调用两次谓词。

代码语言:javascript
复制
(defun filter-list-into-two-parts (predicate list)
  (loop for x in list
    if (funcall predicate x) collect x into yes
    else collect x into no
    finally (return (values yes no))))

我返回两个值,而不是其中的列表;这更符合惯例(您将使用multiple-value-bind从返回的多个值中提取yesno,而不是使用destructuring-bind来解析列表,而是更少、更快,参见通用Lisp中的值函数)。

更一般的版本是

代码语言:javascript
复制
(defun split-list (key list &key (test 'eql))
  (let ((ht (make-hash-table :test test)))
    (dolist (x list ht)
      (push x (gethash (funcall key x) ht '())))))
(split-list (lambda (x) (mod x 3)) (loop for i from 0 to 9 collect i))
==> #S(HASH-TABLE :TEST FASTHASH-EQL (2 . (8 5 2)) (1 . (7 4 1)) (0 . (9 6 3 0)))
票数 8
EN

Stack Overflow用户

发布于 2013-08-08 07:34:38

使用REDUCE

代码语言:javascript
复制
(reduce (lambda (a b)
          (if (evenp a)
              (push a (first b))
            (push a (second b)))
          b)
        '(1 2 3 4 5)
        :initial-value (list nil nil)
        :from-end t)
票数 7
EN

Stack Overflow用户

发布于 2013-08-10 16:14:19

我认为在普通的lisp标准中没有分区函数,但是有提供了这样一个实用程序(包括文档和来源)。

代码语言:javascript
复制
CL-USER> (ql:quickload :arnesi)
CL-USER> (arnesi:partition '(1 2 3 4 5) 'evenp 'oddp)
((2 4) (1 3 5))
CL-USER> (arnesi:partition '(1 2 b "c") 'numberp 'symbolp 'stringp)
((1 2) (B) ("c"))
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/18116949

复制
相关文章

相似问题

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