Wie verwende ich die Ellipsenfunktion von R, wenn ich eine eigene function schreibe?

Die R-Sprache hat eine nette function zum Definieren von functionen, die eine variable Anzahl von Argumenten annehmen können. Zum Beispiel nimmt die function data.frame eine beliebige Anzahl von Argumenten an und jedes Argument wird zu den Daten für eine Spalte in der resultierenden Datentabelle. Beispielverwendung:

 > data.frame(letters=c("a", "b", "c"), numbers=c(1,2,3), notes=c("do", "re", "mi")) letters numbers notes 1 a 1 do 2 b 2 re 3 c 3 mi 

Die Signatur der function enthält eine Ellipse wie folgt:

 function (..., row.names = NULL, check.rows = FALSE, check.names = TRUE, stringsAsFactors = default.stringsAsFactors()) { [FUNCTION DEFINITION HERE] } 

Ich möchte eine function schreiben, die etwas Ähnliches tut, mehrere Werte nimmt und sie zu einem einzigen Rückgabewert konsolidiert (und auch andere Verarbeitungen durchführt). Um dies zu tun, muss ich herausfinden, wie man die ... aus den Argumenten der function innerhalb der function “entpackt”. Ich weiß nicht, wie ich das machen soll. Die relevante Zeile in der functionsdefinition von data.frame ist object <- as.list(substitute(list(...)))[-1L] , was ich nicht nachvollziehen kann.

Wie kann ich die Ellipse von der Signatur der function in beispielsweise eine Liste umwandeln?

Um genauer zu sein, wie kann ich get_list_from_ellipsis in den folgenden Code schreiben?

 my_ellipsis_function(...) { input_list <- get_list_from_ellipsis(...) output_list <- lapply(X=input_list, FUN=do_something_interesting) return(output_list) } my_ellipsis_function(a=1:10,b=11:20,c=21:30) 

Bearbeiten

