連想リストとは
連想リストは、他のリストとは異なる別のものではありません。その形式が、最初の項目を識別子とし後続のデータとグループ化したペアを、要素としてリストにまとめたものを連想リスト(Association list)と言います。そのため、ほとんどの操作はリストの章で紹介した関数を使用し、連想リスト専用で新しく出てくるのは assoc 関数のみです。
連想リストの例として、entget 関数で得られるAutoCAD の図面データベースから得られる図形データは、DXF のグループコードを識別子とした連想リストの形で提供されます。例えば直線の情報は下のようなものです。ドットリストが出てきます。
(0 . "LINE")
(330 . <図形名: 7ffff703980>)
(5 . "2A4")
(100 . "AcDbEntity")
(67 . 0)
(410 . "Model")
(8 . "0")
(100 . "AcDbLine")
(10 0.0 0.0 0.0)
(11 10.0 10.0 0.0)
(210 0.0 0.0 1.0)
)
DXF グループコードの 0 は図形のタイプ、8 は画層名、10 と 11 は頂点の座標といった具合に読み解きます。識別子は、整数に限りません。"banana"のような文字列でも、あるいは(”apple" 10)のようなリストでも可能で、どのようなものであれ第一要素が識別子としてあつかわれます。
なお、連想リストの要素の順番は関係ないように思えますが、AutoCAD の図形データでは要素の順番で AutoCAD の解釈がうまくいったり失敗したりすることが時にはあるので、頭の隅に入れておいてください。図形データベースから得られた順で扱えば問題は起こりません。
連想リストの作法
連想リストを扱うときに、多少の作法があります。先ほどの例で「(0 . "LINE")」のようにドットリストになっているものがあります。また、「(10 0.0 0.0 0.0)」のようにドットが出現していないものもあります。例では出てきませんがドットの無い「(999 1000)」というものや要素が一つの「(70)」というのもあり得ます。これらの違いは以下の表のようにまとめられます。
識別子 | データ | |
---|---|---|
(70) | 70 | nil |
(0 . "LINE") | 0 | "LINE" |
(999 1000) | 999 | (1000) |
(10 0.0 0.0 0.0) | 10 | (0.0 0.0 0.0) |
要素が二つの「(0 . "LINE")」と「(999 1000)」に注目してください。このように、ドットリストでないものは、データがリストであることを示しています。
このような違いがあることを知ったうえで、グループを作成するための関数は、すべての場合でcons 関数を以下のように使用することで対応できます。
「(70)」は (cons 70 nil) で得られます。「(0 . "LINE")」を作成したいのであれば、(cons 0 "LINE") です。「(10 0.0 0.0 0.0)」を作成したいのであれば、(cons 10 '(0.0 0.0 0.0)) です。そして、「(999 1000)」を作成したいのであれば、(cons 999 '(1000)) となります。
次に「(0 . "LINE")」から識別子とデータを分離して取り出す方法について考えてみます。識別子を取り出すのは car 関数で、すべての場合で通用します。
データを取り出したい場合はcdr 関数を使えば、すべての場合に対応できます。
cdr 関数にかければ、ドットリストの場合もそうでない場合も問題は起こらない上、アトムの場合も要素が一つのリストの場合も区別してデータを取り出すことができます。
このように、連想リストの要素の作成は cons 関数、識別子の取り出しは car 関数、データの取り出しは cdr 関数を使ってください。そして、それらの要素を連想リストとして編成するのは list 関数などを通常のリストと同じように使用します。
データの抽出
連想リストから、識別子をインデックスとして要素を取り出す関数が LISP には用意されています。
AutoLISP関数 |
---|
(assoc element alist) |
element:アトム、リストまたはドットリスト alist:連想リスト |
連想リストの要素を検索し、指定された要素が含まれる連想リスト項目を返します。 |
戻り値: リストまたはドットリスト |
assoc 関数は、連想リスト内から指定された識別子をもった要素を返します。
element 引数は識別子を指定します。アトムでもリストでも識別子になります。
alist 引数はデータを検索する連想リストを指定します。
戻り値は、識別子とデータを含んだリストが返りますが、該当する識別子が無かった場合は nil が返ります。
_$ (assoc 0 '((0 . "LINE") (8 . "0") (10 0.0 0.0 0.0))) ⏎
(0 . "LINE")
連想リストに同じ識別子のデータが複数含まれている場合は、assoc 関数は最初に見つかった要素を返します。これは、後ろにあるデータにアクセスするためには特別にコードを書かなければならないことを示します。そして、AutoCAD の図形データの中には DXF グループコード 100 など、複数含まれるものがあります。
AutoCAD から図形データを抽出する実際の例は以下のようになります。
(setq sel nil)
(while (= sel nil) ; ユーザーによる図形の選択
(setq sel (entsel "¥n 図形を一つ選択してください"))
)
(setq name (car sel)) ; 選択図形の【図形名】をname に格納
(setq data (entget name)) ; 【図形名】から詳細な図形データを取得
(setq layer (cdr (assoc 8 data))) ; 図形データから画層情報 8 を抽出
(prompt "¥n 画層は ")
(prompt layer)
(prompt " です。")
上記の実行結果は、ユーザーの選択図形を取得し、コマンドラインで次のように表示されます。
図形を一つ選択してください ;ユーザが図形を選択
画層は 0 です。
データの置換
連想リストを変更するには、通常のリストと同じように subst 関数を使用して連想リストの要素を置き換えます。
次は、連想リストの該当の識別子のデータを subst 関数で置き換える関数の例です。元のリストに該当する識別子が無かった場合は append 関数で連想リストに追加しています。
(defun setAList (alist index data / old) (if (setq old (assoc index alist)) ; assoc の戻り値が nil 以外か? (subst (cons index data) old alist) ; then 置き換え (append alist (list (cons index data)) ; else 追加 ) )
関数の使用例は以下のとおりです。
_$ (setAList '((0 . "LINE") (8 . "0") (10 0.0 0.0 0.0)) 8 "10")⏎ ((0 . "LINE") (8 . "10") (10 0.0 0.0 0.0)) _$ (setAList '((0 . "LINE") (8 . "0") (10 0.0 0.0 0.0)) 11 '(10.0 10.0 10.0))⏎ ((0 . "LINE") (8 . "10") (10 0.0 0.0 0.0) (11 10.0 10.0 10.0))