我想做
(filter-list-into-two-parts #'evenp '(1 2 3 4 5))
; => ((2 4) (1 3 5))
根据谓词的计算结果是否为真,列表被分成两个子列表。定义这样一个函数很容易:
(defun filter-list-into-two-parts (predicate list)
(list (remove-if-not predicate list) (remove-if predicate list)))
但我想知道 Lisp 中是否有内置函数可以执行此操作,或者是否有更好的编写此函数的方法?
最佳答案
我不认为有内置的,你的版本不是最优的,因为它遍历列表两次并对每个列表元素调用谓词两次。
(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
从返回的多个值中提取 yes
和 no
,而不是使用 destructuring-bind
来解析列表,它包含更少,更快,另见 values function in Common Lisp )。
一个更通用的版本是
(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)))
关于通过谓词将列表过滤成两部分,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18116949/