Yuvalanmış assoc listelerindeki değerleri almanın en iyi yolu?


11

Diyelim ki böyle bir dernek listem var:

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

Ve değerini istiyorum bar. Bunu yapabilirim:

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

Ama gerçekten istediğim, birden fazla anahtarı kabul eden bir şey,

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

Böyle bir şey, belki de bir yerlerde bir pakette var mı? Eminim yazabilirim, ancak Google-fu'mun başarısız olduğunu ve bulamıyorum gibi hissediyorum.


FWIW, bu sayfada iç içe geçmiş bir alist görmüyorum. Sadece sıradan, dürüst olmayan alistler görüyorum. Ve hangi davranışı aradığınız açık değil. Davranışları hakkında hiçbir şey söylemiyorsun assoc-multi-key. Muhtemelen ilk iki argümanının her ikisiyle de eşleşiyor, ancak söylediklerinizden birinin gerçekten tahmin edebileceği şey bu. Ve açıkça ikiden fazla anahtarı kabul edemez, çünkü alist argümanı (muhtemelen x) birincisi değil sonuncusu - bu genel olarak çok kullanışlı olmadığını düşündürüyor. Gerçekten ne aradığınızı belirtmeyi deneyin .
Drew

setqFormun orijinal biçimlendirmesini kafa karıştırıcı örnekte de buldum , bu yüzden dolandırıcılık listeleri için ortak nokta gösterimini kullanmak için düzenledim.
paprika

Ah tamam. Yani alistin iki seviyesi var. Soru hala belirsiz - assoc-multi-keybelirtilmemiş.
Drew

1
Drew: Asıl konu assoc-multi-key, listedeki ilk anahtara bakmak. Bu, bir sonraki anahtarı aradığımız yeni bir dernek listesine çözümlenmelidir. Ve böylece. Temelde, iç içe yerleştirilmiş listelerden değerleri kazmak için kullanılan bir kısa el.
abingham

2
@Malabarba Belki siz de bahsedebilirsiniz let-alist? örneğin (let-alist '((foo . ((bar . "llama") (baz . "monkey")))) .foo.bar)dönecektir "llama". Sanırım let-alistsoru sorulduktan sonra yazdınız, ama sorunun ruhunda ve IMO'dan bahsetmeye değer!
YoungFrog

Yanıtlar:


15

İstediğiniz sözdizimini tam olarak genelleştirilmiş bir şekilde alan ve anlaşılması oldukça basit bir seçenek. Tek fark, ALISTparametrenin önce gelmesi gerektiğidir (bu sizin için önemliyse, son gelmesi için uyarlayabilirsiniz).

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

Daha sonra şu şekilde arayabilirsiniz:

(assoc-recursive x 'foo 'bar)

2
Bu da az çok pişirdiğim şeydi. Bunun tire veya benzeri bir yerleşik kütüphanenin bir parçası olmadığına biraz şaşırdım. Örneğin json verileriyle uğraşırken her zaman ortaya çıkıyor gibi görünüyor.
abingham

2

İşte daha genel bir çözüm:

(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)))))

Anahtarların herhangi bir "yolunu" alabilir. Bu geri dönecek(bar . "llama")

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

oysa bu geri dönecektir (baz . "monkey"):

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

3
Bu cevap için ilk notumu aldım. Bana nedenini söylemek isteyen var mı?
rekado

1
Kodunuz çalıştığından (+) aşağı oyuna katılmıyorum. Benim spekülasyonum, @ Malabarba'nın cevabının sunulan diğer cevaplardan açıkça daha genel / zarif olduğu ve diğer cevapların çalışmadıkları için değil, en iyisi olmadıkları için aşağı oy aldıklarıdır. (Olduğu söyleniyor, "en iyiyi oyla ve diğerlerini aşağı indir" alternatifi yerine "en iyiyi oyla" seçeneğini tercih ediyorum.)
Dan

1
Bu iki soru aşağı indirildi, çünkü burada downvotes'un nasıl çalıştığını tam olarak anlamayan bir kişi var (ve arayüzün bir yorum bırakma isteğini göz ardı etmeyi seçiyor). Talihsiz, ama hepimizin yapabileceği en iyi şey yok.
Malabarba

0

İşte başka bir alistin içine yerleştirilmiş bir alist ile çalışan basit bir fonksiyon:

(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
Lütfen insanlar, oy verdiğinizde yorum bırakın.
Malabarba

1
Her kim reddedildi: Rahatsız değilim, ama nedenini merak ediyorum. @Malabara: Artık "downvote + comment" da normlarda bir meta iş parçacığı var mı? ; Senin almayı merak ederdim.
Dan
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.