Función para fusionar dos listas de propiedades?


11

No he encontrado una función de biblioteca estándar de Elisp para fusionar dos listas de propiedades, como esta:

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

Podría construir algo con dolist, pero antes de hacerlo, me gustaría comprobar que no estoy pasando por alto una función existente en alguna biblioteca.

Actualizaciones, basadas en los comentarios :

  1. En respuesta al comentario de "muchas formas":

Me imagino que no existe tal función porque hay respuestas diferentes (y posiblemente válidas) posibles a la pregunta: ¿qué hacer cuando tiene nombres de propiedad duplicados con valores distintos?

Sí, hay una pregunta sobre cómo combinar duplicados, pero hay relativamente pocas formas de abordarlo. Veo dos enfoques generales. Primero, el orden del argumento podría resolver los duplicados; Por ejemplo, la derecha gana, como en la fusión de Clojure . En segundo lugar, la fusión podría delegarse en una función de devolución de llamada proporcionada por el usuario, como en la fusión de Ruby .

En cualquier caso, el hecho de que haya diferentes formas de hacerlo no impide que muchas bibliotecas estándar de otros idiomas proporcionen una función de fusión. Se podría decir el mismo argumento general sobre la clasificación, y sin embargo, Elisp proporciona la funcionalidad de clasificación.

  1. "¿Podrías dar más detalles?" / "Especifique con precisión el comportamiento que está buscando".

En términos generales, estoy abierto a lo que usa la comunidad de Elisp. Si desea un ejemplo específico, este sería un ejemplo que funcionaría:

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

Y vuelve

'(k1 1 k2 2 k3 0))

Este sería un estilo de extrema derecha, como la fusión de Clojure.

  1. "Son listas, así que ¿solo agregar?"

No, appendno conserva la semántica de la lista de propiedades . Esta:

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

Devuelve esto:

(k1 1 k2 2 k2 0)

append es una función incorporada en el 'código fuente C'.

(agregar y descansar SECUENCIAS)

Concatena todos los argumentos y convierte el resultado en una lista. El resultado es una lista cuyos elementos son los elementos de todos los argumentos. Cada argumento puede ser una lista, un vector o una cadena. El último argumento no se copia, solo se usa como la cola de la nueva lista.

  1. "Y su ejemplo no muestra nada parecido a una fusión, ni siquiera muestra dos listas de propiedades".

Sí lo hace; se fusiona paso a paso. Muestra cómo hacer una fusión usando las funciones de la lista de propiedades documentadas de Elisp es dolorosamente detallado:

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

Simplemente muestre el valor de salida resultante de pl:

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

Para reiterar, soy capaz de escribir una función para resolver este problema, pero primero quería averiguar si dicha función existe en algún lugar de uso común.

Finalmente, si rechazó la pregunta porque no le quedó claro, le pediría que reconsiderara ahora que me he esforzado por aclarar. Esto no es una falta de investigación. La documentación de Elisp sobre "Listas" no responde la pregunta.


2
Son listas, ¿entonces solo append?
abo-abo

2
Especifique el comportamiento que está buscando con precisión. Hay muchas formas de "fusionar" dos listas. Y su ejemplo no muestra nada como una fusión, ni siquiera muestra dos listas de propiedades. Hasta ahora, esta pregunta debería cerrarse como poco clara. FWIW, tenga en cuenta que un par más cerca del frente de un plistón sombrea a cualquier par que tenga la misma tecla que está más lejos del frente. Así que fusionar puede significar poner elementos de una lista antes que elementos de la otra, etc.
Drew

1
@ abo-abo: Resulta que el Manual Emacs Lisp establece explícitamente que los nombres de propiedad deben ser distintos .
Constantine

3
Para ganar la lista más a la derecha, solo tiene que invertir el orden de las listas a las que pasa append: (let ((args '((:a 1 :b 1) (:b 2) (:a 3)))) (apply #'append (reverse args))) => (:a 3 :b 2 :a 1 :b 1)que es lo mismo (:a 3 :b 2 :a 1), siempre y cuando solo use las funciones plist para acceder a la lista.
tarsius

1
@Constantine: correcto, aunque plist-getni plist-memberparece importarle si hay varias claves idénticas. Parece que se comportan de forma análoga a alistas a este respecto: (plist-get '(:a "a" :b "b" :a "c") :a) ==> "a". Mientras tanto, (plist-put '(:a "a" :b "b" :a "c") :a "d")reemplaza el valor de la primera :aclave pero no la segunda.
Dan

Respuestas:


8

Org-mode, que se incluye con Emacs, tiene una función de fusión de 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))

Para usarlo, (require 'org)primero debe cargar el archivo. Desafortunadamente, es un archivo muy grande, 900 + KB, por lo que no es realmente útil como biblioteca de utilidades. Sería bueno tener algo así como un paquete plist estándar.

Hace poco comencé uno muy pequeño y me di cuenta de que las listas y listas no son tratadas de la misma manera, en términos de argumentos, por ejemplo (plist-get LIST KEY) vs (assoc KEY LIST), que debe ser una desafortunada reliquia de optimización (o?) .

Pero sí, Emacs necesita una buena biblioteca plist: no encontré una en mi búsqueda, pero aún es posible que haya una en algún lugar, o tendremos que comenzar una y ponerla en Elpa / Melpa .

También sería bueno tener una biblioteca de listas con la misma interfaz.


6

Leer el manual y explorar la lista C-u C-h a plist RETno activa ninguna función para fusionar dos listas de propiedades. Las extensiones Common Lisp no proporcionan ninguna función específica para actuar en listas de propiedades, solo colocan ( getf/ setf/ ...) soporte. Por lo tanto, debe confiar en una biblioteca de terceros o crear una propia.

Rodar el tuyo no es demasiado difícil. Esta implementación utiliza el último valor en caso de conflicto.

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

bonito. ¿Por qué eres copy-sequencela primera lista pero no las otras? Y también, puedes limpiar un poco la anidación con cadry cddr.
fommil

en realidad, org-combine-plists(abajo) es más o menos la versión limpiada. Todavía no entiendo por qué copy-sequenceel auto.
fommil

0

Sé que esto ya ha sido respondido, pero en caso de que alguien esté interesado, tomé la orgimplementación y jugué un poco de código de 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))))))
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.