Memetakan fungsi di seluruh daftar properti?

17

T: apa cara idiomatis untuk memetakan suatu fungsi di seluruh daftar properti?

Berbagai fungsi pemetaan ( mapcardan keluarga) memetakan fungsi melalui urutan seperti daftar. Bagaimana seseorang menggunakan fungsi-fungsi ini ketika berhadapan dengan daftar properti , yaitu, ketika mencoba memetakan di setiap properti yang ada dalam daftar (yang akan menjadi setiap elemen lainnya mulai dari yang pertama)? Sepertinya saya bahwa fungsi pemetaan perlu mengakses daftar berpasangan elemen daripada sebagai elemen individu.

Sebagai contoh mainan, bagaimana orang akan mengambil daftar properti dan mengumpulkan semua nilai properti? Jika itu adalah daftar asosiasi, itu akan sangat sederhana:

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

Saya yakin ini bisa dilakukan dengan loop, tetapi tampaknya sedikit melelahkan dan saya bertanya-tanya apakah ada cara yang lebih idiomatis untuk melakukannya.

Dan
sumber
Harap klarifikasi apakah Anda ingin memetakan hanya nilai properti (yang kedengarannya seperti apa, dan seperti apa mapcarcontoh alist Anda) atau Anda ingin memetakan pasangan simbol properti dan nilai properti. Yang terakhir lebih umum (lebih bermanfaat secara umum), saya kira.
Drew
@Rew: Saya lebih tertarik pada kasus umum; contohnya adalah yang paling sederhana yang dapat saya pikirkan. Saya ingin tahu bagaimana untuk memetakan atas properti / nilai pasangan . Jika jawabannya adalah "lingkaran," maka jadilah itu, tapi saya bertanya-tanya apakah ada solusi yang lebih elegan.
Dan
Apa yang Anda miliki dalam contoh Anda bukan daftar properti. Daftar properti bahkan memiliki jumlah elemen, elemen aneh adalah nama properti, bahkan elemen adalah nilai properti. Apa yang Anda miliki, mungkin, disebut pohon (daftar daftar).
wvxvw

Jawaban:

8

Anda kemungkinan akan mendapatkan berbagai loopjawaban dan iterasi. AFAIK, tidak ada cara idiomatis untuk melakukan ini. Saya bahkan tidak berpikir bahwa sangat umum untuk hanya mengakumulasi nilai properti (tidak mengaitkannya dengan properti).

Ini adalah salah satu cara mudah untuk melakukannya:

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

Untuk kasus yang lebih umum, di mana Anda ingin memetakan fungsi biner di atas sebuah plist:

(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))
Drew
sumber
13

Ini mungkin tergantung pada suatu situasi. Secara umum, jika saya perlu mengikat sejumlah nilai dengan sejumlah nama, saya akan menggunakan tabel-hash, tetapi jika saya harus menggunakan daftar properti, saya akan menggunakan cl-loop. Di bawah ini adalah beberapa contoh:

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

Dan jika Anda memiliki struktur data yang Anda tunjukkan dalam contoh Anda:

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

Bagi saya jawabannya adalah mengubah daftar menjadi daftar, yang merupakan struktur data yang setara, hanya setengah dalamnya dan lebih mudah untuk dimanipulasi.

Stefan
sumber
3
Yah, itu semacam jawaban (dan saran yang bagus), tetapi lebih merupakan komentar.
Drew
4

Dengan Emacs dari Git HEAD saat ini yang akan menjadi Emacs 25, Anda dapat melakukan hal berikut, yaitu mengubah plist menjadi alist dengan mudah dengan seq-partitionfungsi baru , dan kemudian memproses entri alist menggunakan standar 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))

Lihatlah seq-partitiondokumen yang digunakan C-h f seq-partition RET.

Tassilo Horn
sumber
1

Satu ide adalah menggunakan -map-indexeddari dashdan menerapkan transformasi hanya untuk nilai ganjil dari daftar:

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

Gagasan lain adalah hanya mengonversi plist ke tabel hash, menggunakan ht<-plistdari ht:

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

Perhatikan bahwa hash-table tidak mempertahankan urutan item.

Mirzhan Irkegulov
sumber