通过谓词将列表过滤为两部分

wro*_*ame 4 lisp common-lisp filter

我想要做

(filter-list-into-two-parts #'evenp '(1 2 3 4 5))
; => ((2 4) (1 3 5))
Run Code Online (Sandbox Code Playgroud)

其中列表被拆分为两个子列表,具体取决于谓词的计算结果是否为true.定义这样的函数很容易:

(defun filter-list-into-two-parts (predicate list)
  (list (remove-if-not predicate list) (remove-if predicate list)))
Run Code Online (Sandbox Code Playgroud)

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

sds*_*sds 7

我认为没有内置版本,并且您的版本不是最理想的,因为它遍历列表两次并在每个列表元素上调用谓词两次.

(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))))
Run Code Online (Sandbox Code Playgroud)

我返回两个值而不是列表; 这更加惯用(您将使用multiple-value-bind提取yesno返回的多个值,而不是destructuring-bind用于解析列表,它会减少并且更快).

更通用的版本

(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)))
Run Code Online (Sandbox Code Playgroud)

  • 当存在主要返回值以及在大多数情况下将丢弃的其他值时,多个值很有用.除法相关的数学函数都以这种方式工作,将商作为主要值返回,将余数作为次要值返回.当很难确定哪个返回值是"主要"时,我发现最好返回列表中或自描述结构中的值.需要使用笨重的机制在呼叫站点处理多个值,而列表可以很容易地传递. (3认同)

Rai*_*wig 7

使用REDUCE:

(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)
Run Code Online (Sandbox Code Playgroud)