简体   繁体   English

为什么`missing`和默认参数在`lapply`调用的函数中不起作用?

[英]Why `missing` and default arguments are not working in functions called by `lapply`?

I'm astonished that missing seems not working in a function called by lapply . 我很惊讶missing似乎不适用于lapply调用的lapply Assume I have the following functions: 假设我有以下功能:

.add <- function(x, arg, ...) {
  if (missing(arg)) {
    arg <- 1
  }
  print(match.call())
  return(x + arg)
}

wrapper <- function(l, arg, ...) {
  return(lapply(l, .add, arg=arg, ...))
}

Setting arg explicit works like excepted: arg显式设置为例外:

wrapper(list(x=1:10, y=1:10), arg=1)
#FUN(x = X[[1L]], arg = ..1)
#FUN(x = X[[2L]], arg = ..1)
#$x
# [1]  2  3  4  5  6  7  8  9 10 11
#
#$y
# [1]  2  3  4  5  6  7  8  9 10 11

Without arg I would expect the same output but it fails: 没有arg我会期望相同的输出,但它失败了:

wrapper(list(x=1:10, y=1:10))
#FUN(x = X[[1L]], arg = ..1)
# Error in FUN(X[[1L]], ...) : argument "arg" is missing, with no default

missing works in nested wrapper functions where no lapply is used. missing在嵌套包装函数中工作,其中没有使用lapply Why it seems to have no effect in functions called by lapply ? 为什么它似乎对lapply调用的函数没有影响?

EDIT : Default arguments also don't work: 编辑 :默认参数也不起作用:

.add <- function(x, arg=5, ...) {
  if (missing(arg)) {
    arg <- 1
  }
  print(match.call())
  return(x + arg)
}

wrapper(list(x=1:10, y=1:10))
#FUN(x = X[[1L]], arg = ..1)
# Error in FUN(X[[1L]], ...) : argument "arg" is missing, with no default

It seems that arg is neither missing nor accessible . 似乎arg既不缺少也不可访问 What happens here? 这里发生了什么?

(I know that I could circumvent this by setting arg=NULL in wrapper and if (is.null(arg)) in .add or something else. .add is an internal function which determines arg by its own based on the input (eg arg=mean(x) ) and I want arg in the wrapper to document the argument arg for the user and to allow the user to overwrite the default behavior. And most important: I want to understand why this is not working!) (我知道我可以通过在wrapper设置arg=NULL并在.add或其他地方设置if (is.null(arg))来绕过这个.add是一个内部函数,它根据输入确定arg (例如arg=mean(x)我想argwrapper记录参数arg的用户,并允许用户覆盖默认行为,最重要的:!我想知道这是为什么不工作)

EDIT2 : Finally this behaviour is fixed. EDIT2 :最后这个行为是固定的。 It was a bug in R < 3.2.0, see PR#15707 . 这是一个R <3.2.0的错误,见PR#15707

First, I'll mention that I believe the idiomatic way of doing this is by constructing a call and then evaluating it. 首先,我要提到的是,我认为这样做的惯用方法是构建一个调用然后对其进行评估。 See write.csv for an example. 有关write.csv ,请参见write.csv I believe this code will do what you want, using that method. 我相信这段代码会使用该方法做你想做的事。

wrapper <- function(X, arg, ...) {
  force(X) # optional; if X is missing, the error message will be more informative
  Call <- match.call(expand.dots=TRUE)
  Call[[1L]] <- as.name("lapply")
  Call$FUN <- as.name(".add")
  eval.parent(Call)
}

Ok, now here's an attempt to explain the issues you discovered. 好的,现在这是试图解释你发现的问题。 I stand ready to be corrected as well, but hopefully this will at least help clarify the issues, just like @idfah's answer did. 我也随时准备好纠正,但希望这至少有助于澄清问题,就像@ idfah的回答一样。

First, I'll tackle the "defaults" issue, as I think it's more straightforward. 首先,我将解决“默认”问题,因为我认为它更直接。 This one I think can be made simpler, as in the following two functions, where the second ( f2 ) simply calls the first ( f1 ). 我认为这个可以更简单,如下面的两个函数,其中第二个( f2 )只调用第一个( f1 )。 What we see is that the default argument in f1 gets overridden by the promise to x in f2 , and when that promise is evaluated, it is missing. 我们看到的是f1中的默认参数被f2 x的promise所覆盖,并且当评估该promise时,它将丢失。 Moral of this story (I think); 这个故事的道德(我认为); defaults must be set again in your calling function, if that variable is included in the call. 如果调用中包含该变量,则必须在调用函数中再次设置默认值。

f1 <- function(x=1) {print(match.call()); x}
f2 <- function(x) {f1(x=x)}
f1()
## f1()
## [1] 1
f2()
## f1(x = x)
## Error in f1(x = x) : argument "x" is missing, with no default

Now on to the missing in lapply issue. 现在lapply问题中的缺失。 Here I basically have sgibb's code, but have added a message about whether or not arg is considered missing. 在这里我基本上有sgibb的代码,但是添加了一个关于arg是否被认为缺失的消息。 We have what seems to be a curious contradiction; 我们有一个好奇的矛盾; the message tells us that arg is NOT missing, but when the function tries to access it, we get an error message telling us that arg IS missing. 该消息告诉我们arg不会丢失,但是当函数尝试访问它时,我们会收到一条错误消息,告诉我们缺少arg

.add <- function(x, arg) {
  print(match.call())
  if(missing(arg)) {
    message("arg is missing in .add")
    x
  } else {
    message("arg is not missing")
    x + arg
  }
}
wrapper <- function(l, arg) {lapply(l, .add, arg=arg)}
wrapper(1)
## FUN(x = 1[[1L]], arg = ..1)
## arg is not missing
## Error in FUN(1[[1L]], ...) : argument "arg" is missing, with no default

What I think is happening is the lapply is putting the promise to arg in ..1 , so it doesn't look missing, but when it tries to evaluate it, it finds that it is missing. 我认为正在发生的是lapply承诺arg in ..1 ,所以它看起来并不缺少,但当它试图评估它时,它发现它缺失了。 Moral of this story (I think); 这个故事的道德(我认为); don't try to propagate missings through lapply . 不要尝试通过lapply传播lapply

UPDATE: More precisely, it's something with how dot expansion works. 更新:更准确地说,它是点扩展如何工作的东西。 Consider this version of lapply (which doesn't actually work on a list, but otherwise has the same code style); 考虑这个版本的lapply (它实际上不在列表上工作,但在其他方面具有相同的代码样式); this shows we get the same behavior. 这表明我们得到了相同的行为。

