Запретить приведение к одному типу в unlist () или c (); передача аргументов в функции-оболочки - PullRequest
3 голосов
/ 07 января 2011

Есть ли простой способ сгладить список при сохранении исходных типов составных частей списка? .. Есть ли способ программно построить сглаженный список из разных типов составных частей? ..

Например,Я хочу создать простую оболочку для таких функций, как png(filename,width,height), которая будет принимать имя устройства, имя файла и список параметров.Наивный подход будет выглядеть примерно так:

my.wrapper <- function(dev,name,opts) { do.call(dev,c(filename=name,opts)) }

или подобный код с unlist(list(...)).Это не работает, потому что opts приводится к символу, и в результате вызывается, например, png(filename,width="500",height="500").

Если не существует простого способа создания таких разнородных списков, существует ли стандартный идиоматический способ соединенияаргументы в функции, не называя их явно (например, do.call(dev,list(filename=name,width=opts["width"]))?

- Правка -

Гэвин Симпсон ответил на оба вопроса ниже в своем обсуждении о построении функций-оболочек.Позвольте мне кратко изложить ответ на заглавный вопрос:

Можно составить список с помощью c(), если хотя бы один из аргументов c() является списком.Для остроумия:

> foo <- c("a","b"); bar <- 1:3
> c(foo,bar)
[1] "a" "b" "1" "2" "3"
> typeof(c(foo,bar)) 
[1] "character"  ## note that the result is not a list and that coercion occurred
> c(list(foo),list(bar)) ## try also c(foo,list(bar))
[[1]]     [1] "a" "b"
[[2]]     [1] 1 2 3
> typeof(c(foo,list(bar)))  
[1] "list"       ## now the result is a list, so no need to coerce to same type
> c(as.list(foo),as.list(bar)) ## this creates a flattened list, as desired
[[1]]     [1] "a"
[[2]]     [1] "b"
[[3]]     [1] 1
[[4]]     [1] 2
[[5]]     [1] 3

Ответы [ 2 ]

3 голосов
/ 07 января 2011

Нет, поскольку unlist и c (при применении к атомарным векторам разных типов) создают атомарный вектор и по определению они должны быть одного типа , Список в R является самым общим из векторов, и вы можете использовать его, фактически аргумент "args" для do.call запрашивает для списка, и вы предоставляете атомарный вектор (используя c()).

Зачем использовать do.call, когда все, что вы делаете, - это создание нового устройства? Если все, что вам нужно, это обертка для png, которая устанавливает некоторые значения по умолчанию, чтобы вам не приходилось печатать все это каждый раз, когда вы хотите использовать устройство png, то вы слишком сложны. Примерно так будет достаточно:

my.png <- function(name, height = 500, width = 500, ...) {
    png(filename = name, height = height, width = width, ...)
}

Если вам нужна более общая оболочка, то что-то вроде:

my.wrapper <- function(dev, name, ...) {
    dev(filename = name, ...)
}

должно хватить. Вы бы использовали это как:

my.wrapper(png, "my_png.png", height = 500, width = 200, pointsize = 12)

или

my.wrapper(pdf, "my_pdf.pdf", height = 8, width = 6, version = "1.4")

Если вы хотите работать с ..., вы можете, например ::

my.wrapper2 <- function(dev, name, ...) {
    dotargs <- list(...)
    writeLines("my.wrapper2, called with the following extra arguments:")
    print(dotargs)
    ## do nothing now...
}

Что дает:

> my.wrapper2(pdf, "foo.pdf", height = 10, width = 5, pointsize = 8, 
+             version = "1.3")
my.wrapper2, called with the following extra arguments:
$height
[1] 10

$width
[1] 5

$pointsize
[1] 8

$version
[1] "1.3"

Так что вы можете программно выдвигать аргументы, которые вы хотите, и o, что вы хотите с ними.

Еще одна вещь, которая только что пришла мне в голову, - это то, что вы можете использовать c для объединения дополнительных компонентов в списки:

> c(filename = "name", list(height = 500, width = 500))
$filename
[1] "name"

$height
[1] 500

$width
[1] 500

Так что, если вы действительно хотите применить do.call к набору аргументов, тогда

my.wrapper3 <- function(dev, name, ...) {
   dotargs <- list(...)
   ## concatenate with name
   callArgs <- c(filename = name, dotargs)
   ## use do.call
   do.call(dev, args = callArgs)
}

Но это может быть более легко достигнуто с my.wrapper выше.

1 голос
/ 07 января 2011
dev <- "png"
filename <- "foo.png"
width <- 480
height <- 480
opts <- c(width=width, height=height)

my.wrapper <- function(dev,name,opts) {
  name <- list(filename=name)
  do.call(dev, c(name, opts)) 
}
my.wrapper(dev, filename, opts) 

Обратите внимание, что это будет работать, только если opts является именованным вектором, так как do.call нужен список с именованными записями.

Я думаю, что причина, по которой вы должны добавить этот дополнительный шаг, заключается в том, что c() делает разные вещи в зависимости от типов его аргумента - если вы хотите список, один из аргументов должен быть спискомв противном случае он выполняет необходимое преобразование типов для всех своих аргументов, чтобы создать вектор.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...