Naively, I thought that R's ... notation would collect all named arguments that appear after it so that they can be passed along. For example:
> a <- function(..., arg=TRUE) b(...)
> b <- function(arg=TRUE) print(arg)
> a(arg=FALSE)
[1] TRUE # I would want this to be FALSE
Since this clearly doesn't happen, is there some way to package up all of the arguments supplied to a function so that they get sent along?
You will need to pass arg:
a <- function(..., arg=TRUE) b(..., arg = arg)
Related
I am trying to understand the behaviour of user-defined functions like the below (based on the first answer to this question), which returns the arguments supplied to it as a named list:
function(a, b, ...) {
argg <- c(as.list(environment()), list(...))
print(argg)
}
Essentially, functions like the above produce unexpected behaviour when one of the argument names is also the name of a primitive function whose only parameter is ...
Below are some reproducible examples.
Example 1 - function behaves as expected, missing argument does not cause error
#define function as above
fun1 <- function(a, b, ...) {
argg <- c(as.list(environment()), list(...))
print(argg)
}
#run function
fun1(a = 1)
#returns the below. note that $b has the missing argument and this does not cause an error
#$a
#[1] 1
#$b
Example 2 - function returns error if 'c' is one of the explicit parameters and missing
#define function as above but with new explicit argument, called 'c'
#note that c() is a primitive function whose only parameter is ...
fun2 <- function(a, b, c, ...) {
argg <- c(as.list(environment()), list(...))
print(argg)
}
#run function
fun2(a = 1)
#returns error:
#Error in c(as.list(environment()), list(...)) :
# argument "c" is missing, with no default
Example 3 - replace 'c' with 'switch', a primitive function with parameters other than ...
#define function same way as fun2, but change 'c' parameter to 'switch'
#note that switch() is a primitive function that has parameters other than ...
fun3 <- function(a, b, switch, ...) {
argg <- c(as.list(environment()), list(...))
print(argg)
}
#run function
fun3(a = 1)
#returns the below. note that $b and $switch have the missing argument and this does not cause an error
#$a
#[1] 1
#$b
#$switch
I have tried numerous variations of the above that seem pointless to print here given that the basic pattern should be clear and thus easily reproducible without specific passages of code; suffice to say that as far as I have been able to tell, it appears that the function returns an error if one of its arguments a.) has the same name as a primitive function whose only parameter is ... and b.) is also missing. No other changes that I tested (such as removing the ... from the user-defined function's parameters; altering the order in which the arguments are specified when calling the function or when defining the function; changing the names and quantity of other arguments specified when calling the function or defining the function, etc.) had an impact on whether the behaviour was as expected.
Another point to note is that I don't see an error if I define a function with the same parameters as fun2, and with the c argument still missing, if I am not trying to access the function's arguments inside it. For example:
#define function with same parameters but different content to fun2
fun4 <- function(a, b, c, ...) {
return(a+b)
}
#run function
fun4(a = 1, b = 2)
#returns
#[1] 3
Please could somebody explain why I see this pattern of behaviour and the reason for the key role apparently played by primitive functions that only have ... as a parameter.
Please do not submit answers or comments suggesting 'workarounds' or querying the practical significance of the issue at hand. I am not asking my question in order to address a specific practical problem and there is no reason I can think of why I would ever be forced to use the name of a primitive function as a parameter; rather, I want to understand why the errors occur when they do in order to gain a clearer understanding of how functions in general, and the processes used to access their parameters in particular, work in R.
It's not the ... that's causing the problem. When you call c(), R looks for the function definition in the environment. Outside of a function it will normally find this as base::c. But within your function it first looks for the definition in the argument c in the function call, which it then can't find. This way of calling shows that it can work by telling R specifically where to find the definition of c:
fun4 <- function(a, b, c, ...) {
argg <- base::c(as.list(environment()), list(...))
print(argg)
}
#run function
fun4(a = 1)
#> $a
#> [1] 1
#>
#> $b
#>
#>
#> $c
Environments - from Advanced R
To demonstrate where things are being called you can use this tip from Advanced R by Hadley Wickham to see where R is finding each object. In the function where c isn't an argument, it finds it in base, otherwise it "finds" it in the function environment (where a and b are also defined):
library(rlang)
where <- function(name, env = caller_env()) {
if (identical(env, empty_env())) {
stop("Can't find ", name, call. = FALSE)
} else if (env_has(env, name)) {
env
} else {
where(name, env_parent(env))
}
}
fun5 <- function(a, b, ...) {
print(where("a"))
print(where("b"))
print(where("c"))
}
#run function
fun5(a = 1)
#> <environment: 0x000000001de35890>
#> <environment: 0x000000001de35890>
#> <environment: base>
fun6 <- function(a, b, c, ...) {
print(where("a"))
print(where("b"))
print(where("c"))
}
#run function
fun6(a = 1)
#> <environment: 0x000000001e1381f0>
#> <environment: 0x000000001e1381f0>
#> <environment: 0x000000001e1381f0>
Created on 2021-12-15 by the reprex package (v2.0.1)
I have a function f that takes three arguments, and returns the last one. For example:
f <- function(x,y,z){
return(z)
}
f(11,22,33) #yields 33
However, later on I may have more/less than three arguments, so I want to create a function g that returns the last argument of ...
g <- function(...){
#return final argument in '...'
}
g(11,22) #should yield 22
g(11,22,33,44,'abc') #should yield 'abc'
Is there any simple way to do this?
I've looked at existing posts on using ..., but they all seem to use it to pass all the arguments to another function (which is not what I'm trying to do).
I could just make the argument into a vector, and return the last element, but I'd like to avoid that if possible.
Use ...length and ...elt like this:
f <- function(...) ...elt(...length())
f(11, 12, 13)
## [1] 13
g <- function(...) {
dots <- list(...)
if (length(dots)) dots[[length(dots)]]
}
g(11,22)
# [1] 22
g(11,22,33,44,'abc')
# [1] "abc"
g() # returns nothing, NULL invisibly
I often choose to assign to dots or similar in the function and then deal with it, though that can be both good and bad. If you want to pass the args on to other functions, then you can still use ... as before (or you can use do.call(..), though that's not your question). One side-effect is that by doing this, the ... are evaluated, which though generally fine, in some corner-cases this evaluation may be too soon.
A demonstration:
g(stop("quux"), "abc")
# Error in g(stop("quux"), "abc") : quux
If you want to avoid early evaluation of other args, you can use match.call:
g <- function(...){
cl <- match.call(expand.dots = TRUE)
if (length(cl) > 1) cl[[length(cl)]]
}
g() # nothing
g(1)
# [1] 1
g(stop("quux"), "abc")
# [1] "abc"
Is there a function in R, which treats evaluates variable and call it if it's class is function.
a <- 1
b <- function() 2
So I expect return from evaluation of a and do.call(b) automatically understanding that second one is a function and evaluating it.
Of course i can make a check with is.function() and call do.call and in other cases call eval, but I thought it might be already done.
> eval(a)
[1] 1
> do.call(a, args = list())
Error in do.call(a, args = list()) :
'what' must be a character string or a function
> eval(b)
function() 2
> do.call(b, args = list())
[1] 2
I do expect
a <- 1
b <- function() 2
> someFun(a)
1
> someFun(b)
2
It’s hard to prove a negative but I rather doubt that there’s a function which will evaluate non-function variables to their value, and function-variables to the result of making them into a call and executing the call — because these two are fundamentally different operations.
That said, as you realised it’s easy enough to do yourself. Even easier in fact: no do.call needed:
evaluate = function (obj)
if(is.function(obj)) obj() else obj
I'm writing some R code and I want to store a list of Function names and what they are for in a dataframe, and then query that dataframe to determine which function to call, but I can't figure out how to do this, or if it's even possible.
As a basic example, let's assume the function name is just stored as a string in a variable, how do I call the function based on the function name stored in that variable?
MyFunc <-function() {
# Do some stuff...
print("My Function has been called!!!")
return(object)
}
FuncName <- "MyFunc()"
Result <- FuncName
I need to make
Result <- FuncName
Work the same as
Result <- MyFunc()
Also, passing objects, or other variables, to the functions is not a concern for what I am doing here, so those () will always be empty. I realize passing variables like this might get even more complicated.
You could use get() with an additional pair of ().
a<-function(){1+1}
var<-"a"
> get(var)()
[1] 2
To get a function from its name, try match.fun, used just like get in the answer provided by #Alex:
> a <- function(x) x+1
> f <- "a"
> match.fun(f)(1:3)
[1] 2 3 4
To call a function directly using its name, try do.call:
> params <- list(x=1:3)
> do.call(f, params)
[1] 2 3 4
The advantage to do.call is that the parameters passed to the function can change during execution (e.g., values of the parameter list can be dynamic at run time or passed by the user to a custom function), rather than being hardwired in the code.
Why do I suggest match.fun or do.call over get? Both match.fun and do.call will make sure that the character vector ("a" in the above examples) matches a function rather than another type of object (e.g., a numeric, a data.frame, ...).
Consider the following:
# works fine
> get("c")(1,2,3)
[1] 1 2 3
# create another object called "c"
> c <- 33
> get("c")
[1] 33
#uh oh!!
> get("c")(1,2,3)
Error: attempt to apply non-function
# match.fun will return a function; it ignores
# any object named "c" that is not a function.
> match.fun("c")(1,2,3)
[1] 1 2 3
# same with do.call
> do.call("c", list(1,2,3))
[1] 1 2 3
I am attempting to create a list of matrices containing iid Normal numbers. For the sake of a simple example, let the matrices be 4 by 2 and consider a list of length 3. The following code seemed like it should work (to me):
MyMatrix <- lapply(1:3, function() {matrix(rnorm(8), 4, 2)})
But it failed, with the following error:
Error in FUN(1:3[[1L]], ...) : unused argument (1:3[[1]])
On a whim, I tried:
MyMatrix <- lapply(1:3, function(x) {matrix(rnorm(8), 4, 2)})
And it worked! But why? x is not used anywhere in the function, and on experimentation, the behaviour of the expression is not affected by whether x already exists in the workspace or not. It appears to be entirely superfluous.
I am new to R, so I would be very grateful if an experienced user could explain what is going on here and why my first line fails.
You can't have a function that doesn't take arguments and then pass it arguments. Which is exactly what you are doing when you run lapply, as each value is passed in turn as the first argument to the function. E.g.
out <- lapply(1:3, function(x) x)
str(out)
#List of 3
# $ : int 1
# $ : int 2
# $ : int 3
Simple example throwing an error:
test <- function() {"woot"}
test()
#[1] "woot"
test(1)
#Error in test(1) : unused argument (1)
lapply(1:3, test)
#Error in FUN(1:3[[1L]], ...) : unused argument (1:3[[1]])
It's good form for R to error out, as it likely means you're expecting the function's returned result to change based on the arguments passed to the function. And it wouldn't. There are functions like this included in base R, like Sys.time(), which will fail if you try to pass it superfluous arguments which might otherwise make sense:
Sys.time()
#[1] "2014-07-07 13:22:11 EST"
Sys.time(tz="UTC")
#Error in Sys.time(tz = "UTC") : unused argument (tz = "UTC")