apply3 <- function(X, FUN, ...) { 
  print(match.call())
  FUN(X, ...)
}
wrapper3 <- function(l, arg) {apply3(l, .add, arg=arg)}
wrapper3(1)
## apply3(X = l, FUN = .add, arg = arg)
## FUN(x = X, arg = ..1)
## arg is not missing
## Error in FUN(X, ...) : argument "arg" is missing, with no default

But when we substitute the dots with a variable name, it works as expected. 但是当我们用变量名替换点时,它按预期工作。

apply4 <- function(X, FUN, hm) { 
  print(match.call())
  FUN(X, hm)
}
wrapper4 <- function(l, arg) {apply4(l, .add, hm=arg)}
wrapper4(1)
## apply4(X = l, FUN = .add, hm = arg)
## FUN(x = X, arg = hm)
## arg is missing in .add
## [1] 1

And one more example; 还有一个例子; if I use dots, but do the expansion myself, by calling ..1 directly, it also works! 如果我使用点,但自己做扩展,直接调用..1 ,它也有效! This is curious as the matched call is the same as the version that doesn't work. 这很奇怪,因为匹配的调用与不起作用的版本相同。

apply3b <- function(X, FUN, ...) { 
  print(match.call())
  FUN(X, ..1)
}
wrapper3b <- function(l, arg) {apply3b(l, .add, arg=arg)}
wrapper3b(1)
## apply3b(X = l, FUN = .add, arg = arg)
## FUN(x = X, arg = ..1)
## arg is missing in .add
## [1] 1

There is no missing in your wrapper, so it bombs there. 你的包装中没有missing ,所以它在那里炸弹。 In this case, you don't need it since you are using variadic arguments anyway. 在这种情况下,您不需要它,因为您无论如何都在使用可变参数。 Try this: 试试这个:

.add <- function(x, arg, ...) {
    if (missing(arg)) 
      arg <- 1
    print(match.call())
  return(x + arg)
}

wrapper <- function(l, ...) 
  return(lapply(l, .add, ...))

If the wrapper needs to know arg , then you need an missing there: 如果包装器需要知道arg ,那么你需要在那里missing

.add <- function(x, arg, ...) {
  print(match.call())
  return(x + arg)
}

wrapper <- function(l, ...) {
  if (missing(arg)) 
    arg <- 1
  return(lapply(l, .add, arg=arg, ...))
}

I stand corrected 我纠正了

The following example allows the missing to be at the bottom of the call stack, presumably because of lazy evaluation. 以下示例允许missing位于调用堆栈的底部,可能是因为惰性求值。 I am unsure then why your example does not work... curious. 我不确定为什么你的例子不起作用......好奇。

wrapper.c <- function(l, arg)
{
  if (missing(arg))
    arg <- 1
  print("I'm in c")
  arg
}

wrapper.b <- function(l, arg)
{
  print("I'm in b")
  wrapper.c(l, arg)
}

wrapper.a <- function(l, arg)
  wrapper.b(l, arg)

> wrapper.a(1)
[1] "I'm in b"
[1] "I'm in c"
[1] 1

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM