2010-02-10 100 views
31

我正在嘗試使用emacs方言學習lisp,並且我有一個問題。讓我們說列表有一些成員,對於這些成員,謂詞評估爲false。如何在沒有這些成員的情況下創建新列表?像{ A in L: p(A) is true }。在python中有過濾功能,在lisp中是否有相當的功能?如果不是,我該怎麼做?lisp從列表中不匹配謂詞過濾出結果

感謝

回答

38

這些功能在CL包,您將需要(require 'cl)使用它們:

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

這將返回一個新的列表與所有甚至從參數號。

也查找delete-if-not,它做同樣的事情,但修改它的參數列表。

+6

我想指出'#'remove-if-not'在Common Lisp [1]中已棄用(http://www.ai.mit.edu/projects/iiip/doc/CommonLISP/ (remove-if(complement#'evenp)'(1 2 3 4 5))''或簡單地''(remove-if#'oddp' (1 2 3 4 5))' - Emacs Lisp中並不存在函數'complement',但據我所知。 – 2011-09-25 07:19:06

+1

請使用* cl-lib *包,並使用** cl-remove-if-not **函數作爲替換。 – 2016-08-21 13:54:12

19

昨晚我在找同樣的東西,在EmacsWiki上遇到了Elisp CookbookThe 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. 
+1

鏈接中斷;新位置是http://www.emacswiki.org/emacs/ElispCookbook#toc37(但stackoverflow不會讓我編輯少於6個字符...) – robru 2013-04-12 03:01:06

+0

新鏈接是:https://www.emacswiki.org/ emacs/ElispCookbook#toc39 – Cheeso 2017-06-03 18:12:11

18

如果你在你的code操作列表,請使用dash.el現代函數式編程庫,而不是寫樣板代碼和重塑車輪。它具有與您所能想象的列表,樹木,功能應用和流量控制一起工作的各種功能。爲了保持匹配謂詞的所有元素,並刪除其他內容則需要-filter

感興趣的其他功能包括-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個破折號,而不是一個:

(--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) 
+0

真棒圖書館,感謝您的指點! – JustGage 2015-02-27 08:44:10

0

有了Common 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))) 
3

現在的Emacs自帶的庫seq.el,使用seq-remove

seq-remove (pred sequence) 
"Return a list of all the elements for which (PRED element) is nil in SEQUENCE." 
0

這是令人驚訝有過濾器的內置無版無cl或(或seq這是非常新的)。

這裏提到的filter的實現(你在Elisp Cookbook和別處看到的)是不正確的。它使用nil作爲要刪除項目的標記,這意味着如果您的列表中有nil s,那麼即使它們滿足謂詞,它們也將被刪除。

要糾正這個實現,nil標記需要用一個未中斷的符號(即。gensym)替換。

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

有一噸的過濾方法或者使用內置插件這比循環更快列表選擇的東西。內置的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)

你可以使用任何你需要的,你必須提供刪除,如果斷言。唯一的限制是你對使用什麼的想象。您可以使用序列過濾功能,但不需要它們。

或者,您也可以使用mapcar或mapcar *使用某些函數將列表中的特定條目變爲零並使用(remove-if nil ...)來刪除nils。