Gibt es eine allgemeine Möglichkeit, eine Liste zu erweitern, um sie als einzelne Argumente für eine andere Funktion zu verwenden?

9

Angenommen, ich habe eine Liste von Zeichenfolgen L, möglicherweise aus einem &restArgument. Was kann ich tun L, um den gleichen Effekt wie das Folgende zu erzielen?

(concat (first L) (second L) ... (last L))

(Ich weiß, mapconcatdass hier für dieses Beispiel funktionieren würde , aber ich suche nach einem allgemeinen Prozess.)

Sean Allred
quelle

Antworten:

10
(apply #'concat '("foo" "bar" "baz"))
Wasamasa
quelle
1
Ich habe buchstäblich nur daran gedacht! Psychische Verbindung.
Sean Allred
6

Was Sie tun wollten, scheint das Falten oder Entfalten einer Folge von Objekten des gleichen Typs zu sein. Es ist verlockend, applyfür diesen Zweck zu verwenden, weil es in vielen Fällen tatsächlich funktionieren wird. Aber es ist nicht genau das richtige Werkzeug dafür, und hier ist der Grund:

  1. applyist ein Metaprogrammiermechanismus, nicht nur das, er ist auch zu allgemein für die Aufgabe, da er Sequenzen von Objekten unterschiedlichen Typs verarbeiten kann und nicht unbedingt Funktionen von zwei Argumenten aufruft. Infolgedessen wird manchmal ein falsches Verhalten angezeigt, zum Beispiel:

    (apply 'concat "baz" '("foo" "bar"))
     > "bazfoobar"
    

    Aber intuitiv würde man hier eine Typinkongruenz erwarten.

  2. Es gibt keine Möglichkeit sicherzustellen apply, dass so viele Argumente verarbeitet werden können, wie Sie angeben können. Dies ist normalerweise eine durch die Sprachimplementierung auferlegte Grenze.

  3. Die von aufgerufene Funktion applykann auf diese Weise eine Referenz der an sie übergebenen Argumentliste abrufen. Dies ist ebenfalls nicht offensichtlich und kann später zu Fehlern führen:

    (let ((test (list 1 2 3)))
      (cons 
       (apply (lambda (&rest x)
                (prog1 (cl-reduce '+ x) (setcar x 0)))
              test)
       test))
    ;; This behaviour is undefined.  Could end up both ways
    > (6 1 2 3)
    > (6 0 2 3)
    

    Wenn die Argumentliste kopiert wird, zahlen Sie den Preis dafür, mehr Speicher als erforderlich zu verbrauchen. Wenn sie jedoch nicht kopiert wird (wie sie ist übergeben), besteht die Gefahr, dass die Liste beschädigt wird, wenn die aufgerufene Funktion sie ändert.


Der bessere Weg, dies zu tun, ist die Verwendung cl-reduce. Der Vorteil ist, dass es speziell für diese Art von Aufgaben entwickelt wurde.

(cl-reduce 'concat '("foo" "bar" "baz"))
> "foobarbaz"
wvxvw
quelle