Existe-t-il un moyen général de «développer» une liste à utiliser comme arguments individuels pour une autre fonction?

9

Par exemple, disons que j'ai une liste de chaînes L, peut-être à partir d'un &restargument. Que puis-je faire pour Lque cela ait le même effet que le suivant?

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

(Je sais que mapconcatcela fonctionnerait ici pour cet exemple, mais je cherche un processus général.)

Sean Allred
la source

Réponses:

10
(apply #'concat '("foo" "bar" "baz"))
wasamasa
la source
1
J'ai juste pensé à ça! Lien psychique.
Sean Allred
6

Ce que vous vouliez faire ressemble à un pliage ou à un dépliage d'une séquence d'objets du même type. Il est tentant de l'utiliser applyà cette fin, car dans de nombreux cas, cela fonctionnera en effet. Mais ce n'est pas exactement le bon outil pour cela, et voici pourquoi:

  1. applyest un mécanisme de méta-programmation, non seulement cela, il est aussi trop général pour la tâche car il peut gérer des séquences d'objets de différents types, n'appelant pas nécessairement des fonctions de deux arguments. Par conséquent, vous obtiendrez parfois un comportement incorrect, par exemple:

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

    Mais intuitivement, vous vous attendriez à une incompatibilité de type ici.

  2. Il n'y a aucun moyen de s'assurer applyqu'il sera en mesure de traiter autant d'arguments que vous pouvez lui donner, c'est généralement une limite imposée par l'implémentation du langage.

  3. La fonction appelée par applypourra obtenir une référence de la liste des arguments qui lui est passée de cette manière. Cela n'est pas non plus évident et peut conduire à des erreurs plus tard:

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

    Si la liste d'arguments est copiée, vous payez le prix de consommer plus de mémoire que nécessaire, mais si elle n'est pas copiée (passée telle quelle), vous risquez de gâcher la liste, si la fonction appelée la modifie.


Donc, la meilleure façon de le faire est d'utiliser cl-reduce. L'avantage est qu'il a été spécialement conçu pour effectuer ce type de tâches.

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