중첩 된 목록의 Assoc
다음과 같은 중첩 된 목록이 있습니다.
(setq x '(foo . ((bar . ((chocolate . "edible") (gold . "inedible")))
(jar . "glass"))))
어떻게 입장 할 수 (chocolate . "edible")
있습니까?
본인은 이 질문 및 및 이
그러나 q1과 달리 값에 대한 "경로"를 모르고 q2와 달리 Elisp 구현을 원합니다. 또한 2-5의 "깊이"를 가질 수있는 더 큰 목록이 있습니다 (깊이에 따라 alists in alists를 의미 함).
지금까지 이것이 제가 요리 할 수 있었던 것입니다.
(defun assoc-recur (key list)
(if (listp (cdr list))
(assoc key (cdr list))
(assoc-reccur key (cdr list))))
이 코드는 값이 다음과 같은 목록 목록이 아닌 경우에만 작동한다는 것이 분명합니다. (bar . ((..))
vanilla Elisp (CL 에뮬레이션없이)를 사용하여 중첩 된 목록의 값에 어떻게 액세스 할 수 있습니까? 아니면 포기하고 CL API를 설치하고 Q2를 시도해야합니까?
내가 찾고있는 구문은 다음과 같습니다. (func key list)
추신 : 저는 Emacs를 처음 접했기 때문에 편리한 기능을 놓치고있을 것입니다.
답변
다음과 같은 중첩 된 목록이 있습니다.
첫 번째 요소 foo
는 단점 셀이 아니기 때문에이 예제는 실제 목록을 표시하지 않습니다 . 저는 개인적으로 그것을 나무라고 부릅니다. 같은 함수 assoc-string
는 이것을 처리 할 수 있고 다른 함수 는 그러한 요소를 무시할 수 있지만 일반적으로 alist 함수는 모든 요소가 자동차 및 cdr의 단점 일 것으로 예상합니다. (info "(elisp) Lists")및 하위 노드를 참조하십시오 .
지금까지 이것이 제가 요리 할 수 있었던 것입니다.
Elisp는 재귀를 매우 효율적으로 처리하지 않으므로 가능하면 일반적으로 피하는 것이 좋습니다. 그렇지 않으면 max-specpdl-size
한계에 도달 할 수 있습니다 .
q1과 달리 값에 대한 "경로"를 모릅니다.
또한 2-5의 "깊이"를 가질 수있는 더 큰 목록이 있습니다 (깊이에 따라 alists in alists를 의미 함).
이 데이터 구조의 불규칙성을 감안할 때 목록에서 항목을 찾기 전에 먼저 목록을 병합하는 것이 좋습니다. 이것은 약간의 시간과 공간을 희생하면서 코드의 복잡성을 크게 단순화해야합니다. Emacs 27에서 :
(setq x '(foo
(bar (chocolate . "edible")
(gold . "inedible"))
(jar . "glass")))
(cadr (memq 'chocolate (flatten-tree x))) ; => "edible"
다음 flatten-tree
은 이전 버전의 Emacs를 사용하는 경우 의 현재 구현입니다 .
(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)))
또는 반복적 인 깊이 우선 트리 검색을 수행 할 수 있습니다. Elisp의 재귀 문제를 더 복잡한 코드로 교환합니다. 다음은 HTML DOM에 대한 DFS의 예입니다.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))
귀하의 경우 while
루프는 원하는 키의 위치에서 추가로 종료됩니다.
또는 데이터를 더 규칙적으로 구성하는 것이 좋습니다. ;)
내장 매크로 let-alist
를 사용하여 중첩 된 목록에서 값에 액세스 할 수 있습니다.
(let-alist
'((foo . ((bar . ((chocolate . "edible") (gold . "inedible")))
(jar . "glass"))))
.foo.bar.chocolate)
;; => "edible"
그리고 당신 x
은 alist가 아니며 alist는 키-값 쌍의 목록입니다 ((key1 . val1) (key2 . val2) ...)
.