Cách tốt nhất để lấy các giá trị trong danh sách PGS lồng nhau?


11

Giả sử tôi có một danh sách PGS như thế này:

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

Và tôi muốn giá trị tại bar. Tôi có thể làm điều này:

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

Nhưng điều tôi thực sự thích là thứ gì đó chấp nhận nhiều khóa, như

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

Liệu một điều như vậy tồn tại, có lẽ trong một gói ở đâu đó? Tôi chắc chắn rằng tôi có thể viết nó, nhưng tôi cảm thấy như Google-fu của tôi đang thất bại và tôi không thể tìm thấy nó.


FWIW, tôi không thấy bất kỳ dấu hiệu lồng nhau nào trên trang này. Tôi chỉ thấy bình thường, chưa được kiểm tra. Và không rõ bạn đang tìm kiếm hành vi nào. Bạn không nói về hành vi của assoc-multi-key. Có lẽ nó tìm kiếm sự trùng khớp với cả hai đối số đầu tiên của nó, nhưng đó thực sự là tất cả những gì người ta có thể cho là, từ những gì bạn đã nói. Và rõ ràng nó không thể chấp nhận nhiều hơn hai khóa, vì đối số alist (có lẽ là x) là khóa cuối cùng, không phải là khóa đầu tiên - điều này cho thấy rằng nó không quá hữu ích nói chung. Hãy thử thực sự xác định những gì bạn đang tìm kiếm.
vẽ

Tôi cũng tìm thấy định dạng ban đầu của setqbiểu mẫu trong ví dụ khó hiểu, vì vậy tôi đã chỉnh sửa nó để sử dụng ký hiệu chấm phổ biến cho danh sách PGS.
paprika

À, được rồi Vì vậy, alist có hai cấp độ. Câu hỏi vẫn chưa rõ ràng - assoc-multi-keyvẫn chưa được xác định.
vẽ

1
Drew: Quan điểm của assoc-multi-keylà tìm kiếm khóa đầu tiên trong danh sách PGS. Điều này sẽ giải quyết một danh sách PGS mới trong đó chúng tôi tìm khóa tiếp theo. Và kể từ đó trở đi. Về cơ bản là một tay ngắn để đào các giá trị ra khỏi danh sách PGS lồng nhau.
abingham

2
@Malabarba Có lẽ bạn cũng có thể đề cập đến let-alist? ví dụ (let-alist '((foo . ((bar . "llama") (baz . "monkey")))) .foo.bar)sẽ trở lại "llama". Tôi đoán bạn đã viết let-alistsau khi câu hỏi được hỏi, nhưng đó là trên tinh thần của câu hỏi và rất đáng đề cập đến IMO!
YoungFrog

Câu trả lời:


14

Đây là một tùy chọn có cú pháp chính xác mà bạn yêu cầu nhưng theo cách khái quát và khá đơn giản để hiểu. Sự khác biệt duy nhất là ALISTtham số cần phải đến trước (bạn có thể điều chỉnh nó để đến sau, nếu điều đó quan trọng với bạn).

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

Sau đó, bạn có thể gọi nó với:

(assoc-recursive x 'foo 'bar)

2
Đây là ít nhiều những gì tôi đã nấu lên là tốt. Tôi hơi ngạc nhiên khi đây không phải là một phần của một số thư viện đã được thiết lập như dấu gạch ngang hoặc thứ gì đó. Nó dường như xuất hiện mọi lúc khi xử lý dữ liệu json.
abingham

2

Đây là một giải pháp chung chung hơn:

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

Nó có thể lấy bất kỳ "đường dẫn" nào của các phím. Điều này sẽ trở lại(bar . "llama")

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

trong khi điều này sẽ trở lại (baz . "monkey"):

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

3
Có downvote đầu tiên của tôi cho câu trả lời này. Bất cứ ai sẵn sàng cho tôi biết tại sao?
rekado

1
Tôi không đồng ý với downvote vì mã của bạn hoạt động (+1). Suy đoán của tôi là câu trả lời của @ Malabarba rõ ràng chung chung / thanh lịch hơn các câu trả lời khác được cung cấp, và vì vậy các câu trả lời khác nhận được sự phản đối không phải vì chúng không hiệu quả, mà vì chúng không phải là câu trả lời hay nhất. (Điều đó đang được nói, tôi thích tùy chọn "upvote the best" hơn là "upvote the best và downvote the other".)
Dan

1
Hai câu hỏi này đã bị hạ cấp bởi vì có một người ở đây không hiểu rõ cách thức hoạt động của downvote (và chọn bỏ qua yêu cầu của giao diện để để lại nhận xét). Thật không may, nhưng điều tốt nhất chúng ta có thể làm là nâng cấp.
Malabarba

0

Đây là một chức năng đơn giản hoạt động với một alist lồng trong một alist khác:

(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
Xin mọi người, khi bạn bỏ phiếu, để lại nhận xét.
Malabarba

1
Bất cứ ai bị hạ bệ: Tôi không bị xúc phạm, nhưng tôi tò mò tại sao. @Malabara: bây giờ có một chủ đề meta về định mức trên "downvote + bình luận"? ; Tôi sẽ tò mò về việc của bạn.
Dân
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.