Emacs方言を使ってLISPを学ぼうとしていますが、質問があります。リストにいくつかのメンバーがあり、その述語がfalseと評価されたとします。それらのメンバーなしで新しいリストを作成するにはどうすればよいですか? { A in L: p(A) is true }
のようなもの。 in pythonフィルター関数がありますが、lispに同等のものはありますか?そうでない場合はどうすればよいですか?
ありがとう
これらの関数はCLパッケージに含まれているため、使用するには(require 'cl)
する必要があります。
(remove-if-not #'evenp '(1 2 3 4 5))
これにより、引数からすべての偶数を含む新しいリストが返されます。
delete-if-not
も検索します。これは同じことを行いますが、引数リストを変更します。
コード内でリストを頻繁に操作する場合は、定型コードを記述して車輪の再発明を行う代わりに、 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)
私は昨夜まったく同じものを探していて、 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.
Emacsにはライブラリが付属していますseq.el
、 使用する seq-remove
。
seq-remove (pred sequence)
"Return a list of all the elements for which (PRED element) is nil in SEQUENCE."
ループよりもはるかに高速な組み込みを使用して、リストからコンテンツをフィルタリングまたは選択する方法はたくさんあります。組み込みの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)))
cl
または(または非常に新しいseq
)のない組み込みバージョンのフィルターがないのは驚くべきことです。
ここで言及されているfilter
の実装(Elispクックブックや他の場所で見られます)は正しくありません。削除するアイテムのマーカーとして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))))