Lazy evaluation of supplied arguments - r

Say I have the following function:
foo <- function(x, y = min(m)) {
m <- 1:10
x + y
}
When I run foo(1), the returned value is 2, as expected. However, I cannot run foo(1, y = max(m)) and receive 11, since lazy evaluation only works for default arguments. How can I supply an argument but have it evaluate lazily?

The simple answer is that you can't and shouldn't try to. That breaks scope and could wreak havoc if it were allowed. There are a few options that you can think about the problem differently.
first pass y as a function
foo<-function(x,y=min){
m<-1:10
x+y(m)
}
if a simple function does not work you can move m to an argument with a default.
foo<-function(x,y=min(m),m=1:10){
x+y(m)
}
Since this is a toy example I would assume that this would be too trivial. If you insist on breaking scope then you can pass it as an expression that is evaluated explicitly.
foo<-function(x,y=expression(min(m))){
m<-1:10
x+eval(y)
}
Then there is the option of returning a function from another function. And that might work for you as well, depending on your purpose.
bar<-function(f)function(x,y=f(m)){
m<-1:10
x+y
}
foo.min<-bar(min)
foo.min(1) #2
foo.max<-bar(max)
foo.max(1) #10
But now we are starting to get into the ridiculous.

My solution was to just change the default argument:
R> formals(foo)$y <- call("max", as.name("m"))
R> foo(1)
[1] 11

You can use a substitute, eval combintation.
foo <- function(x, y = min(m)) {
y <- substitute(y)
m <- 1:10
x + eval(y)
}
foo(1)
## [1] 2
foo(1, y = max(m))
## [1] 11

Related

R: last argument in elipsis

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"

R - modify an unknown function

I have a function f of some vector x
The function in R is written as :
f <- function(x){#insert some function of x here}
I would like to return (-f), which denotes the negative of the function. In the case the function itself is known beforehand, this is a simple exercise.
However, in this case, I don't know what this function is
Could someone please help me with the R code to carry this out? (The output needs to be a function in the vector x.)
An example would be - f(x) = x + 1, then -f(x) = -x - 1
Thank you!
The following function getNegFn() takes a function fn and returns a function which returns the negative return value of fn:
getNegFn <- function(fn){
fnOut <- function(){
- do.call(what=fn, args=as.list(match.call())[-1])
}
formals(fnOut) <- formals(fn)
fnOut
}
An example:
fn <- function(x) x + 1
nFn <- getNegFn(fn=fn)
fn(1)
[1] 2
nFn(1)
[1] -2
Also works if the input function has ... arguments:
fn2 <- function(x, ...) x + sum(unlist(list(...)))
nFn2 <- getNegFn(fn=fn2)
fn2(x=1, y=2)
[1] 3
nFn2(x=1, y=2)
[1] -3
Look into do.call(). You can give it a string of a function's name as an arguement, and that function will then be called with the arguments you provide (in a list()), so, for example, you can do:
funcname<-"mean"
dat<-1:5
do.call(funcname, list(dat))*(-1)
Would run as if you called mean(dat)*(-1), which will give you:
[1] -3

Using the same argument names for a function defined inside another function

Why does
f <- function(a) {
g <- function(a=a) {
return(a + 2)
}
return(g())
}
f(3) # Error in a + 2: 'a' is missing
cause an error? It has something to do with the a=a argument, particularly with the fact that the variable names are the same. What exactly is going on?
Here are some similar pieces of code that work as expected:
f <- function(a) {
g <- function(a) {
return(a + 2)
}
return(g(a))
}
f(3) # 5
f <- function(a) {
g <- function(g_a=a) {
return(g_a + 2)
}
return(g())
}
f(3) # 5
g <- function(a) a + 2
f <- function(a) g(a)
f(3) # 5
The problem is that, as explained in the R language definition:
The default arguments to a function are evaluated in the evaluation frame of the function.
In your first code block, when you call g() without any arguments, it falls back on its default value of a, which is a. Evaluating that in the "frame of the function" (i.e. the environment created by the call to g()), it finds an argument whose name matches the symbol a, and its value is a. When it looks for the value of that a, it finds an argument whose name matches that symbol, and whose value is a. When...
As you can see, you're stuck in a loop, which is what the error message is trying to tell you:
Error in g() :
promise already under evaluation: recursive default argument reference or
earlier problems?
Your second attempt, which calls g(a) works as you expected, because you've supplied an argument, and, as explained in the same section of R-lang:
The supplied arguments to a function are evaluated in the evaluation frame of the calling function.
There it finds a symbol a, which is bound to whatever value you passed in to the outer function's formal argument a, and all is well.
The problem is the a=a part. An argument can't be its own default. That is a circular reference.
This example may help clarify how it works:
x <- 1
f <- function(a = x) { x <- 2; a }
f()
## [1] 2
Note that a does not have the default 1; it has the default 2. It looks first in the function itself for the default. In a similar way a=a would cause a to be its own default which is circular.

Setting conditional(ifelse) arguments in a function

I have a simple function and one of its arguments needs to be made conditional, in the sense:
IFELSE NOT MISSING take the given value
IFELSE NOT EXISTS then give some default value
ELSE give the global value of that argument
something like this:
f <- function(x,y=ifelse(!missing("y"),y,ifelse(!exists("y"),1,get("y",envir=.GlobalEnv))))
{
assign("y",y,envir=.GlobalEnv)
return(x+y)
}
required outputs:
# :f(3) should give me 4 with global y=1
# :f(4,2) should give me 6 with global y=2
# :f(5) should give me 7 with global y=2
#Note that y does not exist when we first run the function
running the above function with f(3) gives me:- Error in get("y", envir = .GlobalEnv) : object 'y' not found
If i specify y in the first-go then the function works perfectly, but I want it to run even if the user doesnt specify the y argument in the first-go
Any suggestions?
Thanks.
EDIT:
#Richard i dont think it matters if missing() is in the function or in the argument, in any case try to do the same using this function:
f <- function(x,y)
{
y=ifelse(!missing("y"),y,ifelse(!exists("y"),1,get("y",envir=.GlobalEnv)))
assign("y",y,envir=.GlobalEnv)
return(x+y)
}
First, I'll begin by stating that I don't recommend you do this. You'll be messing around with reassignment of global objects, which can be dangerous and lead to trouble if you need to go back and recall a value that you've overwritten.
Next, missing should not be used in the argument list of a function. It should be used in the function body to check the arguments in the argument list, and has specific usage that is noted in the help file.
That said, if you must do it, here you go.
f <- function(x, y)
{
if(missing(y)) {
y <- if(exists("y", envir = .GlobalEnv)) {
get("y", envir = .GlobalEnv)
} else {
1L
}
}
assign("y", y, .GlobalEnv)
x + y
}
> rm(y)
> f(3)
#[1] 4
> f(4, 2)
#[1] 6
> f(5)
#[1] 7
Try:
f <- function(x,y=NULL)
{
y = ifelse( !is.null(y), y,
ifelse(!exists("y"), 1,get("y",envir=.GlobalEnv)
)
)
assign("y",y,envir=.GlobalEnv)
return(x+y)
}
If y does not exist, try:
f <- function(x,y)
{
if(missing("y") || !exists('y')) y = 1
assign("y",y,envir=.GlobalEnv)
x+y
}

Anonymous passing of variables from current environment to subfunction calls

The function testfun1, defined below, does what I want it to do. (For the reasoning of all this, see the background info below the code example.) The question I wanted to ask you is why what I tried in testfun2 doesn't work. To me, both appear to be doing the exact same thing. As shown by the print in testfun2, the evaluation of the helper function inside testfun2 takes place in the correct environment, but the variables from the main function environment get magically passed to the helper function in testfun1, but not in testfun2. Does anyone of you know why?
helpfun <- function(){
x <- x^2 + y^2
}
testfun1 <- function(x,y){
xy <- x*y
environment(helpfun) <- sys.frame(sys.nframe())
x <- eval(as.call(c(as.symbol("helpfun"))))
return(list(x=x,xy=xy))
}
testfun1(x = 2,y = 1:3)
## works as intended
eval.here <- function(fun){
environment(fun) <- parent.frame()
print(environment(fun))
eval(as.call(c(as.symbol(fun))))
}
testfun2 <- function(x,y){
print(sys.frame(sys.nframe()))
xy <- x*y
x <- eval.here("helpfun")
return(list(x=x,xy=xy))
}
testfun2(x = 2,y = 1:3)
## helpfun can't find variable 'x' despite having the same environment as in testfun1...
Background info: I have a large R code in which I want to call helperfunctions inside my main function. They alter variables of the main function environment. The purpose of all this is mainly to unclutter my code. (Main function code is currently over 2000 lines, with many calls to various helperfunctions which themselves are 40-150 lines long...)
Note that the number of arguments to my helper functions is very high, so that the traditional explicit passing of function arguments ( "helpfun(arg1 = arg1, arg2 = arg2, ... , arg50 = arg50)") would be cumbersome and doesnt yield the uncluttering of the code that I am aiming for. Therefore, I need to pass the variables from the parent frame to the helper functions anonymously.
Use this instead:
eval.here <- function(fun){
fun <- get(fun)
environment(fun) <- parent.frame()
print(environment(fun))
fun()
}
Result:
> testfun2(x = 2,y = 1:3)
<environment: 0x0000000013da47a8>
<environment: 0x0000000013da47a8>
$x
[1] 5 8 13
$xy
[1] 2 4 6

Resources