lisp过滤掉列表中不匹配谓词的结果

时间:2010-02-10 06:25:43

标签: list lisp filter elisp predicate

我正在尝试学习lisp,使用emacs方言,我有一个问题。 让我们说list有一些成员,谓词的评估结果为false。如何在没有这些成员的情况下创建新列表?像{ A in L: p(A) is true }这样的东西。在python中有过滤函数,在lisp中是否有相同的东西?如果没有,我该怎么做?

由于

7 个答案:

答案 0 :(得分:42)

这些功能在CL包中,您需要(require 'cl)才能使用它们:

(remove-if-not #'evenp '(1 2 3 4 5))

这将返回一个包含参数中所有偶数的新列表。

同时查找delete-if-not,它会执行相同操作,但会修改其参数列表。

答案 1 :(得分:21)

如果您在代码中大量操作列表,请使用dash.el现代函数式编程库,而不是编写样板代码并重新发明轮子。它具有处理您可以想象的列表,树,函数应用程序和流控制的所有功能。要保留与谓词匹配的所有元素并删除其他元素,您需要-filter

(-filter (lambda (x) (> x 2)) '(1 2 3 4 5)) ; (3 4 5)

其他感兴趣的功能包括-remove-take-while-drop-while

(-remove (lambda (x) (> x 2)) '(1 2 3 4 5)) ; (1 2)    
(-take-while (lambda (x) (< x 3)) '(1 2 3 2 1)) ; (1 2)
(-drop-while (lambda (x) (< x 3)) '(1 2 3 2 1)) ; (3 2 1)

dash.el的优点在于它支持anaphoric macros。回指宏的行为与函数类似,但它们允许使用特殊语法使代码更简洁。不要提供anonymous function作为参数,只需编写s-expression并使用it代替本地变量,例如前面示例中的x。相应的照应宏以2个破折号开头而不是1个:

(--filter (> it 2) '(1 2 3 4 5)) ; (3 4 5)
(--remove (> it 2) '(1 2 3 4 5)) ; (1 2)
(--take-while (< it 3) '(1 2 3 2 1)) ; (1 2)
(--drop-while (< it 3) '(1 2 3 2 1)) ; (3 2 1)

答案 2 :(得分:19)

昨晚我一直在寻找同样的事情,遇到Elisp Cookbook上的EmacsWikiThe section on Lists/Sequences包含过滤技术,并说明如何使用mapcardelq完成此操作。我不得不修改代码以将其用于我自己的目的,但这是原始的:

;; Emacs Lisp doesn’t come with a ‘filter’ function to keep elements that satisfy 
;; a conditional and excise the elements that do not satisfy it. One can use ‘mapcar’ 
;; to iterate over a list with a conditional, and then use ‘delq’ to remove the ‘nil’  
;; values.

   (defun my-filter (condp lst)
     (delq nil
           (mapcar (lambda (x) (and (funcall condp x) x)) lst)))

;; Therefore

  (my-filter 'identity my-list)

;; is equivalent to

  (delq nil my-list)

;; For example:

  (let ((num-list '(1 'a 2 "nil" 3 nil 4)))
    (my-filter 'numberp num-list))   ==> (1 2 3 4)

;; Actually the package cl-seq contains the functions remove-if and remove-if-not. 
;; The latter can be used instead of my-filter.

答案 3 :(得分:5)

Emacs现在附带库CROSS JOIN,使用seq.el

seq-remove

答案 4 :(得分:1)

使用常见的lisp,您可以按如下方式实现该功能:

(defun my-filter  (f args)
    (cond ((null args) nil)
        ((if (funcall f (car args))
            (cons (car args) (my-filter  f (cdr args)))
            (my-filter  f (cdr args))))))

(print 
      (my-filter #'evenp '(1 2 3 4 5)))

答案 5 :(得分:1)

有很多方法可以使用内置函数从列表中过滤或选择内容,这些内置函数比循环快得多。内置的remove-if可以这种方式使用。例如,假设我想删除列表MyList中的元素3到10。执行以下代码作为示例:

(let ((MyList (number-sequence 0 9))
      (Index -1)
      )
  (remove-if #'(lambda (Elt)
                  (setq Index (1+ Index))
                  (and (>= Index 3) (<= Index 5))
                  )
              MyList
           )
 )

你会得到'(0 1 2 6 7 8 9)。

假设你只想保留3到5之间的元素。你基本上翻转了我在谓词中写的条件。

(let ((MyList (number-sequence 0 9))
      (Index -1)
      )
  (remove-if #'(lambda (Elt)
                   (setq Index (1+ Index))
                   (or (< Index 3) (> Index 5))
                  )
              MyList
           )
 )

你会得到'(3 4 5)

您可以使用您必须提供的谓词所需的任何内容来删除if。唯一的限制是你对使用什么的想象力。您可以使用序列过滤功能,但不需要它们。

或者,您也可以使用mapcar或mapcar *使用某些函数循环遍历列表,该函数将特定条目转换为nil并使用(remove-if nil ...)删除nils。

答案 6 :(得分:0)

令人惊讶的是,如果没有cl或(或seq这是非常新的)过滤器没有内置版本。

此处提到的filter的实施(您在Elisp Cookbook和其他地方看到的)是不正确的。它使用nil作为要删除项目的标记,这意味着如果您的列表中有nil个,那么即使它们满足谓词,它们也会被删除。

要纠正此实现,需要使用未加密的符号(即gensym)替换nil标记。

(defun my-filter (pred list)
  (let ((DELMARKER (make-symbol "DEL")))
    (delq
      DELMARKER
      (mapcar (lambda (x) (if (funcall pred x) x DELMARKER))
              list))))