Ánh xạ một chức năng trên một danh sách tài sản?


17

Q: cách thành ngữ để ánh xạ một chức năng qua danh sách tài sản là gì?

Các hàm ánh xạ khác nhau ( mapcarvà gia đình) ánh xạ một hàm qua một chuỗi như danh sách. Làm thế nào để sử dụng các chức năng này khi giao dịch với một tài sản danh sách, ví dụ, khi cố gắng để lập bản đồ trên mỗi trong những tài sản chứa trong danh sách (đó sẽ là mọi phần tử khác bắt đầu từ cái đầu tiên)? Dường như với tôi rằng chức năng ánh xạ sẽ cần truy cập vào danh sách theo từng cặp phần tử chứ không phải là các phần tử riêng lẻ.

Như một ví dụ về đồ chơi, làm thế nào một người có thể lấy một danh sách tài sản và thu thập tất cả các giá trị tài sản? Nếu đó là một danh sách liên kết thay vào đó, nó sẽ khá đơn giản:

(mapcar #'cadr '((:prop1 a) (:prop2 b) (:prop3 c))) ;=> (a b c)

Tôi chắc chắn rằng điều này có thể được thực hiện với một vòng lặp, nhưng có vẻ hơi tốn công và tôi tự hỏi liệu có cách nào thành ngữ hơn để làm điều đó không.


Vui lòng làm rõ xem bạn muốn ánh xạ chỉ các giá trị thuộc tính (đó là những gì nó nghe giống như, và đó là những gì mapcarví dụ tồn tại của bạn làm) hoặc bạn muốn ánh xạ qua các cặp ký hiệu thuộc tính và giá trị thuộc tính. Cái sau nói chung hơn (nói chung là hữu ích hơn), tôi đoán vậy.
vẽ

@Drew: Tôi quan tâm nhiều hơn đến trường hợp chung; ví dụ đơn giản nhất tôi có thể nghĩ ra. Tôi muốn biết cách ánh xạ cặp tài sản / giá trị . Nếu câu trả lời là "một vòng lặp" thì cũng vậy, nhưng tôi tự hỏi liệu có một giải pháp nào thanh lịch hơn không.
Dan

Những gì bạn có trong ví dụ của bạn không phải là một danh sách tài sản. Danh sách tài sản có số phần tử chẵn, phần tử lẻ là tên thuộc tính, thậm chí phần tử là giá trị thuộc tính. Những gì bạn có, có lẽ, được gọi là một cái cây (một danh sách các danh sách).
wvxvw

Câu trả lời:


8

Bạn có thể sẽ nhận được loopcâu trả lời khác nhau và lặp đi lặp lại. AFAIK, không có cách thành ngữ để làm điều này. Tôi thậm chí không nghĩ rằng chỉ muốn tích lũy các giá trị tài sản (không liên kết chúng với các thuộc tính) là điều rất phổ biến.

Đây là một cách đơn giản để làm điều đó:

(defun prop-values (plist)
  "..."
  (let ((pl    (cdr plist))
        (vals  ()))
    (while pl
      (push (car pl) vals)
      (setq pl  (cddr pl)))
    (nreverse vals)))

Đối với trường hợp tổng quát hơn, nơi bạn muốn ánh xạ hàm nhị phân qua một số nguyên:

(defun map-plist (fn plist)
  "..."
  (let ((pl    plist)
        (vals  ()))
    (while pl
      (push (funcall fn (car pl) (cadr pl)) vals)
      (setq pl (cddr pl)))
    (nreverse vals)))

(setq foo '(a 1 b 2 c 3 d 4 e 5))

(map-plist #'cons foo) ; => ((a . 1) (b . 2) (c . 3) (d . 4) (e . 5))

13

Điều này có thể sẽ phụ thuộc vào một tình huống. Nói chung, nếu tôi cần buộc một số giá trị với một số tên, tôi sẽ sử dụng bảng băm, nhưng nếu tôi phải sử dụng danh sách thuộc tính, tôi sẽ sử dụng cl-loop. Dưới đây là một số ví dụ:

(cl-loop for (key value) on '(:prop1 a :prop2 b :prop3 c) by 'cddr
         collect value)
;;; (a b c)

Và nếu bạn có cấu trúc dữ liệu bạn hiển thị trong ví dụ của mình:

(cl-loop for (key value) in '((:prop1 a) (:prop2 b) (:prop3 c))
         collect value)
;;; (a b c)

5

Đối với tôi, câu trả lời là biến plist thành một alist, đây là một cấu trúc dữ liệu tương đương, chỉ sâu bằng một nửa và dễ thao tác hơn.


3
Vâng, đó là một câu trả lời (và lời khuyên tốt), nhưng nhiều bình luận hơn.
vẽ

4

Với Emacs từ Git HEAD hiện tại sẽ trở thành Emacs 25, bạn có thể thực hiện các thao tác sau, tức là biến plist thành một khối dễ dàng với seq-partitionchức năng mới , sau đó xử lý các mục nhập bằng cách sử dụng tiêu chuẩn mapcar.

(let* ((my-plist (list :a 1 :b 2 :c 3 :more (list 4 5 6)))
       (my-alist (seq-partition my-plist 2))
       (my-reverse-alist (mapcar (lambda (entry)
                                   (let ((prop (car entry))
                                         (val  (cadr entry)))
                                     (list val prop)))
                                 my-alist)))
  (message "my-plist: %s\nmy-alist: %s\nmy-reverse-alist: %s"
           my-plist my-alist my-reverse-alist))
;; my-plist: (:a 1 :b 2 :c 3 :more (4 5 6))
;; my-alist: ((:a 1) (:b 2) (:c 3) (:more (4 5 6)))
;; my-reverse-alist: ((1 :a) (2 :b) (3 :c) ((4 5 6) :more))

Có một cái nhìn vào các seq-partitiontài liệu bằng cách sử dụng C-h f seq-partition RET.


1

Một ý tưởng là sử dụng -map-indexedtừ dashvà áp dụng chuyển đổi chỉ với các giá trị lẻ của danh sách:

(-non-nil (-map-indexed (lambda (index item) (when (oddp index) item))
  '(a x b y c z))) ; => (x y z)
(-non-nil (--map-indexed (when (oddp it-index) it) '(a x b y c z))) ; => (x y z)

Một ý tưởng khác là chỉ cần chuyển đổi một bảng thành bảng băm, sử dụng ht<-plisttừ ht:

(ht-values (ht<-plist '(a x b y c z) 'equal)) ; (z y x)

Lưu ý rằng bảng băm không bảo toàn thứ tự của các mục.

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.