Funzione per unire due elenchi di proprietà?


11

Non ho trovato una funzione di libreria Elisp standard per unire due elenchi di proprietà, in questo modo:

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

Potrei costruire qualcosa con dolist, ma prima di farlo, vorrei verificare che non sto trascurando una funzione esistente in qualche libreria.

Aggiornamenti, basati sui commenti :

  1. In risposta al commento "molti modi":

Immagino che non ci sia una tale funzione perché ci sono risposte diverse (e forse valide) alla domanda: cosa fare quando si hanno nomi di proprietà duplicati con valori distinti?

Sì, c'è una domanda su come unire i duplicati, ma ci sono relativamente pochi modi per affrontarlo. Vedo due approcci generali. Innanzitutto, l'ordine degli argomenti potrebbe risolvere i duplicati; es. vince all'estrema destra, come nella fusione di Clojure . In secondo luogo, l'unione potrebbe delegare a una funzione di richiamata fornita dall'utente, come nell'unione di Ruby .

In ogni caso, il fatto che ci siano diversi modi per farlo non impedisce a molte altre librerie standard di lingue di fornire una funzione di unione. Lo stesso argomento generale si potrebbe dire dell'ordinamento, eppure Elisp fornisce funzionalità di ordinamento.

  1. "Potresti elaborare?" / "Specifica con precisione il comportamento che stai cercando."

In generale, sono aperto a ciò che utilizza la comunità Elisp. Se desideri un esempio specifico, ecco un esempio che funzionerebbe:

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

E ritorna

'(k1 1 k2 2 k3 0))

Questo sarebbe uno stile vincente all'estrema destra, come l'unione di Clojure.

  1. "Sono liste, quindi basta aggiungere?"

No, appendnon conserva la semantica dell'elenco delle proprietà . Questo:

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

Restituisce questo:

(k1 1 k2 2 k2 0)

append è una funzione integrata in "codice sorgente C".

(aggiungere e riposare SEQUENZE)

Concatena tutti gli argomenti e trasforma il risultato in un elenco. Il risultato è un elenco i cui elementi sono gli elementi di tutti gli argomenti. Ogni argomento può essere un elenco, un vettore o una stringa. L'ultimo argomento non viene copiato, utilizzato solo come coda del nuovo elenco.

  1. "E il tuo esempio non mostra nulla di simile a un'unione - non mostra nemmeno due elenchi di proprietà."

Sì lo fa; fa l'unione passo dopo passo. Mostra come eseguire un'unione usando le funzioni dell'elenco delle proprietà documentate di Elisp sia penosamente prolisso:

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

Visualizza semplicemente il valore di output risultante da pl:

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

Per ribadire, sono in grado di scrivere una funzione per risolvere questo problema, ma prima volevo capire se una tale funzione esiste da qualche parte nell'uso comune.

Infine, se hai annullato la votazione della domanda perché l'hai trovata poco chiara, ti chiederei di riconsiderare ora che ho fatto qualche sforzo per chiarire. Questa non è una mancanza di ricerca. La documentazione Elisp su "Elenchi" non risponde alla domanda.


2
Sono liste, quindi solo append?
abo-ABO

2
Si prega di specificare il comportamento che si sta cercando con precisione. Esistono molti modi per "unire" due elenchi. E il tuo esempio non mostra nulla di simile a un'unione - non mostra nemmeno due elenchi di proprietà. Finora, questa domanda dovrebbe essere chiusa come poco chiara. FWIW, tieni presente che una coppia più vicina alla parte anteriore di un plist ombreggia qualsiasi coppia che abbia la stessa chiave più lontana dalla parte anteriore. Quindi fondersi può significare mettere elementi da un piano prima di elementi dall'altro, ecc.
Disegnato il

1
@ abo-abo: si scopre che il manuale Emacs Lisp afferma esplicitamente che i nomi delle proprietà devono essere distinti .
Costantino,

3
Per vincere la lista più a destra devi solo invertire l'ordine delle liste a cui passi append: (let ((args '((:a 1 :b 1) (:b 2) (:a 3)))) (apply #'append (reverse args))) => (:a 3 :b 2 :a 1 :b 1)che è lo stesso (:a 3 :b 2 :a 1)fintanto che usi solo le funzioni di plist per accedere alla plist.
Tarsius,

1
@Constantine: a destra, anche se nessuno plist-getne plist-membersembrano preoccuparsi se ci sono più chiavi identiche. Sembra che si comportano in modo analogo a alists a questo riguardo: (plist-get '(:a "a" :b "b" :a "c") :a) ==> "a". Nel frattempo, (plist-put '(:a "a" :b "b" :a "c") :a "d")sostituisce il valore della prima :achiave ma non della seconda.
Dan

Risposte:


8

La modalità Org, inclusa in Emacs, ha una funzione di unione 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))

Per usarlo dovresti (require 'org)prima caricare il file. Sfortunatamente, è un file molto grande, 900 + KB, quindi non è realmente utilizzabile come libreria di utilità. Qualcosa come un pacchetto plist standard sarebbe bello avere.

Ne ho iniziato uno molto piccolo di recente e mi sono reso conto che gli elenchi e i piani non sono trattati allo stesso modo, per quanto riguarda l'argomento - ad esempio (plist-get LIST KEY) vs (assoc KEY LIST), che deve essere una sfortunata reliquia dell'ottimizzazione (o?) .

Ma sì, Emacs ha bisogno di una bella libreria di plist: non ne ho trovato uno nella mia ricerca, ma è ancora possibile che ce ne sia uno là fuori da qualche parte, altrimenti dovremo avviarne uno e metterlo su Elpa / Melpa .

Sarebbe bello avere anche una libreria di alist con la stessa interfaccia.


6

Leggere il manuale e sfogliare l'elenco da C-u C-h a plist RETnon attiva alcuna funzione per unire due elenchi di proprietà. Le estensioni Common Lisp non forniscono alcuna funzione specifica per agire sugli elenchi di proprietà, ma solo per posizionare ( getf/ setf/ ...) il supporto. Quindi devi fare affidamento su una libreria di terze parti o crearne una tua.

Rotolare il tuo non è troppo difficile. Questa implementazione utilizza l'ultimo valore in caso di conflitto.

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

simpatico. Perché sei copy-sequenceil primo pianista ma non gli altri? Inoltre, puoi ripulire un po 'l'annidamento con cadre cddr.
fommil

in realtà, org-combine-plists(sotto) è più o meno la versione ripulita. Ancora non capisco perché loro copy-sequencela macchina.
fommil

0

So che è già stata data una risposta, ma in caso qualcuno fosse interessato, ho preso l' orgimplementazione e ci ho giocato un po 'con il golf

(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))))))
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.