Chức năng hợp nhất hai danh sách tài sản?


11

Tôi chưa tìm thấy chức năng thư viện Elisp tiêu chuẩn để hợp nhất hai danh sách tài sản, như thế này:

(setq pl nil)
(setq pl (plist-put pl 'key-1 'value-1))
(setq pl (plist-put pl 'key-2 'value-2))

Tôi có thể xây dựng một cái gì đó với dolist, nhưng trước khi tôi làm, tôi muốn kiểm tra xem tôi không xem một chức năng hiện có trong một số thư viện.

Cập nhật, dựa trên các ý kiến :

  1. Đáp lại bình luận "nhiều cách":

Tôi sẽ tưởng tượng rằng không có chức năng như vậy bởi vì có câu trả lời khác nhau (và có thể hợp lệ) cho câu hỏi: phải làm gì khi bạn có tên thuộc tính trùng lặp với các giá trị riêng biệt?

Vâng, có một câu hỏi về cách hợp nhất các bản sao, nhưng có khá ít cách để giải quyết điều đó. Tôi thấy hai cách tiếp cận chung. Đầu tiên, thứ tự đối số có thể giải quyết các bản sao; ví dụ như chiến thắng ngoài cùng bên phải, như trong hợp nhất của Clojure . Thứ hai, việc hợp nhất có thể ủy quyền cho chức năng gọi lại do người dùng cung cấp, như trong hợp nhất của Ruby .

Trong mọi trường hợp, thực tế là có nhiều cách thực hiện khác nhau không ngăn cản nhiều thư viện chuẩn ngôn ngữ khác cung cấp chức năng hợp nhất. Lập luận chung tương tự có thể nói về sắp xếp, và Elisp cung cấp chức năng sắp xếp.

  1. "Bạn có thể giải thích?" / "Hãy xác định chính xác hành vi bạn đang tìm kiếm."

Nói chung, tôi cởi mở với những gì cộng đồng Elisp sử dụng. Nếu bạn muốn có một ví dụ cụ thể, đây sẽ là một ví dụ có thể hoạt động:

