Eine Funktion über eine Eigenschaftsliste zuordnen?

17

F: Wie lässt sich eine Funktion idiomatisch auf eine Eigenschaftsliste abbilden?

Die verschiedenen Zuordnungsfunktionen ( mapcarund die Familie) ordnen eine Funktion über eine Sequenz wie eine Liste zu. Wie verwendet man diese Funktionen, wenn man mit einer Eigenschaftsliste umgeht, dh wenn versucht wird, alle in der Liste enthaltenen Eigenschaften (die jedes andere Element ab dem ersten sein würden) zuzuordnen? Es scheint mir, dass die Zuordnungsfunktion auf die Liste in Paaren von Elementen und nicht als einzelne Elemente zugreifen müsste.

Wie würde man als Spielzeugbeispiel eine Eigenschaftsliste nehmen und alle Eigenschaftswerte sammeln? Wenn es stattdessen eine Assoziationsliste wäre, wäre es ziemlich einfach:

(mapcar #'cadr '((:prop1 a) (:prop2 b) (:prop3 c))) ;=> (a b c)

Ich bin mir sicher, dass dies mit einer Schleife möglich ist, aber es scheint ein wenig mühsam zu sein, und ich frage mich, ob es einen idiomatischeren Weg gibt, dies zu tun.

Dan
quelle
Bitte klären Sie, ob Sie nur die Eigenschaftswerte abbilden möchten (wie es sich anhört und was Ihr mapcarAlist-Beispiel tut) oder ob Sie die Paare von Eigenschaftssymbol und Eigenschaftswert abbilden möchten. Letzteres ist allgemeiner (allgemeiner nützlich), würde ich vermuten.
Drew
@Drew: Mich interessiert eher der allgemeine Fall; Das Beispiel war das einfachste, an das ich denken konnte. Ich möchte wissen, wie man das Eigenschafts- / Wert- Paar abbildet . Wenn die Antwort "eine Schleife" ist, dann soll es so sein, aber ich frage mich, ob es eine elegantere Lösung gibt.
Dan
Was Sie in Ihrem Beispiel haben, ist keine Eigenschaftsliste. Eigenschaftslisten haben eine gerade Anzahl von Elementen, ungerade Elemente sind Eigenschaftsnamen, gerade Elemente sind Eigenschaftswerte. Was Sie haben, wäre wahrscheinlich ein Baum (eine Liste von Listen).
wvxvw

Antworten:

8

Sie erhalten wahrscheinlich verschiedene loopund Iterationsantworten. AFAIK, es gibt keine idiomatische Möglichkeit, dies zu tun. Ich denke nicht einmal, dass es üblich ist, nur die Eigenschaftswerte zu akkumulieren (ohne sie mit den Eigenschaften zu verknüpfen).

Hier ist eine einfache Möglichkeit:

(defun prop-values (plist)
  "..."
  (let ((pl    (cdr plist))
        (vals  ()))
    (while pl
      (push (car pl) vals)
      (setq pl  (cddr pl)))
    (nreverse vals)))

Für den allgemeineren Fall, dass Sie eine Binärfunktion über eine Plist abbilden möchten:

(defun map-plist (fn plist)
  "..."
  (let ((pl    plist)
        (vals  ()))
    (while pl
      (push (funcall fn (car pl) (cadr pl)) vals)
      (setq pl (cddr pl)))
    (nreverse vals)))

(setq foo '(a 1 b 2 c 3 d 4 e 5))

(map-plist #'cons foo) ; => ((a . 1) (b . 2) (c . 3) (d . 4) (e . 5))
Drew
quelle
13

Dies würde wahrscheinlich von einer Situation abhängen. Wenn ich eine Anzahl von Werten mit einer Anzahl von Namen verknüpfen muss, würde ich im Allgemeinen eine Hash-Tabelle verwenden, aber wenn ich eine Eigenschaftsliste verwenden muss, würde ich verwenden cl-loop. Nachfolgend einige Beispiele:

(cl-loop for (key value) on '(:prop1 a :prop2 b :prop3 c) by 'cddr
         collect value)
;;; (a b c)

Und wenn Sie eine Datenstruktur haben, zeigen Sie in Ihrem Beispiel:

(cl-loop for (key value) in '((:prop1 a) (:prop2 b) (:prop3 c))
         collect value)
;;; (a b c)
wvxvw
quelle
5

Für mich ist die Antwort, die Plist in eine Alist zu verwandeln, die eine äquivalente Datenstruktur darstellt, nur halb so tief und einfacher zu manipulieren ist.

Stefan
quelle
3
Nun, das ist eine Art Antwort (und ein guter Rat), aber eher ein Kommentar.
Drew
4

Mit Emacs aus dem aktuellen Git HEAD, das zu Emacs 25 wird, können Sie Folgendes tun, dh, Sie können die Liste mit der neuen seq-partitionFunktion auf einfache Weise in eine Liste umwandeln und dann die Listeneinträge mit Standard verarbeiten mapcar.

(let* ((my-plist (list :a 1 :b 2 :c 3 :more (list 4 5 6)))
       (my-alist (seq-partition my-plist 2))
       (my-reverse-alist (mapcar (lambda (entry)
                                   (let ((prop (car entry))
                                         (val  (cadr entry)))
                                     (list val prop)))
                                 my-alist)))
  (message "my-plist: %s\nmy-alist: %s\nmy-reverse-alist: %s"
           my-plist my-alist my-reverse-alist))
;; my-plist: (:a 1 :b 2 :c 3 :more (4 5 6))
;; my-alist: ((:a 1) (:b 2) (:c 3) (:more (4 5 6)))
;; my-reverse-alist: ((1 :a) (2 :b) (3 :c) ((4 5 6) :more))

Schauen Sie sich die seq-partitionDokumentation mit an C-h f seq-partition RET.

Tassilo Horn
quelle
1

Eine Idee ist die Verwendung -map-indexedvon dashund anwenden Transformation nur auf ungerade Werte der Liste:

(-non-nil (-map-indexed (lambda (index item) (when (oddp index) item))
  '(a x b y c z))) ; => (x y z)
(-non-nil (--map-indexed (when (oddp it-index) it) '(a x b y c z))) ; => (x y z)

Eine weitere Idee ist es , nur eine plist auf eine Hash-Tabelle zu konvertieren, indem ht<-plistaus ht:

(ht-values (ht<-plist '(a x b y c z) 'equal)) ; (z y x)

Beachten Sie, dass die Reihenfolge der Elemente in der Hash-Tabelle nicht erhalten bleibt.

Mirzhan Irkegulov
quelle