Domanda

Il linguaggio R ha una caratteristica elegante per definire le funzioni che possono assumere un numero variabile di argomenti. Ad esempio, la funzione data.frame assume un numero qualsiasi di argomenti e ogni argomento diventa i dati per una colonna nella tabella dei 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, come questa:

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

Vorrei scrivere una funzione che fa qualcosa di simile, prendendo più valori e consolidandoli in un singolo valore di ritorno (oltre a fare qualche altra elaborazione). Per fare ciò, devo capire come "disimballare" 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], di cui non posso avere alcun senso.

Quindi, come posso convertire le ellissi dalla firma della funzione in, ad esempio, un elenco?

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

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 possibili modi 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?

È stato utile?

Soluzione

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

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

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

    object è usato per fare un po 'di magia con i nomi delle colonne, ma x viene utilizzato per creare finale data.frame.
    Per l'uso non valutato ... argomento guarda write.csv Codice dove match.call viene usato.

  2. Mentre scrivi nel commento, il risultato in Dirk Risposta non è un elenco di elenchi. È un elenco di lunghezza 4, quali elementi sono language genere. Il primo oggetto è un symbol - list, il secondo è l'espressione 1:10 e così via. Che spiegano perché [-1L] è necessario: rimuove il previsto symbol da argomenti forniti in ... (Perché è sempre un elenco).
    Come afferma Dirk substitute ritorna "Analsimi l'espressione non valutata".
    Quando chiami my_ellipsis_function(a=1:10,b=11:20,c=21:30) poi ... "crea" un elenco di argomenti: list(a=1:10,b=11:20,c=21:30) e substitute rendilo un elenco 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]] in Dirk Risposta. Ottengo questo risultato 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 controllare 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 substitute versione:

       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 ulteriori trucchi per affrontare questo tipo di oggetti (come in write.csv).

Se vuoi usare ... allora dovresti usarlo come nella risposta di Shane, da list(...).

Altri suggerimenti

Puoi convertire le ellipsis in un elenco con list(), quindi esegui le tue operazioni su di esso:

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

$b
[1] "numeric"

Quindi il tuo get_list_from_ellipsis La funzione non è altro che list.

Un caso d'uso valido per questo è nei casi in cui si desidera passare in un numero sconosciuto di oggetti per il funzionamento (come nel tuo esempio di c() o data.frame()). Non è una buona idea usare il ... Quando conosci ogni parametro in anticipo, tuttavia, in quanto aggiunge un po 'di ambiguità e ulteriori complicazioni alla stringa dell'argomento (e rende la firma della funzione poco chiara a qualsiasi altro utente). L'elenco degli argomenti è un importante pezzo di documentazione per gli utenti delle funzioni.

Altrimenti, è utile anche per i casi in cui si desidera passare attraverso i parametri a una sottofunzione senza esporli tutti negli argomenti della tua funzione. Questo può essere notato nella documentazione della funzione.

Solo per aggiungere alle 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

insieme a

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

Allo stato attuale, entrambe le versione appare adatta ai tuoi scopi in my_ellipsis_function, sebbene il primo sia chiaramente più semplice.

Hai già dato metà della risposta. Ritenere

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 impiegato due argomenti a e b dalla chiamata e lo ha convertito in un elenco. Non era quello che hai chiesto?

Funziona come previsto. Di seguito è una sessione interattiva:

> 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 usarlo per passare argomenti "extra" a una funzione all'interno della tua funzione se le impostazioni predefinite non sono ciò che desideri in un caso particolare.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top