Function 有没有办法使用两个'';R中函数中的语句?

Function 有没有办法使用两个'';R中函数中的语句?,function,r,argument-passing,optional-arguments,Function,R,Argument Passing,Optional Arguments,我想编写一个同时调用plot()和legend()的函数,如果用户可以指定一些附加参数,然后将这些参数传递给plot()或legend(),这将是一个理想的选择。我知道我可以使用…实现这两个函数中的一个: foo.plot <- function(x,y,...) { plot(x,y,...) legend("bottomleft", "bar", pch=1) } foo.plot(1,1, xaxt = "n") 我在这里使用dput(),因为行plot.args

我想编写一个同时调用
plot()
legend()
的函数,如果用户可以指定一些附加参数,然后将这些参数传递给
plot()
legend()
,这将是一个理想的选择。我知道我可以使用
实现这两个函数中的一个:

foo.plot <- function(x,y,...) {
    plot(x,y,...)
    legend("bottomleft", "bar", pch=1)
}

foo.plot(1,1, xaxt = "n")

我在这里使用
dput()
,因为行
plot.args的一种方法是结合使用参数列表和
do.call
。这不是最漂亮的解决方案,但确实有效

foo.plot <- function(x,y,legend.args,...) {
    la <- list(
        x="bottomleft",
        legend="bar",
        pch=1
    )
    if (!missing(legend.args)) la <- c(la,legend.args)
    plot(x,y,...)
    do.call(legend,la)
}
foo.plot(1,1, xaxt = "n")    
foo.plot(1,1, xaxt = "n",legend.args=list(bg="yellow",title="legend"))

foo.plot这些事情变得棘手,如果不在函数中指定额外参数,就没有简单的解决方案。如果在
绘图
图例
调用中都有
..
,则在传入
图例
特定参数时,最终会收到警告。例如,使用:

foo.plot <- function(x,y,...) {
    plot(x,y,...)
    legend("bottomleft", "bar", pch = 1, ...)
}
有一些方法可以解决这个问题,请参阅
plot.default
及其局部函数,这些函数定义为围绕函数的包装器,例如
axis
box
等。在这些函数中,您可以使用
localPlot()
包装器、内联函数,并直接调用它,而不是
plot()

bar.plot <- function(x, y, pch = 1, ...) {
    localPlot <- function(..., legend, fill, border, angle, density,
                          xjust, yjust, x.intersp, y.intersp,
                          text.width, text.col, merge, trace, plot = TRUE, ncol,
                          horiz, title, inset, title.col, box.lwd,
                          box.lty, box.col, pt.bg, pt.cex, pt.lwd) plot(...)
    localPlot(x, y, pch = pch, ...)
    legend(x = "bottomleft", legend = "bar", pch = pch, ...)
}
如何处理图形参数(例如
bty
)将取决于您-
bty
将影响打印框类型和图例框类型。另外请注意,我对
'pch'
的处理方式有所不同,因为如果有人在
bar.plot()
调用中使用该参数,您将在图例/绘图中使用不同的字符,并且您将收到关于
'pch'
匹配两次的警告或错误

正如你所见,这开始变得相当棘手


Joris的回答提供了一个有趣的解决方案,我对它的评论让我想起了函数中的控件列表参数,如
lme()
。以下是我对Joris的答案的版本,它实现了这个控制列表的想法:

la.args <- function(x = "bottomleft", legend = "bar", pch = 1, ...)
    c(list(x = x, legend = legend, pch = pch), list(...))

foo.plot <- function(x,y, legend.args = la.args(), ...) {
    plot(x, y, ...)
    do.call(legend, legend.args)
}
在设置
la.args()
函数时,您可以随心所欲地完成设置-这里我只为设置的参数设置默认值,并连接其他参数。如果
la.args()
包含所有带有默认值的图例参数,则会更简单。

自动方式:

foo.plot <- function(x,y,...) {
    lnames <- names(formals(legend))
    pnames <- c(names(formals(plot.default)), names(par()))
    dots <- list(...)
    do.call('plot', c(list(x = x, y = x), dots[names(dots) %in% pnames]))
    do.call('legend', c("bottomleft", "bar", pch = 1, dots[names(dots) %in% lnames]))
}

foo.plot我们可以构建一个
形式化的
函数,使任何函数与dots兼容:

formalize <- function(f){
  # add ... to formals
  formals(f) <- c(formals(f), alist(...=))
  # release the ... in the local environment
  body(f)    <- substitute({x;y},list(x = quote(list2env(list(...))),y = body(f)))
  f
}

foo.plot <- function(x,y,...) {
  legend <- formalize(legend) # definition is changed locally
  plot(x,y,...)
  legend("bottomleft", "bar", pch = 1, ...)
}    

foo.plot(1,1, xaxt = "n", title = "legend")

现在它工作得很好

