我正在尝试学习lisp,使用emacs方言,我有一个问题。
让我们说list有一些成员,谓词的评估结果为false。如何在没有这些成员的情况下创建新列表?像{ A in L: p(A) is true }
这样的东西。在python中有过滤函数,在lisp中是否有相同的东西?如果没有,我该怎么做?
由于
答案 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上的EmacsWiki。 The section on Lists/Sequences包含过滤技术,并说明如何使用mapcar
和delq
完成此操作。我不得不修改代码以将其用于我自己的目的,但这是原始的:
;; 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))))