ネストされた関連リストの値を取得する最良の方法は?


11

次のような連想リストがあるとします。

(setq x '((foo . ((bar . "llama")
                  (baz . "monkey")))))

そして、私はの値を求めていますbar。私がすることができます:

(assoc-default 'bar (assoc-default 'foo x))

しかし、私が本当に欲しいのは、次のような複数のキーを受け入れるものです

(assoc-multi-key 'foo 'bar x)

そのようなものが、おそらくどこかのパッケージに存在していますか?きっと書けると思いますが、グーグルフーが失敗して、見つけられない気がします。


FWIW、このページにネストされたalistはありません。通常のネストされていない連想配列のみが表示されます。そして、あなたが探している行動は明確ではありません。の動作については何も言わないassoc-multi-key。おそらく、最初の2つの引数の両方に一致するものを探しますが、あなたが言ったことから、それは実際にそれがすべて推測できることのすべてです。そして、alist引数(おそらくx)は最初の引数ではなく最後の引数であるため、明らかに3つ以上のキーを受け入れることはできません。これは、一般的にあまり役に立たないことを示唆しています。実際に探しているものを指定してみてください。
2014年

setq例のフォームの元のフォーマットもわかりにくいので、assoc-listに共通のドット表記を使用するように編集しました。
パプリカ2014年

あ、そう。したがって、連想リストには2つのレベルがあります。質問はまだ不明確です- assoc-multi-key不特定のままです。
ドリュー、

1
ドリュー:ポイントはassoc-multi-key連想リストの最初のキーを調べることです。これは、次のキーを検索する新しいassoc-listに解決されるはずです。などなど。基本的には、ネストされたassoc-listから値を掘り下げるための省略形です。
アビンガム2014年

2
@Malabarbaおそらくあなたも言及let-alistできますか?例:(let-alist '((foo . ((bar . "llama") (baz . "monkey")))) .foo.bar)戻り"llama"ます。let-alist質問された後に書いたと思いますが、それは質問の精神に基づいており、IMOに言及する価値は非常にあります。
YoungFrog 2015

回答:


14

ここに、あなたが求めた正確な構文を一般化された方法で受け取り、理解するのが非常に簡単なオプションがあります。唯一の違いは、ALISTパラメーターが最初に来る必要があるということです(それが重要な場合は、最後に来るように調整できます)。

(defun assoc-recursive (alist &rest keys)
  "Recursively find KEYs in ALIST."
  (while keys
    (setq alist (cdr (assoc (pop keys) alist))))
  alist)

それからあなたはそれを呼び出すことができます:

(assoc-recursive x 'foo 'bar)

2
これも多かれ少なかれ私が調理したものです。これがダッシュなどの確立されたライブラリの一部ではないことに少し驚いています。たとえばjsonデータを処理するとき、それはいつも出てくるようです。
アビンガム2014年

2

これはより一般的な解決策です:

(defun assoc-multi-key (path nested-alist)
   "Find element in nested alist by path."
   (if (equal nested-alist nil)
       (error "cannot lookup in empty list"))
   (let ((key (car path))
         (remainder (cdr path)))
     (if (equal remainder nil)
         (assoc key nested-alist)
       (assoc-multi-key remainder (assoc key nested-alist)))))

キーの任意の「パス」をとることができます。これは戻ります(bar . "llama")

(assoc-multi-key '(foo bar)
    '((foo (bar . "llama") (baz . "monkey"))))

これは次を返し(baz . "monkey")ます:

(assoc-multi-key '(foo bar baz)
    '((foo (bar (bozo . "llama") (baz . "monkey")))))

3
この回答に対する最初の反対票を獲得しました。理由を教えてくれる人はいますか?
rekado 2014年

1
コードが機能するため(+1)、反対票には同意しません。私の推測では、@ Malabarbaの回答は、提供されている他の回答よりも明らかに一般的でエレガントです。したがって、他の回答は、機能しないためではなく、最良のものではないため、反対票を受け取りました。(とはいえ、私は「最高を賛成し、他を賛成投票する」という選択肢よりも、「最高を賛成する」オプションを好む。)
ダン

1
これらの2つの質問は、反対投票がどのように機能するかを完全に理解していない(そしてコメントを残すためのインターフェースの要求を無視することを選択した)人がいるため、反対投票されました。残念ですが、私たち全員にできる最善のことは賛成です。
Malabarba 2014年

0

次に、別のalist内にネストされたalistで機能する単純な関数を示します。

(defun assoc2 (outer inner alist)
  "`assoc', but for an assoc list inside an assoc list."
  (assoc inner (assoc outer alist)))

(setq alist2 '((puppies (tail . "waggly") (ears . "floppy"))
               (kitties (paws . "fuzzy")  (coat . "sleek"))))

(assoc2 'kitties 'coat alist2)       ;; => (coat . "sleek")
(cdr (assoc2 'kitties 'coat alist2)) ;; => "sleek"

3
反対票を投じるときはコメントを残してください
Malabarba 2014年

1
反対票を投じた者:私は気分を害することはありませんが、その理由に興味があります。@マラバラ:「反対票+コメント」の規範にメタスレッドがありますか?; あなたの見方に興味があります。
ダン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.