連想リスト(略してalist)は、キーから値へのマッピングを記録します。これは連想と呼ばれるコンスセルのリストであり、各コンスセルのCARがキー、CDRが対応する値となります。7
以下にalistの例を示します。キー pine は値 cones に、キー oak は acorns に、キー maple は seeds にそれぞれ対応しています。
((pine . cones) (oak . acorns) (maple . seeds))
alistの値とキーは、いずれも任意のLispオブジェクトであり得ます。例えば、以下のalistでは、シンボル a が数値 1 に、文字列 "b" が リスト (2 3)(alist要素の CDR)にそれぞれ関連付けられています:
((a . 1) ("b" 2 3))
場合によっては、要素のCDRのCARに関連値を格納するalistを設計した方がよいことがあります。以下に、そのようなalistの例を示します。
((rose red) (lily white) (buttercup yellow))
ここでは、red を rose
に関連付けられた値として扱います。このようなアリストの利点の一つは、他の関連情報――他の項目のリストでさえも――を CDR の CDR
に格納できる点です。欠点の一つは、rassq(後述)を用いて特定の値を含む要素を探し出せないことです。これらどちらの点も重要でない場合、任意のアリストに対して一貫性を保っている限り、どちらを選択するかは好みの問題となります。
上記に示したのと同じalistは、要素のCDRに関連付けられた値を持つと見なすことができ、roseに関連付けられた値はリスト(red)となります。
連想リストは、リストの先頭に新しい連想を容易に追加できるため、本来であればスタックで管理するような情報を記録する際によく用いられます。特定のキーを持つ連想をリストから検索する場合、該当するものが複数あれば、最初に見つかったものが返されます。
Emacs Lispでは、連想リストの要素がコンスセルでなくてもエラーにはなりません。alist検索関数は、単にそのような要素を無視します。他の多くのLisp実装では、このような場合にエラーを発生させます。
なお、プロパティリストはいくつかの点で連想リストに似ています。プロパティリストは、各キーが一度しか出現できない連想リストのように動作します。プロパティリストと連想リストの比較については、プロパティリストを参照してください。
この関数は、alist における key
の最初の関連付けを返します。その際、key を、testfn
が関数であればそれを用いて、そうでなければ equal を用いて alist
の要素と比較します(等価述語
を参照)。testfn が関数である場合、alist
の要素の CAR と key
という2つの引数で呼び出されます。testfn による判定の結果、alist のどの関連付けの CAR も key と等しくない場合、この関数は nil
を返します。例えば:
(setq trees '((pine . cones) (oak . acorns) (maple . seeds)))
⇒ ((pine . cones) (oak . acorns) (maple . seeds))
(assoc 'oak trees)
⇒ (oak . acorns)
(cdr (assoc 'oak trees))
⇒ acorns
(assoc 'birch trees)
⇒ nil
キーと値がシンボルではない別の例を以下に示します。
(setq needles-per-cluster
'((2 "Austrian Pine" "Red Pine")
(3 "Pitch Pine")
(5 "White Pine")))
(cdr (assoc 3 needles-per-cluster))
⇒ ("Pitch Pine")
(cdr (assoc 2 needles-per-cluster))
⇒ ("Austrian Pine" "Red Pine")
関数 assoc-string は、文字列間の特定の差異を無視する点を除けば、assoc と非常によく似ています。文字と文字列の比較を参照してください。
この関数は、alist 内で値 value
を持つ最初の関連付けを返します。alist 内のどの関連付けにも equal から value への CDR がない場合は、nil を返します。
rassoc は assoc
と同様ですが、CAR ではなく、各 alist
アソシエーションの CDR
を比較する点が異なります。これは、与えられた値に対応するキーを探す、逆方向の assoc
と考えることができます。
この関数は、alist 内の key
に対する最初の関連付けを返すという点では assoc と同様ですが、比較には eq を使用します。alist 内のどの関連付けにも key への CAR eq がない場合、assq は nil を返します。eq は equal よりも高速であり、ほとんどのアリスタではシンボルをキーとして使用するため、この関数は assoc よりも頻繁に利用されます。等価述語 を参照してください。
(setq trees '((pine . cones) (oak . acorns) (maple . seeds)))
⇒ ((pine . cones) (oak . acorns) (maple . seeds))
(assq 'pine trees)
⇒ (pine . cones)
一方で、キーがシンボルとは限らないalistでは、assqは通常役に立ちません:
(setq leaves
'(("simple leaves" . oak)
("compound leaves" . horsechestnut)))
(assq "simple leaves" leaves)
⇒ Unspecified; might be nil or ("simple leaves" . oak).
(assoc "simple leaves" leaves)
⇒ ("simple leaves" . oak)
この関数は assq と同様です。key と alist 要素を比較して最初の関連付け (key . value)
を探し、見つかった場合はその関連付けの value を返します。関連付けが見つからない場合は、この関数は
default を返します。key と alist 要素の比較には testfn
で指定された関数が使用され、デフォルトは eq となります。
これは一般化変数(一般化変数を参照)であり、setfを用いて値を変更するために使用できます。値を設定する際にオプション引数remove(non-nil)を指定すると、新しい値がeqlからdefaultである場合に、alistからkeyの関連付けを削除します。
この関数は、alist 内で値 value
を持つ最初の関連付けを返します。alist 内のどの関連付けにも eq から value への CDR がない場合は、nil を返します。
rassq は assq
と同様ですが、CAR の代わりに、各 alist
アソシエーションの CDR
を比較する点が異なります。これは、与えられた値に対応するキーを探す、逆方向の assq
と考えることができます。
例えば:
(setq trees '((pine . cones) (oak . acorns) (maple . seeds)))
(rassq 'acorns trees)
⇒ (oak . acorns)
(rassq 'spores trees)
⇒ nil
rassq は、要素の CDR の CAR に格納されている値を検索できません:
(setq colors '((rose red) (lily white) (buttercup yellow)))
(rassq 'white colors)
⇒ nil
この場合、関連付け (lily white) の CDR はシンボル white ではなく、リスト (white) となります。関連付けを点対表記で書くと、より分かりやすくなります:
(lily white) ≡ (lily . (white))
この関数は、alist の中から key
に一致するものを検索します。alist の各要素について、その要素(アトムの場合)または要素の
CAR(コンスの場合)を key
と比較するため、要素またはその CAR、および key
の2つの引数を指定して test
を呼び出します。引数はこの順序で渡されるため、正規表現を含むアリストを用いて string-match を使うことで有用な結果を得ることができます(正規表現による検索 を参照)。test
が省略されているか、または nil が指定されている場合は、比較に equal が使用されます。
この基準でalistの要素がkeyに一致する場合、assoc-defaultはこの要素に基づく値を返します。要素がconsである場合、値はその要素のCDRとなります。それ以外の場合、戻り値はdefaultとなります。
alistの要素にkeyが一致しない場合、assoc-defaultはnilを返します。
この関数は alist の2レベルのディープコピーを返します。各アソシエーションの新しいコピーを作成するため、元のアリストを変更することなく、新しいアリストのアソシエーションを変更できます。
(setq needles-per-cluster
'((2 . ("Austrian Pine" "Red Pine"))
(3 . ("Pitch Pine"))
(5 . ("White Pine"))))
⇒
((2 "Austrian Pine" "Red Pine")
(3 "Pitch Pine")
(5 "White Pine"))
(setq copy (copy-alist needles-per-cluster))
⇒
((2 "Austrian Pine" "Red Pine")
(3 "Pitch Pine")
(5 "White Pine"))
(eq needles-per-cluster copy)
⇒ nil
(equal needles-per-cluster copy)
⇒ t
(eq (car needles-per-cluster) (car copy))
⇒ nil
(cdr (car (cdr needles-per-cluster)))
⇒ ("Pitch Pine")
(eq (cdr (car (cdr needles-per-cluster)))
(cdr (car (cdr copy))))
⇒ t
この例では、copy-alist
を用いることで、もう一方に影響を与えることなく、一方のコピーの関連付けを変更できる様子を示しています:
(setcdr (assq 3 copy) '("Martian Vacuum Pine"))
(cdr (assq 3 needles-per-cluster))
⇒ ("Pitch Pine")
この関数は、delq を用いて各要素を一つずつ削除する場合と同様に、alist から CAR が eq から key
までのすべての要素を削除します。短縮されたアリストを返し、多くの場合 alist
の元のリスト構造を変更します。正しい結果を得るには、alist の保存値を参照するのではなく、assq-delete-all の戻り値を使用してください。
(setq alist (list '(foo 1) '(bar 2) '(foo 3) '(lose 4)))
⇒ ((foo 1) (bar 2) (foo 3) (lose 4))
(assq-delete-all 'foo alist)
⇒ ((bar 2) (lose 4))
alist
⇒ ((foo 1) (bar 2) (lose 4))
This function is like assq-delete-all except that
it accepts an optional argument test, a predicate
function to compare the keys in alist. If omitted or
nil, test defaults to equal. As assq-delete-all,
this function often modifies the original list structure of alist.
この関数は、alist から CDR が eq から value
であるすべての要素を削除します。短縮されたアリストを返し、多くの場合 alist
の元のリスト構造を変更します。rassq-delete-all は assq-delete-all と同様ですが、CAR
の代わりに各 alist アソシエーションの CDR
を比較します。
連想リスト alist のキーとして使用される各シンボルに対し、先頭にドットを付けたバインディングを作成します。これは、同一の連想リスト内の複数の項目にアクセスする際に有用であり、簡単な例を挙げると分かりやすくなります:
(setq colors '((rose . red) (lily . white) (buttercup . yellow)))
(let-alist colors
(if (eq .rose 'red)
.lily))
⇒ white
body はマクロ展開時に検査され、シンボル名の先頭文字が ‘.’ である body
にリテラルとして現れるシンボルがバインドされます。キーの検索は assq で行われ、この assq の戻り値の cdr
がバインドの値として割り当てられます。生成されたコードは、body の他のどの部分よりも先にすべての
alist ルックアップを実行するため、ボディ内のどこに現れても、シンボル .foo は(キー foo
を用いて)ルックアップされ、実行時に body
で実際に使用されているかどうかにかかわらずバインドされます。
ネストされた連想リストがサポートされています:
(setq colors '((rose . red) (lily (belladonna . yellow) (brindisi . pink))))
(let-alist colors
(if (eq .rose 'red)
.lily.belladonna))
⇒ yellow
Nesting let-alist inside each other is allowed,
but the code in the inner let-alist can’t
access the variables bound by the outer let-alist.
リストへのインデックス指定にも対応しています:
(setq colors '((rose . red) (lily . (yellow pink))))
(let-alist colors .lily.1)
⇒ pink
なお、‘.0’ や ‘.3’ のような形式は、記号ではなく数値として解釈されるため、alist の対応する値にバインドされない点に注意してください。
ここでの「key」という用法は、「キーシーケンス」という用語とは関係ありません。これは、テーブル内の項目を参照するために用いられる値を指します。この場合、テーブルはalistであり、alistの関連付けが項目となります。