这让我想起了函数中的
control
参数,如
lme()
,它使用
control=lme.control()
等参数
lme.control()
包含默认值,用于填充合适参数的列表。我将在我的答案中添加一个例子,因为它太复杂了,无法发表评论。谢谢,这似乎是一个很好的方法。我会再等一段时间再接受,也许会有其他好的答案出现。但是,我已经愿意接受这一点。您可以将此方法与
modifyList
结合使用,以便能够将legend.args与默认的图例参数集合并。另外一种方法是匹配eg名称(formals(legend)),以分割每个函数要使用的参数。@Charles:事情是,一些参数将同时匹配形式(图例)和形式(绘图)。这就是加文所阐述的问题。一下子要消化的问题可不止这些;-)然而,非常感谢你们详尽的回答,它确实教会了我一些关于争论通过的东西。事实上,我是从lme和其他函数中得到这个想法的,这些函数部分地将列表作为某些特定的参数。感谢您演示如何绕过“由多个实际参数匹配”的问题。@Joris:谢谢。Ripley教授向我传授了
localFoo()
方法,我们在我参与的一个软件包中使用了该方法。这是一个不错的解决方案,但您需要的过滤远不止是pch。有许多参数对绘图和图例都有效。你如何处理它们?@Joris,如果一个参数对两个函数都有效,它将被传递给两个函数。我真的不明白你的意思。需要过滤pch以避免调用图例时出现重复。@VitoshKa:假设您只想在绘图中或图例中设置该参数。比如说pch。按原样使用函数是不可能的,您无法指定要为哪个函数设置参数。@Joris,我明白您的意思。在这种情况下,您使用legend.args的解决方案当然更合适。是的,这是一种很好的方法。然而,有一些小问题,我在我的问题中纠正了;i)
par()。这是无害的,因为您将要在其上绘图,ii)您对
'what'
参数的理解是错误的-它可以是字符或函数,iii)您不需要
list()
包装在
list(“左下角”,“条形”)
,因为所有内容都强制为一个公共类型--witness
c(“foo”,1:10,list(a=1:10,b=letters[1:3]))
。很好的Q和后续思维,但很好地清理了您的编辑以确保准确性。@Gavin请查看我的更新以获得对您的观点的回应。很好的更新,很好地指出了这样的极端情况。好极了!在函数名/变量名冲突上-您可以始终更具防御性,使用
graphics::plot
graphics::legend
来双重确保找到正确的函数。这是一个很酷的解决方案,因为它可以自动避免警告。
bar.plot <- function(x, y, pch = 1, ...) {
    localPlot <- function(..., legend, fill, border, angle, density,
                          xjust, yjust, x.intersp, y.intersp,
                          text.width, text.col, merge, trace, plot = TRUE, ncol,
                          horiz, title, inset, title.col, box.lwd,
                          box.lty, box.col, pt.bg, pt.cex, pt.lwd) plot(...)
    localPlot(x, y, pch = pch, ...)
    legend(x = "bottomleft", legend = "bar", pch = pch, ...)
}
bar.plot(1, 1, xjust = 0.5, title = "foobar", pch = 3)
la.args <- function(x = "bottomleft", legend = "bar", pch = 1, ...)
    c(list(x = x, legend = legend, pch = pch), list(...))

foo.plot <- function(x,y, legend.args = la.args(), ...) {
    plot(x, y, ...)
    do.call(legend, legend.args)
}
foo.plot(1,1, xaxt = "n", legend.args=la.args(bg = "yellow", title = "legend"))
foo.plot <- function(x,y,...) {
    lnames <- names(formals(legend))
    pnames <- c(names(formals(plot.default)), names(par()))
    dots <- list(...)
    do.call('plot', c(list(x = x, y = x), dots[names(dots) %in% pnames]))
    do.call('legend', c("bottomleft", "bar", pch = 1, dots[names(dots) %in% lnames]))
}
formalize <- function(f){
  # add ... to formals
  formals(f) <- c(formals(f), alist(...=))
  # release the ... in the local environment
  body(f)    <- substitute({x;y},list(x = quote(list2env(list(...))),y = body(f)))
  f
}

foo.plot <- function(x,y,...) {
  legend <- formalize(legend) # definition is changed locally
  plot(x,y,...)
  legend("bottomleft", "bar", pch = 1, ...)
}    

foo.plot(1,1, xaxt = "n", title = "legend")
casualize <- function(f){
  f_name <- as.character(substitute(f))
  f_ns   <- getNamespaceName(environment(f))
  body(f) <- substitute({
      # extract all args
      args <- as.list(match.call()[-1]);
      # relevant args only
      args <- args[intersect(names(formals()),names(args))]
      # call initial fun with relevant args
      do.call(getExportedValue(f_ns, f_name),args)})
  f
}

foo.plot <- function(x,y,...) {
  legend <- formalize(legend)
  plot   <- casualize(plot)
  plot(x,y,...)
  legend("bottomleft", "bar", pch = 1, ...)
}

foo.plot(1,1, xaxt = "n", title = "legend")