Shammer's Philosophy

My private adversaria

remove-if と remove-if-not を理解する

リストの中から、特定の条件に合致するものを削除するのが remove-if で、特定の条件に合致しないものを削除するのが remove-if-not だ。定義はわかっていても、引数に lambda 式が必要なのであまり使っていなかった、というか、強引に自力でリストの中身を一つ一つ比較して削除とかしていた。いい加減、使えるようになりたい、ということでちょっと使い方を整理してみることにした。

remove-if 基本形

一番簡単な形で使ってみる。渡されたリストの中から、ddd に合致するものがあれば削除する、というもの。

? (defparameter x '(aaa bbb ccc ddd eee fff ggg))
X
? (remove-if #'(lambda (n) (equal n 'ddd)) x)
(AAA BBB CCC EEE FFF GGG)
?

これは、渡す側のリストもシンプルなフラット(?)なものだからわかりやすい。あと、重要なのは、lambda の引数は、渡されたリストの中を順番に見ていく、ということ。この全体の式は、以下のように展開できる、ということだろうか。

(let ((result nil))
  (dolist (n x)
    (unless (equal n 'ddd)
      (push n result)))
  (nreverse result))

結果は同じになった。

? (defparameter x '(aaa bbb ccc ddd eee fff ggg))
X
? (remove-if #'(lambda (n) (equal n 'ddd)) x)
(AAA BBB CCC EEE FFF GGG)
? (let ((result nil))
(dolist (n x)
  (unless (equal n 'ddd)
    (push n result)))
(nreverse result))
(AAA BBB CCC EEE FFF GGG)
? x
(AAA BBB CCC DDD EEE FFF GGG)
?

ここで、ちょっと複雑にしてみる。渡されるリストがフラットじゃない場合。例えばこんな感じ。

? (defparameter x '(("000" "AAA") ("111" "BBB") ("222" "CCC")))
X
? (remove-if #'(lambda (n) (string-equal "222" (car n))) x)
(("000" "AAA") ("111" "BBB"))
? 

なるほど。lambda の式は渡されるリストの中身全部に適用され、その適用結果で戻り値も変わってくる、ということか。なんかやってみると、なぜこんな当たり前のことをやっていたんだ、、、という気持ちにもなるが、この当たり前を感覚的に理解できているかが基礎力になっていくのだ。

remove-if-not

基本は remove-if と同じだが、動作としてはなんか find とかに近いイメージ。条件に該当しないものは削除される、ということは、条件に該当するもののみが返される、ということでもある。これを使いこなすことができるとできないとでは、かなり大きな違いになってきそうだ。

? (defparameter x '(("000" "AAA") ("111" "BBB") ("222" "CCC")))
X
? (remove-if-not #'(lambda (n) (string-equal "222" (car n))) x)
(("222" "CCC"))
?