Funkcja łączenia dwóch list właściwości?


11

Nie znalazłem standardowej funkcji biblioteki Elisp do scalania dwóch list właściwości, takich jak to:

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

Mógłbym coś zbudować dolist, ale zanim to zrobię, chciałbym sprawdzić, czy nie przeoczam istniejącej funkcji w jakiejś bibliotece.

Aktualizacje na podstawie komentarzy :

  1. W odpowiedzi na komentarz „na wiele sposobów”:

Wyobrażam sobie, że nie ma takiej funkcji, ponieważ istnieją różne (i być może prawidłowe) odpowiedzi na pytanie: co zrobić, gdy masz zduplikowane nazwy właściwości o różnych wartościach?

Tak, istnieje pytanie, jak scalić duplikaty, ale istnieje stosunkowo niewiele sposobów rozwiązania tego problemu. Widzę dwa ogólne podejścia. Po pierwsze, kolejność argumentów może rozwiązać duplikaty; np. wygrane z prawej strony, jak w przypadku połączenia Clojure . Po drugie, scalanie może delegować na funkcję zwrotną podaną przez użytkownika, tak jak w przypadku scalania Ruby .

W każdym razie fakt, że istnieją różne sposoby wykonania tego zadania, nie przeszkadza wielu innym standardowym bibliotekom językowym w zapewnieniu funkcji scalania. Ten sam ogólny argument można powiedzieć o sortowaniu, a jednak Elisp zapewnia funkcjonalność sortowania.

  1. „Czy mógłbyś opracować?” / „Proszę dokładnie określić zachowanie, którego szukasz.”

Ogólnie rzecz biorąc, jestem otwarty na to, czego używa społeczność Elisp. Jeśli chcesz konkretny przykład, oto jeden przykład, który działałby:

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

I wraca

'(k1 1 k2 2 k3 0))

Byłby to styl najbardziej zwycięski, jak połączenie Clojure.

  1. „To listy, więc po prostu dołącz?”

Nie, appendnie zachowuje semantyki listy właściwości . To:

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

Zwraca to:

(k1 1 k2 2 k2 0)

append to wbudowana funkcja w kodzie źródłowym `C '.

(dołącz i odpoczywaj SEKWENCJE)

Połącz wszystkie argumenty i ustaw wynik jako listę. Wynikiem jest lista, której elementy są elementami wszystkich argumentów. Każdy argument może być listą, wektorem lub łańcuchem. Ostatni argument nie jest kopiowany, tylko używany jako ogon nowej listy.

  1. „A twój przykład nie pokazuje scalenia - nie pokazuje nawet dwóch list właściwości”.

Tak; wykonuje scalanie krok po kroku. To pokazuje, jak wykonywanie scalania przy użyciu udokumentowanych funkcji listy właściwości Elisp jest boleśnie pełne:

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

Po prostu wyświetl wynikową wartość wyjściową z pl:

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

Powtarzając, jestem w stanie napisać funkcję rozwiązującą ten problem, ale najpierw chciałem dowiedzieć się, czy taka funkcja istnieje gdzieś w powszechnym użyciu.

Na koniec, jeśli głosowałeś za pytaniem, ponieważ okazało się, że jest niejasne, poprosiłbym o ponowne rozważenie teraz, kiedy dołożyłem starań, aby wyjaśnić. To nie jest brak badań. Dokumentacja Elisp dotycząca „List” nie odpowiada na pytanie.


2
To są listy, więc po prostu append?
abo-abo

2
Proszę dokładnie określić zachowanie, którego szukasz. Istnieje wiele sposobów na „scalenie” dwóch list. A twój przykład nie pokazuje nic takiego jak scalenie - nie pokazuje nawet dwóch list właściwości. Do tej pory pytanie to powinno być zamknięte jako niejasne. FWIW, pamiętaj, że para bliżej przedniej części listwy cienia każdą parę mającą ten sam klucz, który znajduje się dalej od przodu. Zatem scalanie może oznaczać umieszczanie elementów z jednej listy przed elementami z drugiej itd.
Drew

1
@ abo-abo: Okazuje się, że Podręcznik Emacs Lisp wyraźnie stwierdza, że nazwy właściwości muszą być różne .
Constantine

3
Aby wygrać listę z prawej strony, wystarczy odwrócić kolejność list, które przekazujesz append: (let ((args '((:a 1 :b 1) (:b 2) (:a 3)))) (apply #'append (reverse args))) => (:a 3 :b 2 :a 1 :b 1)co jest takie samo, (:a 3 :b 2 :a 1)o ile używasz tylko funkcji plist, aby uzyskać dostęp do listy.
Tarsius

1
@Constantine: racja, chociaż ani plist-getnie plist-memberwydaje się, aby to obchodziło, czy istnieje wiele identycznych kluczy. Wygląda na to, że zachowują się analogicznie do alists pod tym względem (plist-get '(:a "a" :b "b" :a "c") :a) ==> "a". Tymczasem (plist-put '(:a "a" :b "b" :a "c") :a "d")zastępuje wartość pierwszego :aklucza, ale nie drugiego.
Dan

Odpowiedzi:


8

Tryb Org, który jest dołączony do Emacsa, ma funkcję scalania 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))

Aby go użyć, musisz (require 'org)najpierw załadować plik. Niestety, jest to bardzo duży plik, ponad 900 KB, więc tak naprawdę nie można go używać jako biblioteki narzędziowej. Byłoby miło mieć coś takiego jak standardowy pakiet Plist.

Niedawno założyłem bardzo mały i zdałem sobie sprawę, że listy i listy nie są traktowane tak samo, jeśli chodzi o argumenty - np. (Plist-get LIST KEY) vs (assoc KEY LIST), który musi być jakimś niefortunnym reliktem optymalizacji (lub?) .

Ale tak, Emacs potrzebuje ładnej biblioteki list - nie natknąłem się na jedną w moich poszukiwaniach, ale nadal jest możliwe, że gdzieś tam jest, albo będziemy musieli ją założyć i umieścić na Elpa / Melpa .

Dobrze byłoby mieć także bibliotekę alist z tym samym interfejsem.


6

Czytanie instrukcji i przeglądanie listy C-u C-h a plist RETnie powoduje włączenia żadnej funkcji do scalenia dwóch list właściwości. Rozszerzenia Common Lisp nie zapewniają żadnej funkcji działającej na listach właściwości, obsługują tylko miejsca ( getf/ setf/…). Musisz więc polegać na bibliotece innej firmy lub stworzyć własną.

Toczenie własnego nie jest zbyt trudne. Ta implementacja używa ostatniej wartości w przypadku konfliktu.

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

ładny. Dlaczego jesteś copy-sequencepierwszym plistą, a nie innymi? A także możesz trochę posprzątać zagnieżdżanie za pomocą cadri cddr.
fommil,

tak naprawdę org-combine-plists(poniżej) to mniej więcej oczyszczona wersja. Nadal nie rozumiem, dlaczego to copy-sequencesamochód.
fommil,

0

Wiem, że już na to odpowiedziano, ale jeśli ktoś jest zainteresowany, wziąłem orgimplementację i zagrałem w nią trochę golfa

(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))))))
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.