Es scheint, dass es zwei Möglichkeiten gibt, dies zu tun. Sie sind as.list(substitute(list(...)))[-1L] und list(...) . Allerdings machen diese beiden nicht genau dasselbe. (Für Unterschiede, siehe Beispiele in den Antworten.) Kann mir jemand sagen, was der praktische Unterschied zwischen ihnen ist und welchen ich verwenden sollte?

   

    Ich lese Antworten und Kommentare und ich sehe, dass einige Dinge nicht erwähnt wurden:

    1. data.frame verwendet die list(...) . Fragment des Codes:

       object < - as.list(substitute(list(...)))[-1L] mrn <- is.null(row.names) x <- list(...) 

      object wird verwendet, um mit Spaltennamen etwas Magie zu machen, aber x wird verwendet, um final data.frame zu erstellen.
      Um das Argument unevaluated ... , sehen write.csv Code match.call an, in dem match.call verwendet wird.

    2. Wie Sie im Kommentar-Ergebnis in Dirk-Antwort schreiben, ist keine Liste von Listen. Ist eine Liste der Länge 4, welche Elemente sind Sprachtyp. Das erste Objekt ist eine symbol - list , das zweite ist der Ausdruck 1:10 und so weiter. Das erklärt, warum [-1L] benötigt wird: Es entfernt das erwartete symbol von den bereitgestellten Argumenten in ... (weil es immer eine Liste ist).
      Wie Dirk substitute sagt, "parse Baum den unausgewerteten Ausdruck".
      Wenn Sie my_ellipsis_function(a=1:10,b=11:20,c=21:30) aufrufen, "erstellt" ... eine Liste von Argumenten: list(a=1:10,b=11:20,c=21:30) und substitute es durch eine Liste von vier Elementen:

       List of 4 $ : symbol list $ a: language 1:10 $ b: language 11:20 $ c: language 21:30 

      Das erste Element hat keinen Namen und dies ist [[1]] in Dirk-Antwort. Ich erreiche dieses Ergebnis mit:

       my_ellipsis_function < - function(...) { input_list <- as.list(substitute(list(...))) str(input_list) NULL } my_ellipsis_function(a=1:10,b=11:20,c=21:30) 
    3. Wie oben können wir mit str überprüfen, welche Objekte in einer function sind.

       my_ellipsis_function < - function(...) { input_list <- list(...) output_list <- lapply(X=input_list, function(x) {str(x);summary(x)}) return(output_list) } my_ellipsis_function(a=1:10,b=11:20,c=21:30) int [1:10] 1 2 3 4 5 6 7 8 9 10 int [1:10] 11 12 13 14 15 16 17 18 19 20 int [1:10] 21 22 23 24 25 26 27 28 29 30 $a Min. 1st Qu. Median Mean 3rd Qu. Max. 1.00 3.25 5.50 5.50 7.75 10.00 $b Min. 1st Qu. Median Mean 3rd Qu. Max. 11.0 13.2 15.5 15.5 17.8 20.0 $c Min. 1st Qu. Median Mean 3rd Qu. Max. 21.0 23.2 25.5 25.5 27.8 30.0 

      Es ist in Ordnung. Lasst uns die substitute :

        my_ellipsis_function < - function(...) { input_list <- as.list(substitute(list(...))) output_list <- lapply(X=input_list, function(x) {str(x);summary(x)}) return(output_list) } my_ellipsis_function(a=1:10,b=11:20,c=21:30) symbol list language 1:10 language 11:20 language 21:30 [[1]] Length Class Mode 1 name name $a Length Class Mode 3 call call $b Length Class Mode 3 call call $c Length Class Mode 3 call call 

      Wir brauchen nicht. Sie benötigen zusätzliche Tricks, um mit solchen Objekten umzugehen (wie in write.csv ).

    Wenn Sie verwenden möchten ... dann sollten Sie es wie in Shane-Antwort verwenden, nach list(...) .

    Sie können die Auslassungspunkte in eine Liste mit list() konvertieren und dann Ihre Operationen darauf ausführen:

     > test.func < - function(...) { lapply(list(...), class) } > test.func(a="b", b=1) $a [1] "character" $b [1] "numeric" 

    get_list_from_ellipsis function get_list_from_ellipsis ist also nichts anderes als list .

    Ein gültiger Anwendungsfall hierfür ist, wenn Sie eine unbekannte Anzahl von Objekten zur Operation übergeben möchten (wie in Ihrem Beispiel von c() oder data.frame() ). Es ist jedoch keine gute Idee, das ... wenn Sie jeden Parameter im Voraus kennen, da dies der Argument-Zeichenkette eine gewisse Mehrdeutigkeit und weitere Komplikationen hinzufügt (und die functionssignatur für jeden anderen Benutzer unklar macht). Die Argumentliste ist eine wichtige Dokumentation für functionsbenutzer.

    Ansonsten ist es auch nützlich für Fälle, in denen Sie Parameter an eine Unterfunktion übergeben möchten, ohne sie in Ihren eigenen functionsargumenten anzuzeigen. Dies kann in der functionsdokumentation vermerkt werden.

    Um Shane und Dirks Antworten hinzuzufügen: Es ist interessant zu vergleichen

     get_list_from_ellipsis1 < - function(...) { list(...) } get_list_from_ellipsis1(a = 1:10, b = 2:20) # returns a list of integer vectors $a [1] 1 2 3 4 5 6 7 8 9 10 $b [1] 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 

    mit

     get_list_from_ellipsis2 < - function(...) { as.list(substitute(list(...)))[-1L] } get_list_from_ellipsis2(a = 1:10, b = 2:20) # returns a list of calls $a 1:10 $b 2:20 

    So wie es my_ellipsis_function , scheint jede Version für Ihre Zwecke in my_ellipsis_function geeignet zu my_ellipsis_function , obwohl die erste deutlich einfacher ist.

    Du hast schon die halbe Antwort gegeben. Erwägen

     R> my_ellipsis_function < - function(...) { + input_list <- as.list(substitute(list(...))) + } R> print(my_ellipsis_function(a=1:10, b=2:20)) [[1]] list $a 1:10 $b 11:20 R> 

    Also nahm das zwei Argumente a und b vom Anruf und wandelte es in eine Liste um. War das nicht das, wonach du gefragt hast?

    Dies funktioniert wie erwartet. Das Folgende ist eine interaktive Sitzung:

     > talk < - function(func, msg, ...){ + func(msg, ...); + } > talk(cat, c("this", "is", "a","message."), sep=":") this:is:a:message. > 

    Gleich, außer mit einem Standardargument:

     > talk < - function(func, msg=c("Hello","World!"), ...){ + func(msg, ...); + } > talk(cat,sep=":") Hello:World! > talk(cat,sep=",", fill=1) Hello, World! > 

    Wie Sie sehen, können Sie mit dieser Option ‘zusätzliche’ Argumente an eine function innerhalb Ihrer function übergeben, wenn die Standardeinstellungen in einem bestimmten Fall nicht Ihren Vorstellungen entsprechen.

    Ich denke das, was Sie erwartet haben (folgende Abbildung). a1, a2, a3, a4 sind einige beliebige Vektoren, und “catt” ist die function, die eine beliebige Anzahl von Argumenten akzeptiert und den verketteten Vektor der Eingabeargumente zurückgibt.

    Klicken Sie hier, um die Konsolenausgabe anzuzeigen