Why are the `missing` and default arguments not working in functions called` lapply`?

I am surprised that missing does not seem to work in a function called lapply . Suppose 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 explicitly works as excepted:

 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:

 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 shell nested functions where lapply is not used. Why does this not affect functions called lapply ?

EDIT : The default arguments also do not 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 not disappearing and not accessible. What's going on here?

(I know that I could get around this by setting arg=NULL in wrapper and if (is.null(arg)) in .add or something else. .add is an internal function that determines arg itself based on (e.g. , arg=mean(x) ), and I want arg in the wrapper document the arg argument for the user and allow the user to overwrite the default behavior. And most importantly: I want to understand why this does not work!)

EDIT2 . Finally, this behavior is fixed. This was a bug in R <3.2.0, see PR # 15707 .

+6
source share
2 answers

First, I mentioned that I believe that the idiomatic way to do this is to build a challenge and then evaluate it. See the write.csv example. I believe that this code will do what you want using this 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 is an attempt to explain the problems you discovered. I'm ready to fix too, but hopefully this will at least help clarify the issues, as @idfah did.

First, Iโ€™ll deal with the problem of โ€œdefaults,โ€ because I find it easier. I think this can be done easier, as in the following two functions, where the second ( f2 ) just calls the first ( f1 ). We see that the default argument in f1 overridden by the promise x in f2 , and when this promise is evaluated, it is missing. The moral of this story (I think); the default values โ€‹โ€‹must be set again in your calling function, if this 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 about the flaw in lapply . Here I basically have sgibb code, but you added a message about whether the arg argument is considered absent. We have what seems like a curious contradiction; the message says that arg NOT missing, but when the function tries to access it, we get an error message telling us that arg IS is missing.

 .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 

I think lapply puts the arg promise in ..1 , so it doesn't look absent, but when it tries to evaluate it, it discovers that it is absent. The moral of this story (I think); do not try to pass passes through lapply .

UPDATE: More precisely, this is what the dot scan extension works with. Consider this version of lapply (which actually doesn't work on the list, but otherwise has the same code style); this shows that we are getting 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 the 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 the dots, but I will do the extension myself by calling directly ..1 , it also works! This is curious, since the consistent call is the same as the version that is not working.

 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 
+4
source

There is no missing in your wrapper, so it is bombing there. In this case, you do not need it, since you use variable arguments anyway. Try the following:

 .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 , you'll need 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 correct

The following example allows missing be at the bottom of the call stack, presumably due to lazy grades. I'm not sure why your example is not working ... 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 
+1
source

Source: https://habr.com/ru/post/951354/


All Articles