Come usare la funzione di ellissi di R quando si scrive la propria funzione?

Il linguaggio R ha una bella funzionalità per la definizione di funzioni che possono assumere un numero variabile di argomenti. Ad esempio, la funzione data.frame accetta un numero qualsiasi di argomenti e ogni argomento diventa i dati di una colonna nella tabella di dati risultante. Esempio di utilizzo:

 > 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 

La firma della funzione include un’ellissi, in questo modo:

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

Mi piacerebbe scrivere una funzione che faccia qualcosa di simile, prendendo più valori e consolidandoli in un unico valore di ritorno (oltre a fare qualche altra elaborazione). Per fare questo, ho bisogno di capire come “scompattare” il ... dagli argomenti della funzione all’interno della funzione. Non so come farlo. La riga pertinente nella definizione della funzione di data.frame è object <- as.list(substitute(list(...)))[-1L] , che non riesco a capire.

Quindi, come posso convertire i puntini di sospensione dalla firma della funzione in, ad esempio, un elenco?

Per essere più specifici, come posso scrivere get_list_from_ellipsis nel codice qui sotto?

     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) 

    modificare

    Sembra che ci siano due modi possibili per farlo. Sono as.list(substitute(list(...)))[-1L] e list(...) . Tuttavia, questi due non fanno esattamente la stessa cosa. (Per le differenze, vedi esempi nelle risposte.) Qualcuno può dirmi qual è la differenza pratica tra loro e quale dovrei usare?

    Leggo risposte e commenti e vedo che poche cose non sono state menzionate:

    1. data.frame usa la list(...) versione. Frammento del codice:

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

      object è usato per fare magie con i nomi delle colonne, ma x è usato per creare data.frame finale.
      Per l'uso di argomenti non valutati ... guarda il codice match.call dove match.call è usato.

    2. Mentre scrivi nel commento, risultato in risposta Dirk non è un elenco di elenchi. È un elenco di lunghezza 4, quali elementi sono il tipo di language . Il primo object è un symbol - list , il secondo è espressione 1:10 e così via. Questo spiega perché è necessario [-1L] : rimuove il symbol previsto dagli argomenti forniti in ... (perché è sempre un elenco).
      Come afferma Dirk, i resi substitute "descrivono l'espressione non valutata".
      Quando chiami my_ellipsis_function(a=1:10,b=11:20,c=21:30) allora ... "crea" un elenco di argomenti: list(a=1:10,b=11:20,c=21:30) e substitute con una lista di quattro elementi:

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

      Il primo elemento non ha un nome e questo è [[1]] nella risposta Dirk. Raggiungo questi risultati usando:

       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. Come sopra possiamo usare str per verificare quali oggetti sono in una funzione.

       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 

      Va bene. Vediamo la versione 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 

      Non è quello di cui avevamo bisogno. Avrai bisogno di trucchi aggiuntivi per gestire questo tipo di oggetti (come in write.csv ).

    Se vuoi usare ... allora dovresti usarlo come in Shane answer, by list(...) .

    È ansible convertire i puntini di sospensione in una lista con list() e quindi eseguire le operazioni su di essa:

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

    Quindi la tua funzione get_list_from_ellipsis non è altro che una list .

    Un caso d’uso valido per questo è nei casi in cui si desidera passare un numero sconosciuto di oggetti per l’operazione (come nell’esempio di c() o data.frame() ). Non è una buona idea usare il ... quando si conoscono tutti i parametri, tuttavia, poiché aggiunge un po ‘di ambiguità e un’ulteriore complicazione alla stringa argomento (e rende la firma della funzione poco chiara a qualsiasi altro utente). L’elenco degli argomenti è una parte importante della documentazione per gli utenti delle funzioni.

    In caso contrario, è utile anche nei casi in cui si desidera passare i parametri a una sottofunzione senza esporli tutti negli argomenti della propria funzione. Questo può essere notato nella documentazione della funzione.

    Solo per aggiungere le risposte di Shane e Dirk: è interessante confrontare

     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 

    con

     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 

    my_ellipsis_function attuale, entrambe le versioni appaiono adatte ai tuoi scopi in my_ellipsis_function , anche se la prima è chiaramente più semplice.

    Hai già dato la metà della risposta. Prendere in considerazione

     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> 

    Quindi questo ha preso due argomenti a e b dalla chiamata e convertito in una lista. Non era quello che hai chiesto?

    Funziona come previsto. La seguente è una sessione intertriggers:

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

    Lo stesso, tranne con un argomento predefinito:

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

    Come puoi vedere, puoi usare questo per passare argomenti ‘extra’ a una funzione all’interno della tua funzione se i valori predefiniti non sono quelli che vuoi in un caso particolare.

    Penso che questo ti stavi aspettando (seguendo la fig). a1, a2, a3, a4 sono alcuni vettori arbitrari, e ‘catt’ è la funzione che prende un numero qualsiasi di argomenti e restituisce il vettore concatenato degli argomenti di input.

    clicca qui per vedere l'output della console