web-dev-qa-db-ja.com

lispは、述語に一致しないリストから結果を除外します

Emacs方言を使ってLISPを学ぼうとしていますが、質問があります。リストにいくつかのメンバーがあり、その述語がfalseと評価されたとします。それらのメンバーなしで新しいリストを作成するにはどうすればよいですか? { A in L: p(A) is true }のようなもの。 in pythonフィルター関数がありますが、lispに同等のものはありますか?そうでない場合はどうすればよいですか?

ありがとう

35
Anycorn

これらの関数はCLパッケージに含まれているため、使用するには(require 'cl)する必要があります。

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

これにより、引数からすべての偶数を含む新しいリストが返されます。

delete-if-notも検索します。これは同じことを行いますが、引数リストを変更します。

42
rootzlevel

コード内でリストを頻繁に操作する場合は、定型コードを記述して車輪の再発明を行う代わりに、 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の優れている点は、 照応マクロ をサポートしていることです。照応マクロは関数のように動作しますが、特別な構文を使用してコードをより簡潔にすることができます。 無名関数 を引数として指定する代わりに、 s式 を記述し、前の例のitのようにローカル変数の代わりにxを使用します。対応する照応マクロは、1つではなく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)
21

私は昨夜まったく同じものを探していて、 Elisp Cookbook on EmacsWiki に出くわしました。 リスト/シーケンスのセクション にはフィルタリング手法が含まれており、これを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.
19
kjfletch

Emacsにはライブラリが付属していますseq.el、 使用する seq-remove

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

ループよりもはるかに高速な組み込みを使用して、リストからコンテンツをフィルタリングまたは選択する方法はたくさんあります。組み込みの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)を取得します

Remove-ifに指定する必要のある述語に必要なものは何でも使用できます。唯一の制限は、何を使用するかについてのあなたの想像力です。シーケンスフィルタリング機能は使用できますが、必要ありません。

または、mapcarまたはmapcar *を使用して、特定のエントリをnilに変換する関数を使用してリストをループし、(remove-if nil ...)を使用してnilを削除することもできます。

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)))
1
prosseek

clまたは(または非常に新しいseq)のない組み込みバージョンのフィルターがないのは驚くべきことです。

ここで言及されているfilterの実装(Elispクックブックや他の場所で見られます)は正しくありません。削除するアイテムのマーカーとしてnilを使用します。つまり、最初にリストにnilsがある場合、それらが述語を満たしていても、アイテムは削除されます。

この実装を修正するには、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
pyrocrasty