(a-merge-function '(k1 1) '(k2 2 k3 3) '(k3 0))

Và trả lại

'(k1 1 k2 2 k3 0))

Đây sẽ là một phong cách chiến thắng đúng nhất, như hợp nhất của Clojure.

  1. "Chúng là danh sách, vì vậy chỉ cần nối thêm?"

Không, appendkhông bảo tồn danh sách ngữ nghĩa tài sản . Điều này:

(append '(k1 1 k2 2) '(k2 0))

Trả về cái này:

(k1 1 k2 2 k2 0)

append là một hàm dựng sẵn trong 'mã nguồn C'.

(chắp thêm và nghỉ ngơi

Nối tất cả các đối số và làm cho kết quả thành một danh sách. Kết quả là một danh sách có các phần tử là các phần tử của tất cả các đối số. Mỗi đối số có thể là một danh sách, vectơ hoặc chuỗi. Đối số cuối cùng không được sao chép, chỉ được sử dụng làm đuôi của danh sách mới.

  1. "Và ví dụ của bạn không hiển thị bất cứ điều gì như hợp nhất - nó thậm chí không hiển thị hai danh sách tài sản."

Có nó làm; nó thực hiện hợp nhất từng bước. Nó cho thấy cách thực hiện hợp nhất bằng cách sử dụng các chức năng danh sách tài sản của Elisp rất dài dòng:

(setq pl nil)
(setq pl (plist-put pl 'key-1 'value-1))
(setq pl (plist-put pl 'key-2 'value-2))

Chỉ hiển thị giá trị đầu ra kết quả từ pl:

(key-1 value-1 key-2 value-2)

Để nhắc lại, tôi có khả năng viết một hàm để giải quyết vấn đề này, nhưng trước tiên tôi muốn tìm hiểu xem một hàm như vậy có tồn tại ở đâu đó trong sử dụng chung không.

Cuối cùng, nếu bạn đánh giá thấp câu hỏi vì bạn thấy nó không rõ ràng, tôi sẽ yêu cầu bạn xem xét lại rằng tôi đã nỗ lực để làm rõ. Đây không phải là một nghiên cứu thiếu. Tài liệu Elisp về "Danh sách" không trả lời câu hỏi.


2
Chúng là danh sách, vậy chỉ append?
abo-abo

2
Vui lòng xác định chính xác hành vi bạn đang tìm kiếm chính xác. Có nhiều cách để "hợp nhất" hai danh sách. Và ví dụ của bạn không hiển thị bất cứ điều gì như hợp nhất - nó thậm chí không hiển thị hai danh sách tài sản. Cho đến nay, câu hỏi này nên được đóng lại là không rõ ràng. FWIW, lưu ý rằng một cặp ở gần mặt trước của bóng plist bất kỳ cặp nào có cùng khóa nằm xa phía trước. Vì vậy, việc hợp nhất có thể có nghĩa là đặt các phần tử từ một phần tử trước các phần tử từ phần tử khác, v.v.
vẽ

1
@ abo-abo: Hóa ra Hướng dẫn Lisp của Emacs nói rõ rằng tên thuộc tính phải khác biệt .
Constantine

3
Để làm cho danh sách ngoài cùng bên phải giành chiến thắng, bạn chỉ cần đảo ngược thứ tự của các danh sách bạn chuyển đến append: (let ((args '((:a 1 :b 1) (:b 2) (:a 3)))) (apply #'append (reverse args))) => (:a 3 :b 2 :a 1 :b 1)giống (:a 3 :b 2 :a 1)như miễn là bạn chỉ sử dụng các hàm plist để truy cập vào bảng.
tarsius

1
@Constantine: đúng, mặc dù dường như plist-getkhông plist-memberquan tâm nếu có nhiều khóa giống nhau. Có vẻ như họ hành xử tương tự với những người theo chủ nghĩa này (plist-get '(:a "a" :b "b" :a "c") :a) ==> "a". Trong khi đó, (plist-put '(:a "a" :b "b" :a "c") :a "d")thay thế giá trị của :akhóa đầu tiên nhưng không phải là khóa thứ hai.
Dân

Câu trả lời:


8

Chế độ Org, được bao gồm trong Emacs, có chức năng hợp nhất plist:

(defun org-combine-plists (&rest plists)
  "Create a single property list from all plists in PLISTS.
The process starts by copying the first list, and then setting properties
from the other lists.  Settings in the last list are the most significant
ones and overrule settings in the other lists."
  (let ((rtn (copy-sequence (pop plists)))
        p v ls)
    (while plists
      (setq ls (pop plists))
      (while ls
        (setq p (pop ls) v (pop ls))
        (setq rtn (plist-put rtn p v))))
    rtn))

Để sử dụng nó, (require 'org)trước tiên bạn cần tải tệp. Thật không may, đó là một tệp rất lớn, 900 + KB, vì vậy nó không thực sự hữu dụng như một thư viện tiện ích. Một cái gì đó giống như một gói plist tiêu chuẩn sẽ là tốt đẹp để có.

Gần đây tôi đã bắt đầu một cái rất nhỏ và nhận ra rằng những người ủng hộ và những người không được đối xử như nhau, khôn ngoan - ví dụ (plist-get LIST KEY) vs (assoc KEY LIST), đó phải là một di tích đáng tiếc về tối ưu hóa (hoặc?) .

Nhưng, vâng, Emacs cần một thư viện plist đẹp - tôi đã không bắt gặp một cái trong tìm kiếm của mình, nhưng vẫn có thể có một cái ở đâu đó, nếu không chúng ta sẽ phải bắt đầu và đặt nó lên Elpa / Melpa .

Một thư viện alist với giao diện tương tự cũng sẽ rất tuyệt.


6

Đọc hướng dẫn và duyệt danh sách từ C-u C-h a plist RETkhông bật bất kỳ chức năng nào để hợp nhất hai danh sách tài sản. Các tiện ích mở rộng Lisp chung không cung cấp bất kỳ chức năng cụ thể nào để hoạt động trên danh sách tài sản, chỉ hỗ trợ địa điểm ( getf/ setf/). Vì vậy, bạn cần phải dựa vào thư viện của bên thứ ba hoặc tự cuộn.

Tự lăn không quá khó. Việc thực hiện này sử dụng giá trị cuối cùng trong trường hợp xung đột.

(defun plist-merge (&rest plists)
  (if plists
      (let ((result (copy-sequence (car plists))))
        (while (setq plists (cdr plists))
          (let ((plist (car plists)))
            (while plist
              (setq result (plist-put result (car plist) (car (cdr plist)))
                    plist (cdr (cdr plist))))))
        result)
    nil))

(plist-merge '(:x 2 :y 3)
             '(     :y 0 :z 7))
=>            (:x 2 :y 0 :z 7)

đẹp. Tại sao bạn copy-sequencelà người đầu tiên mà không phải là người khác? Ngoài ra, bạn có thể dọn sạch tổ một chút với cadrcddr.
fommil

thực tế, org-combine-plists(bên dưới) ít nhiều là phiên bản đã dọn sạch. Tôi vẫn không hiểu tại sao họ copy-sequencexe.
fommil

0

Tôi biết điều này đã được trả lời rồi, nhưng trong trường hợp có ai quan tâm, tôi đã orgthực hiện và chơi một chút golf mã trên đó

(defun plist-merge (&rest plists)
  "Create a single property list from all PLISTS.
Inspired by `org-combine-plists'."
  (let ((rtn (pop plists)))
    (dolist (plist plists rtn)
      (setq rtn (plist-put rtn
                           (pop plist)
                           (pop plist))))))
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.