İç içe geçmiş bir alistte Assoc

Nov 19 2020

Aşağıdaki iç içe geçmiş aliste sahibim:

(setq x '(foo . ((bar . ((chocolate . "edible") (gold . "inedible")))
                 (jar . "glass"))))

Nasıl giriş yapılır (chocolate . "edible")?

Okuduğum bu soruyu & ve bu

Ancak q1'den farklı olarak, değerin "yolunu" bilmiyorum ve q2'nin aksine, bir Elisp uygulaması istiyorum. Ayrıca, 2 - 5 "derinliği" olabilen daha büyük bir listem var (derinlikten alistlerde alistleri kastediyorum)

Şimdiye kadar pişirebildiğim şey buydu:

(defun assoc-recur (key list)
  (if (listp (cdr list))
      (assoc key (cdr list))
    (assoc-reccur key (cdr list))))

Açıktır ki, bu kod, değer, gibi alistlerin bir listesi olmadığı sürece çalışır. (bar . ((..))

Vanilya Elisp ile (CL öykünmesi olmadan) iç içe geçmiş bir listedeki bir değere nasıl erişebilirim? Veya CL api'yi bırakıp q2'yi denemeli miyim?

Aradığım sözdizimi şöyle bir şey (func key list)

ps: Emacs'ta oldukça yeniyim, bu yüzden muhtemelen uygun bir işlevi kaçırıyorum.

Yanıtlar

3 Basil Nov 19 2020 at 22:55

Aşağıdaki iç içe geçmiş aliste sahibim:

Örnek gerçek bir listeyi göstermez, çünkü onun ilk öğesi foobir eksper hücresi değildir. Ben şahsen ona ağaç derim. assoc-stringBu gibi işlevler bunu halledebilir, diğerleri bu tür öğeleri göz ardı edebilir, ancak genel olarak alist işlevler her öğenin bir araba ve bir cdr ile bir eksilerini bekler. Bakınız (info "(elisp) Lists")ve alt düğümleri.

Şimdiye kadar pişirebildiğim şey buydu:

Elisp özyinelemeyi çok verimli bir şekilde ele almaz, bu nedenle mümkünse genel olarak bundan kaçınmanızı öneririm. Aksi takdirde max-specpdl-sizesınırları aşabilirsiniz.

q1'in aksine, değerin "yolunu" bilmiyorum

Ayrıca, 2 - 5 "derinliği" olabilen daha büyük bir listem var (derinlikten alistlerde alistleri kastediyorum)

Bu veri yapısının düzensizliği göz önüne alındığında, içindeki şeylere bakmadan önce listeyi düzleştirmenizi tavsiye ederim. Bu, kodun karmaşıklığını belirli bir zaman ve alan pahasına büyük ölçüde basitleştirmelidir. Emacs 27'de:

(setq x '(foo
          (bar (chocolate . "edible")
               (gold . "inedible"))
          (jar . "glass")))
(cadr (memq 'chocolate (flatten-tree x))) ; => "edible"

flatten-treeEmacs'ın daha eski bir sürümünü kullanıyorsanız şu anki uygulaması şu şekildedir:

(defun flatten-tree (tree)
  "Return a \"flattened\" copy of TREE.
In other words, return a list of the non-nil terminal nodes, or
leaves, of the tree of cons cells rooted at TREE.  Leaves in the
returned list are in the same order as in TREE.

\(flatten-tree \\='(1 (2 . 3) nil (4 5 (6)) 7))
=> (1 2 3 4 5 6 7)"
  (let (elems)
    (while (consp tree)
      (let ((elem (pop tree)))
        (while (consp elem)
          (push (cdr elem) tree)
          (setq elem (car elem)))
        (if elem (push elem elems))))
    (if tree (push tree elems))
    (nreverse elems)))

Alternatif olarak, yinelemeli derinlik öncelikli ağaç araması gerçekleştirebilirsiniz. Elisp'in özyineleme sorunlarını daha karmaşık kodlarla değiştirir. Aşağıda, şuradan alınan bir HTML DOM üzerindeki bir DFS örneği verilmiştir:https://github.com/abo-abo/swiper/pull/1593#issuecomment-392587760 :

(defun counsel--firefox-bookmarks-libxml ()
  "Parse current buffer contents as Firefox HTML bookmarks.
Return list of propertized string candidates for
`counsel-firefox-bookmarks'.
Note: This function requires libxml2 support."
  ;; Perform iterative pre-order depth-first search instead of using
  ;; `dom.el' because the latter is new to Emacs 25 and uses recursion.
  (let ((stack (cddr (libxml-parse-html-region (point-min) (point-max))))
        cands)
    (while (let ((node (pop stack)))
             (if (eq (car-safe node) 'a)
                 (let* ((text (cl-caddr node))
                        (attrs (cadr node))
                        (href (cdr (assq 'href attrs)))
                        (tags (cdr (assq 'tags attrs))))
                   (unless (zerop (length href))
                     (push (counsel--firefox-bookmarks-cand href text tags)
                           cands)))
               (dolist (child (nreverse (cddr node)))
                 (when (consp child)
                   (push child stack))))
             stack))
    cands))

Sizin durumunuzda whiledöngü ayrıca istenen anahtarın bulunduğu yerde sona erecektir.

Alternatif olarak, verilerinizi daha düzenli olacak şekilde yapılandırmanızı öneririm. ;)

xuchunyang Nov 20 2020 at 00:30

Yerleşik makroyu let-alist, iç içe geçmiş listedeki değere erişmek için kullanabilirsiniz , örneğin,

(let-alist
    '((foo . ((bar . ((chocolate . "edible") (gold . "inedible")))
              (jar . "glass"))))
  .foo.bar.chocolate)
;; => "edible"

Ve sizin xbir alist değil, alist anahtar-değer çiftlerinin bir listesidir, yani ((key1 . val1) (key2 . val2) ...).