Функція для об'єднання двох списків властивостей?


11

Я не знайшов стандартної функції бібліотеки Elisp для об'єднання двох списків властивостей, наприклад цього:

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

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

Оновлення на основі коментарів :

  1. У відповідь на "багатосторонній" коментар:

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

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

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

  1. "Не могли б ви детальніше?" / "Будь ласка, вкажіть точно поведінку, яку ви шукаєте."

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

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

І повертається

'(k1 1 k2 2 k3 0))

Це був би кращий стиль виграшів, як злиття Clojure.

  1. "Вони списки, тому просто додайте?"

Ні, appendне зберігає семантику списку властивостей . Це:

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

Повертає це:

(k1 1 k2 2 k2 0)

append - це вбудована функція у `C вихідному коді '.

(додавання та відпочинок СЕКВЕНЦІЇ)

Об'єднайте всі аргументи і складіть результат. Результатом є список, елементи якого є елементами всіх аргументів. Кожен аргумент може бути списком, вектором або рядком. Останній аргумент не копіюється, а використовується лише як хвіст нового списку.

  1. "І ваш приклад не показує нічого подібного до злиття - він навіть не показує два списки властивостей."

Так; вона робить злиття крок за кроком. Це показує, як злиття за допомогою документально підтверджених функцій списку властивостей Elisp є болісно багатослівним:

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

Просто виведіть отримане вихідне значення з pl:

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

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

Нарешті, якщо ви спростували це питання, тому що ви визнали його незрозумілим, я б просив вас переглядати зараз, коли я доклав певних зусиль для уточнення. Це не брак досліджень. Документація Elisp у "Списках" не відповідає на запитання.


2
Вони списки, так просто append?
або-або

2
Вкажіть, будь ласка, саме ту поведінку, яку ви шукаєте. Існує багато способів "об'єднати" два списки. І ваш приклад не показує нічого подібного до злиття - він навіть не показує два списки властивостей. Поки що це питання мало бути закритим як незрозуміле. FWIW, майте на увазі, що пара ближче до передньої частини пліси затіняє будь-яку пару, що має той самий ключ, який знаходиться далі від передньої частини. Тож злиття може означати розміщення елементів з одного плістера перед елементами з іншого тощо.
Дрю

1
@ abo-abo: виявляється, що в посібнику Emacs Lisp прямо вказано, що імена властивостей повинні бути чіткими .
Костянтин

3
Для того, щоб виграти найправіший список, вам просто потрібно змінити порядок списків, до яких ви переходите append: (let ((args '((:a 1 :b 1) (:b 2) (:a 3)))) (apply #'append (reverse args))) => (:a 3 :b 2 :a 1 :b 1)що таке саме (:a 3 :b 2 :a 1), доки ви використовуєте лише функції плісту для доступу до списку.
Тарсій

1
@ Константин: правильно, хоча ні, plist-getні plist-memberздається, байдуже, чи є кілька однакових клавіш. Схоже , що вони ведуть себе аналогічно alists в цьому відношенні: (plist-get '(:a "a" :b "b" :a "c") :a) ==> "a". Тим часом (plist-put '(:a "a" :b "b" :a "c") :a "d")замінює значення першого :aключа, а не другого.
Дан

Відповіді:


8

Org-режим, який входить до Emacs, має функцію злиття плістів:

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

Щоб скористатися нею, потрібно (require 'org)спочатку завантажити файл. На жаль, це дуже великий файл, 900 + КБ, тому він насправді не використовується як бібліотека утиліт. Щось на зразок стандартного пакету плістів було б непогано мати.

Нещодавно я почав дуже маленький і зрозумів, що алісти та плісти не трактуються однаково, аргументовано - наприклад (plist-get LIST KEY) vs (assoc KEY LIST), що повинно бути якоюсь нещасною реліквією оптимізації (або?) .

Але, так, Emacs потрібна прекрасна бібліотека плістів - я не шукав жодної в моєму пошуку, але все-таки можливо, що вона є десь там, інакше нам доведеться її запустити і помістити на Elpa / Melpa .

Було б непогано мати також бібліотеку з таким самим інтерфейсом.


6

Читання посібника та перегляд списку C-u C-h a plist RETне призводить до жодної функції для об'єднання двох списків властивостей. Загальні розширення Lisp не надають жодної функції, спеціально для дії зі списками властивостей, лише місце ( getf/ setf/…). Таким чином, вам або потрібно покластися на сторонні бібліотеки, або передати свою власну.

Згорнути свій власний не надто складно. Ця реалізація використовує останнє значення у випадку конфлікту.

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

приємно. Чому ти copy-sequenceперший філіст, а не інші? А також, ви можете трохи почистити гніздування за допомогою cadrі cddr.
fommil

насправді org-combine-plists(нижче) - це більш-менш очищена версія. Я досі не розумію, навіщо їм copy-sequenceмашину.
fommil

0

Я знаю, що на це вже відповіли, але, якщо хтось зацікавлений, я взяв на orgреалізацію і пограв у нього трохи гольфу коду

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