Fonction pour fusionner deux listes de propriétés?

11

Je n'ai pas trouvé de fonction de bibliothèque Elisp standard pour fusionner deux listes de propriétés, comme ceci:

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

Je pourrais construire quelque chose avec dolist, mais avant de le faire, je voudrais vérifier que je ne néglige pas une fonction existante dans une bibliothèque.

Mises à jour, basées sur les commentaires :

  1. En réponse au commentaire "multiple":

J'imagine qu'il n'y a pas une telle fonction car il existe des réponses différentes (et éventuellement valides) à la question: que faire lorsque vous avez des noms de propriété en double avec des valeurs distinctes?

Oui, il y a une question sur la façon de fusionner les doublons, mais il existe relativement peu de façons de résoudre ce problème. Je vois deux approches générales. Premièrement, l'ordre des arguments pourrait résoudre les doublons; par exemple, le plus à droite gagne, comme dans la fusion de Clojure . Deuxièmement, la fusion pourrait déléguer à une fonction de rappel fournie par l'utilisateur, comme dans la fusion de Ruby .

Dans tous les cas, le fait qu'il existe différentes façons de le faire n'empêche pas de nombreuses autres bibliothèques standard de langage de fournir une fonction de fusion. Le même argument général pourrait être dit du tri, et pourtant Elisp fournit une fonctionnalité de tri.

  1. "Pourriez-vous élaborer?" / "Veuillez spécifier précisément le comportement que vous recherchez."

D'une manière générale, je suis ouvert à ce que la communauté Elisp utilise. Si vous souhaitez un exemple spécifique, voici un exemple qui fonctionnerait:

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

Et retourne

'(k1 1 k2 2 k3 0))

Ce serait un style gagnant à droite, comme la fusion de Clojure.

  1. "Ce sont des listes, alors ajoutez-les?"

Non, appendne conserve pas la sémantique des listes de propriétés . Cette:

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

Renvoie ceci:

(k1 1 k2 2 k2 0)

append est une fonction intégrée au `code source C '.

(ajouter et reposer des séquences)

Concatène tous les arguments et fait du résultat une liste. Le résultat est une liste dont les éléments sont les éléments de tous les arguments. Chaque argument peut être une liste, un vecteur ou une chaîne. Le dernier argument n'est pas copié, il est juste utilisé comme queue de la nouvelle liste.

  1. "Et votre exemple ne montre rien de tel qu'une fusion - il n'affiche même pas deux listes de propriétés."

Oui; il fait la fusion étape par étape. Il montre comment effectuer une fusion à l'aide des fonctions de liste de propriétés documentées d'Elisp est douloureusement détaillé:

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

Affichez simplement la valeur de sortie résultante de pl:

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

Pour réitérer, je suis capable d'écrire une fonction pour résoudre ce problème, mais je voulais d'abord savoir si une telle fonction existe quelque part dans l'usage courant.

Enfin, si vous avez voté contre la question parce que vous l'avez trouvée peu claire, je vous demanderais de reconsidérer maintenant que j'ai fait des efforts pour clarifier. Ce n'est pas un manque de recherche. La documentation Elisp sur les "Listes" ne répond pas à la question.

David J.
la source
2
Ce sont des listes, alors juste append?
abo-abo
2
Veuillez spécifier précisément le comportement que vous recherchez. Il existe de nombreuses façons de «fusionner» deux listes. Et votre exemple ne montre rien comme une fusion - il n'affiche même pas deux listes de propriétés. Jusqu'à présent, cette question devrait être classée comme peu claire. FWIW, sachez qu'une paire plus proche de l'avant d'un plist assombrit toute paire ayant la même clé qui est plus éloignée de l'avant. Donc, la fusion peut signifier mettre les éléments d'un plist avant les éléments de l'autre, etc.
Drew
1
@ abo-abo: Il s'avère que le manuel Emacs Lisp indique explicitement que les noms de propriété doivent être distincts .
Constantine
3
Pour faire gagner la liste la plus à droite, il vous suffit d'inverser l'ordre des listes auxquelles vous passez append: (let ((args '((:a 1 :b 1) (:b 2) (:a 3)))) (apply #'append (reverse args))) => (:a 3 :b 2 :a 1 :b 1)ce qui est le même tant (:a 3 :b 2 :a 1)que vous n'utilisez que les fonctions plist pour accéder au plist.
tarsius
1
@Constantine: à droite, bien que ni plist-getne plist-membersemble se soucier s'il y a plusieurs clés identiques. On dirait qu'ils se comportent de façon analogue à alistes à cet égard: (plist-get '(:a "a" :b "b" :a "c") :a) ==> "a". Pendant ce temps, (plist-put '(:a "a" :b "b" :a "c") :a "d")remplace la valeur de la première :aclé mais pas la seconde.
Dan

Réponses:

8

Le mode Org, qui est inclus avec Emacs, a une fonction de fusion 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))

Pour l'utiliser, vous devez d' (require 'org)abord charger le fichier. Malheureusement, c'est un très gros fichier, 900 + KB, donc ce n'est pas vraiment utilisable comme bibliothèque d'utilitaires. Quelque chose comme un paquet plist standard serait bien d'avoir.

J'ai commencé récemment une toute petite et j'ai réalisé que les listes et les listes ne sont pas traitées de la même manière, par exemple - par exemple (plist-get LIST KEY) vs (assoc KEY LIST), qui doit être une relique regrettable d'optimisation (ou?) .

Mais, oui, Emacs a besoin d'une belle bibliothèque de plist - je n'en ai pas trouvé une dans ma recherche, mais il est toujours possible qu'il y en ait une quelque part, sinon nous devrons en démarrer une et la mettre sur Elpa / Melpa .

Une bibliothèque d'alist avec la même interface serait bien d'avoir aussi.

Brian Burns
la source
6

La lecture du manuel et la navigation dans la liste C-u C-h a plist RETne permettent pas de fusionner deux listes de propriétés. Les extensions Common Lisp ne fournissent aucune fonction spécifique pour agir sur les listes de propriétés, mais uniquement la prise en charge ( getf/ setf/…). Vous devez donc soit compter sur une bibliothèque tierce, soit lancer la vôtre.

Rouler le vôtre n'est pas trop difficile. Cette implémentation utilise la dernière valeur en cas de conflit.

(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)
Gilles 'SO- arrête d'être méchant'
la source
agréable. Pourquoi êtes-vous copy-sequencele premier plist mais pas les autres? Et aussi, vous pouvez nettoyer un peu l'imbrication avec cadret cddr.
fommil
en fait, org-combine-plists(ci-dessous) est plus ou moins la version nettoyée. Je ne comprends toujours pas pourquoi ils ont copy-sequencela voiture.
fommil
0

Je sais que cela a déjà été répondu, mais au cas où quelqu'un serait intéressé, j'ai pris la orgmise en œuvre et joué un peu de golf de code dessus

(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))))))
fommil
la source