중첩 된 연관 목록에서 값을 검색하는 가장 좋은 방법은 무엇입니까?


11

다음과 같은 연관 목록이 있다고 가정하십시오.

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

그리고에서 값을 원합니다 bar. 나는 이것을 할 수있다 :

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

하지만 내가 정말로 원하는 것은 여러 키를 허용하는 것입니다.

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

어딘가에 패키지에 그러한 것이 있습니까? 나는 그것을 쓸 수 있다고 확신하지만 Google-fu가 실패하고 그것을 찾을 수없는 것처럼 느낍니다.


FWIW,이 페이지에 중첩 된 목록이 없습니다. 나는 평범하고 중첩되지 않은 목록만을 본다. 그리고 어떤 행동을 찾고 있는지 명확하지 않습니다. 의 행동에 대해서는 아무 말도 하지 않습니다assoc-multi-key . 아마도 첫 번째 두 가지 주장과 모두 일치하는 것으로 보이지만 실제로는 당신이 말한 것으로 추측 할 수있는 모든 것입니다. 그리고 alist 인수 (아마도 x)가 첫 번째 키가 아닌 마지막 키이기 때문에 두 개 이상의 키를 명확하게 받아 들일 수 없습니다 . 일반적으로 너무 유용하지 않다는 것을 암시합니다. 실제로 원하는 것을 지정 하십시오 .
Drew

또한 setq혼란스러운 예제 에서 양식 의 원래 형식을 찾았 으므로 assoc-lists에 공통 점 표기법을 사용하도록 편집했습니다.
파프리카

그래. 따라서 목록에는 두 가지 수준이 있습니다. 질문은 여전히 ​​불분명합니다- assoc-multi-key지정되지 않은 채 남아 있습니다.
Drew

1
Drew : 요점은 assoc-multi-keyassoc-list에서 첫 번째 키를 찾는 것입니다. 이것은 다음 키를 찾는 새로운 assoc-list로 해결되어야합니다. 기타 등등. 기본적으로 중첩 된 연관 목록에서 값을 파기위한 약칭.
abingham 5

2
@Malabarba 아마도 당신도 언급 할 수 let-alist있습니까? 예 (let-alist '((foo . ((bar . "llama") (baz . "monkey")))) .foo.bar)를 들어을 반환 "llama"합니다. let-alist질문을 한 후에 쓴 것 같지만 질문의 정신에 있으며 IMO를 언급 할 가치가 있습니다!
YoungFrog

답변:


15

다음은 요청한 정확한 구문을 사용하지만 일반화 된 방식으로 이해하기 매우 쉬운 옵션입니다. 유일한 차이점은 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 데이터를 처리 할 때 항상 등장하는 것 같습니다.
abingham 5

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
이 답변에 대한 첫 번째 downvote를 얻었습니다. 이유를 말해 줄 사람이 있습니까?
rekado

1
코드가 작동하기 때문에 downvote에 동의하지 않습니다 (+1). @Malabarba의 답변은 제안 된 다른 답변보다 분명히 더 일반적 / 우아하므로 다른 답변은 작동하지 않기 때문에가 아니라 최고의 답변이 아니기 때문에 다운 투표를 받았습니다. (즉, "최고를 찬성하고 다른 것을 공여"하는 것보다는 "최고를 찬성"옵션을 선호합니다.)
Dan

1
이 두 가지 질문은 다운 보트의 작동 방식을 잘 이해하지 못하는 사람이 있고 (인터페이스의 의견 요청 요청을 무시하기로 선택한 ) 사람 이 있기 때문에 다운 보트되었습니다 . 불행한 일이지만 우리가 할 수있는 최선은 공감입니다.
Malabarba

0

다른 목록 안에 중첩 된 목록과 함께 작동하는 간단한 함수는 다음과 같습니다.

(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

1
누구든 공감하지 않은 사람 : 나는 기분이 상하지 않지만 왜 그런지 궁금하다. @Malabara : "downvote + comment"에 대한 표준에 메타 스레드가 있습니까? ; 당신의 테이크가 궁금합니다.
Dan
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.