Зіставити функцію у списку властивостей?


17

З: який ідіоматичний спосіб відображення функції у списку властивостей?

Різні функції відображення ( mapcarта сімейства) відображають функцію в такій послідовності, як список. Як користуються цими функціями при роботі зі списком властивостей , тобто при спробі відображення всіх властивостей, що містяться у списку (який би був кожен інший елемент, починаючи з першого)? Мені здається, що функція відображення потребує доступу до списку парами елементів, а не як окремі елементи.

Як іграшковий приклад, як можна взяти список властивостей та зібрати всі цінності майна? Якщо б це був список асоціацій, це було б досить просто:

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

Я впевнений, що це можна зробити за допомогою циклу, але це здається трохи трудомістким, і мені цікаво, чи існує більш ідіоматичний спосіб зробити це.


Будь ласка, уточніть, чи потрібно відображати лише значення властивостей (як це звучить, і як це робиться у вашому mapcarалліст-прикладі) чи ви хочете зіставити пари символів властивості та значення властивості. Останнє є більш загальним (більше загалом корисним), я б здогадався.
Дрю

@Drew: Мене більше цікавить загальна справа; приклад був найпростішим, про який я міг придумати. Мені хотілося б знати, як відобразити пару властивостей / цінностей . Якщо відповідь "петля", то так і буде, але мені цікаво, чи є більш елегантне рішення.
День

Що ви маєте у своєму прикладі, це не список властивостей. У списках властивостей є парна кількість елементів, непарні елементи - це назви властивостей, навіть елементи є значеннями властивостей. Те, що у вас було, ймовірно, називалося б деревом (список списків).
wvxvw

Відповіді:


8

Ви, ймовірно, отримаєте різні loopітераційні відповіді. AFAIK, немає ідіоматичного способу зробити це. Я навіть не думаю, що дуже часто хочеться накопичувати лише значення властивостей (не пов'язуючи їх із властивостями).

Ось один простий спосіб зробити це:

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

Для більш загального випадку, коли ви хочете зіставити бінарну функцію над плістом:

(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

Це, мабуть, залежатиме від ситуації. Взагалі, якщо мені потрібно зв'язати ряд значень з кількістю імен, я б використовував хеш-таблицю, але якщо мені доведеться використовувати список властивостей, я б використовував cl-loop. Нижче наведено кілька прикладів:

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

А якщо у вас є структура даних, ви показуєте у своєму прикладі:

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

5

Для мене відповідь полягає в тому, щоб перетворити пліст на алліст, який є рівнозначною структурою даних, на половину глибший і простіший в маніпулюванні.


3
Ну, це відповідь (і хороша порада), але більше коментар.
Дрю

4

З Emacs з поточної Git HEAD, яка стане Emacs 25, ви можете виконати наступне, тобто легко перетворити пліст в алліст за допомогою нової seq-partitionфункції, а потім обробити записи списків, використовуючи стандартні 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))

Погляньте на seq-partitionдокументи, що використовують C-h f seq-partition RET.


1

Одна ідея полягає в тому, щоб використовувати -map-indexedвід dashі застосувати перетворення тільки непарних значень списку:

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

Інша ідея полягає в тому, щоб просто перетворити PLIST в хеш-таблицю, використовуючи ht<-plistз ht:

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

Зауважте, що хеш-таблиця не зберігає порядок елементів.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.