Lambdaカクテル

Common Lispを書くMT-03ライダー(初心者)です

Common Lispで文字列をキーにするのはあまりおすすめできない

まあ慣れかもしれないが,Common Lispで文字列をキーとした辞書,つまりalistやplistを作るとあまり使い勝手が良くないので避けたいと思うようになった. なぜなら標準的なリスト操作関数では比較をeqlで行うことが多い(要出展)ので,辞書構造を操作する際に文字列がキーだと支障をきたすことが多いからだ.

ちなみにJSONライブラリであるJSOWNはキーが文字列となった状態でJSONオブジェクトを表現するので扱いにくい.これはまあJSONオブジェクトの特性上仕方がないことなのだろうが・・・

:testを用いたナイーブな回避方法

このような場合に重宝するのは:testキーワードを各種コレクション操作関数のオプションとして渡すことだ.:test #'string=とすることで文字列のキーを正しく比較することができるようになる.

ただし,コレクション操作関数がライブラリなどに組込まれて使われていて:testオプションを変更できない場合は対処不能だ.例えばパターンマッチライブラリtriviaを使った以下のようなスクリプトについて考えてみよう.

(ql:quickload :trivia)
(match '(("a" . 1) ("b" . 2))
  ((alist ("a" . x) ("b" . y))
    (cons x y)))

これはalist(("a" . 1) ("b" . 2))の中の特定の箇所をパターンマッチによって取り出そうとする処理だが,このalistのパターンマッチは内部でassocによる比較を利用しており,(相当面倒なことをしなければ)外から:testを変更することができない.したがって,このパターンマッチは失敗する.

ちなみにassocは比較にeqlを使うので,以下のような場合はパターンマッチに成功する.

(ql:quickload :trivia)
(match '((:a . 1) (:b . 2))
  ((alist (:a . x) (:b . y))
    (cons x y)))

自分がよくやる方法

ちなみに自分はmapcarを使ってalistのキーを全てキーワードに変換してしまった.キーワード名前空間を汚してしまうが,手っ取り早い解決方法だ. 文字列からキーワードを生成するには,ALEXANDRIAライブラリのmake-keywordが役に立つ.