Função para mesclar duas listas de propriedades?


11

Não encontrei uma função da biblioteca Elisp padrão para mesclar duas listas de propriedades, como esta:

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

Eu poderia criar algo com dolist, mas antes de fazer, gostaria de verificar se não estou negligenciando uma função existente em alguma biblioteca.

Atualizações, com base nos comentários :

  1. Em resposta ao comentário "de várias maneiras":

Eu imaginaria que não existe essa função, porque existem respostas diferentes (e possivelmente válidas) possíveis para a pergunta: o que fazer quando você tem nomes de propriedades duplicados com valores distintos?

Sim, há uma pergunta sobre como mesclar duplicatas, mas existem relativamente poucas maneiras de resolver isso. Eu vejo duas abordagens gerais. Primeiro, a ordem dos argumentos poderia resolver as duplicatas; por exemplo, vitórias mais à direita, como na mesclagem de Clojure . Segundo, a mesclagem pode delegar para uma função de retorno de chamada fornecida pelo usuário, como na mesclagem de Ruby .

De qualquer forma, o fato de existirem diferentes maneiras de fazer isso não impede que muitas outras bibliotecas padrão de idiomas forneçam uma função de mesclagem. O mesmo argumento geral pode ser dito sobre a classificação, e ainda o Elisp fornece a funcionalidade de classificação.

  1. "Você poderia elaborar?" / "Especifique o comportamento que você está procurando com precisão."

De um modo geral, estou aberto ao que a comunidade Elisp usa. Se você quiser um exemplo específico, aqui está um exemplo que funcionaria:

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

E retorna

'(k1 1 k2 2 k3 0))

Esse seria um estilo de vitórias mais à direita, como a mesclagem de Clojure.

  1. "São listas, então basta acrescentar?"

Não, appendnão preserva a semântica da lista de propriedades . Este:

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

Retorna isso:

(k1 1 k2 2 k2 0)

append é uma função embutida no `código fonte C '.

(acrescente e descanse SEQUÊNCIAS)

Concatene todos os argumentos e torne o resultado uma lista. O resultado é uma lista cujos elementos são os elementos de todos os argumentos. Cada argumento pode ser uma lista, vetor ou string. O último argumento não é copiado, apenas usado como o final da nova lista.

  1. "E seu exemplo não mostra nada como uma mesclagem - nem mostra duas listas de propriedades."

Sim; faz a fusão passo a passo. Ele mostra como fazer uma mesclagem usando as funções da lista de propriedades documentadas do Elisp é dolorosamente detalhado:

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

Basta exibir o valor de saída resultante de pl:

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

Para reiterar, sou capaz de escrever uma função para resolver esse problema, mas primeiro queria descobrir se essa função existe em algum lugar de uso comum.

Por fim, se você rebaixou a questão por achar que ela não estava clara, peço que você reconsidere agora que fiz um esforço para esclarecer. Isso não é falta de pesquisa. A documentação do Elisp em "Listas" não responde à pergunta.


2
São listas, apenas append?
abo-abo

2
Por favor, especifique o comportamento que você está procurando com precisão. Existem várias maneiras de "mesclar" duas listas. E seu exemplo não mostra nada como uma mesclagem - nem mostra duas listas de propriedades. Até agora, essa questão deve ser encerrada como incerta. FWIW, esteja ciente de que um par mais próximo à frente de uma plist sombreia qualquer par com a mesma chave mais distante da frente. Portanto, mesclar pode significar colocar elementos de uma lista antes de elementos da outra, etc. #
Drew

1
@ abo-abo: Acontece que o Manual Emacs Lisp declara explicitamente que os nomes das propriedades devem ser distintos .
Constantine

3
Para ganhar a lista mais à direita, basta reverter a ordem das listas para as quais você passa append: (let ((args '((:a 1 :b 1) (:b 2) (:a 3)))) (apply #'append (reverse args))) => (:a 3 :b 2 :a 1 :b 1)o mesmo (:a 3 :b 2 :a 1), desde que você use apenas as funções plist para acessar a lista.
tarsius

1
@ Constantine: certo, embora plist-getnem plist-memberpareça nem se importar se há várias chaves idênticas. Parece que eles se comportam de forma análoga ao alists a este respeito: (plist-get '(:a "a" :b "b" :a "c") :a) ==> "a". Enquanto isso, (plist-put '(:a "a" :b "b" :a "c") :a "d")substitui o valor da primeira :achave, mas não da segunda.
Dan

Respostas:


8

O modo organizacional, incluído no Emacs, possui uma função de mesclagem 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 usá-lo, você precisa (require 'org)primeiro carregar o arquivo. Infelizmente, é um arquivo muito grande, 900 + KB, portanto não é realmente utilizável como uma biblioteca de utilitários. Algo como um pacote plist padrão seria bom ter.

Comecei um pequeno muito recentemente e percebi que listas e listas não são tratadas da mesma forma, em termos de argumento - por exemplo (plist-get LIST KEY) vs (assoc KEY LIST), que deve ser uma relíquia infeliz da otimização (ou?) .

Mas, sim, o Emacs precisa de uma boa biblioteca de listas - não encontrei uma na minha pesquisa, mas ainda é possível que exista uma em algum lugar, ou então teremos que iniciar uma e colocá-la em Elpa / Melpa .

Uma biblioteca alist com a mesma interface também seria legal.


6

A leitura do manual e a navegação na lista C-u C-h a plist RETnão ativam nenhuma função para mesclar duas listas de propriedades. As extensões Common Lisp não fornecem nenhuma função especificamente para atuar em listas de propriedades, apenas colocam ( getf/ setf/…) suporte. Portanto, você precisa contar com uma biblioteca de terceiros ou criar sua própria.

Rolar o seu próprio não é muito difícil. Esta implementação usa o último valor em caso de conflito.

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

agradável. Por que você é copy-sequenceo primeiro, mas não os outros? E também, você pode limpar um pouco o aninhamento com cadre cddr.
fommil

na verdade, org-combine-plists(abaixo) é mais ou menos a versão limpa. Eu ainda não entendo por que eles estão copy-sequenceno carro.
21315

0

Eu sei que isso já foi respondido, mas caso alguém esteja interessado, eu peguei a orgimplementação e joguei um pouco de código de golfe nela

(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))))))
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.