Map関数の違い#
Common Lispでは、
- 戻り値
- 副作用
- 対象のデータ型
が異なるMap関数が用意されている。
いろいろなMap関数#
1. mapcar#
基本的なMap関数。
リストに対して関数を適用し、結果を新しいリストとして返す。
リスト専用なので、ベクタや文字列には後述するmapを使う。
;; リストの要素を2倍する
(mapcar (lambda (x) (* x 2)) (list 1 2 3))
;; => (2 4 6)また、複数のリストを引数に取ることもできます。処理は、最も短いリストの長さに合わせて止まります。
;; 対応する要素同士を足し算する
(mapcar #'+ (list 1 2 3) (list 10 20 30))
;; => (11 22 33)2. mapc#
リストの各要素に関数を適用しますが、その戻り値は捨てられます。mapc自身の戻り値は、引数として渡された最初のリストです。
計算結果のリストは作られないため、副作用のみが目的の場合に効率的です。
printなどの副作用を目的とする場合(結果のリストが不要な場合)に使われる。
リスト専用なので注意(ベクタには使えない)
(mapc #'princ (list 1 2 3))
;; Output: 123
;; Return: (1 2 3)3. mapcan#
関数が返すリストをnconcで繋げ、平坦化して返す。
リストの構造を変形したり、特定の要素を取り除いたりするのに便利。
(list x)を返すかわりに、空リスト(nil)を返すことで平坦化された時に要素が消える。
リスト専用。
注意: mapcanは内部でnconcを使っており、関数が返すリストを破壊的に連結するため、関数内で新しいリストを生成して返す。
リテラルリストや共有されているリストを返すと、予期せぬ副作用が発生する可能性がある。
;; ネストされる
(mapcar (lambda (x) (list x)) (list 1 2 3))
;; => ((1) (2) (3))
;; 平坦化される
(mapcan (lambda (x) (list x)) (list 1 2 3))
;; => (1 2 3)
;; 偶数だけを残す
(mapcan (lambda (x)
(if (evenp x)
(list x)
()) ; nil (省略可)
(list 1 2 3 4 5 6))
;; => (2 4 6)4. map#
汎用的なMap関数。 リスト、ベクタ、文字列など様々なシーケンス型に対応していおり、第一引数に戻り値の型を指定する。
(map 'string (lambda (x) (code-char x)) (list 65 66 67))
;; => "ABC"
(map 'list #'1+ (vector 1 2 3))
;; => (2 3 4)戻り値の型にnilを指定すると、mapcのように副作用だけが評価され、nilが返る。
(map nil #'print (list 1 2 3))
;; Output: 123
;; Return: nil5. map-into#
計算結果をシーケンスに確保せず、第一引数で指定された既存のシーケンスに直接書き込むため、メモリ効率が高く、高速化に有利。
リストにもベクタにも使える。
結果を書き込むシーケンスの長さが入力のシーケンスより短い場合、短い方の長さまでしか処理されない。
(let ((result (list 0 0 0)))
(map-into result #'1+ (list 1 2 3))
result)
;; => (2 3 4)まとめ#
- mapcar: リストの各要素に関数を適用し、新しいリストを返す。
- mapc: 副作用が目的の場合に使う。元のリストを返す。
- mapcan: 結果のリストを平坦化したい場合に使う。破壊的である点に注意。
- map: リスト以外のシーケンス(ベクタや文字列)を扱いたい場合や、結果の型を明示したい場合に使う。
- map-into: 既存のシーケンスに結果を上書きすることで、メモリ効率を重視する場合に使う。
