防止在unlist()或c()中使用单一类型;将参数传递给包装函数

时间:2011-01-07 10:46:25

标签: r

是否有一种简单的方法可以在保留原始类型的列表成分的同时展平列表?有没有办法以编程方式构建不同成分类型的扁平列表?...

例如,我想为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"]))?

- 编辑 -

Gavin Simpson在关于构建包装函数的讨论中回答了以下两个问题。让我总结一下标题问题的答案:

可以使用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 个答案:

答案 0 :(得分:3)

不,因为unlistc(当应用于不同类型的原子向量时)正在创建原子向量,根据定义它们必须是单一类型。 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"

那么你可以以编程方式提取你想要的参数以及你想要的东西。

我刚刚想到的其他可能有用的东西是你可以使用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 :(得分:1)

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()根据其参数的类型做不同的事情 - 如果你想要一个列表,它的一个参数必须是一个列表,否则它会对其所有参数进行必要的类型转换,以便它可以创建一个向量。