Determine if a Function Argument is a Function Call - r

I want to be able to determine if an argument to a function is a call to a function or not. Lets say I have two functions , f() and g():
f <- function() "foo"
g <- function(x){
???
}
I want the output to the calls as below:
g(f())
#> [1] TRUE
g("bar")
#> [1] FALSE
I can get this to work by quoting the function arguments:
f <- function() "foo"
g <- function(x) is.call(x)
g(quote(f()))
#> [1] TRUE
g(quote("bar"))
#> [1] FALSE
However this is sub-optimal as I don't want users of the function to have to do this. Any suggestions?

You can use substitute():
h <- function(x) is.call(substitute(x))
h(f())
# [1] TRUE

Related

How to delay the evaluation of function arguments in R?

I would like to delay the evaluation of a function argument in R. Example:
my_func <- function(FUN){print(FUN); print(FUN)}
my_func(runif(1))
#> [1] 0.2833882
#> [1] 0.2833882
Created on 2019-07-21 by the reprex package (v0.2.1)
This works as documented because runif(1) is only evaluated once and its results printed twice.
Instead, I don't want runif(1) to be evaluated until it is within each print() statement. This would generate two different random numbers.
In other words, I don't want FUN to "resolve" --- if that is the right word --- to runif(1) until we are within a print() statement.
You can also achieve this with substitute and eval:
my_func <- function(FUN) {
print(eval(substitute(FUN)))
print(eval(substitute(FUN)))
}
my_func(runif(1))
#> [1] 0.09973534
#> [1] 0.8096205
my_func(runif(1))
#> [1] 0.2231202
#> [1] 0.5386637
NB: For additional details, check out this chapter Non-standard evaluation of Advanced R
Here is one trick with match.call and eval
my_func <- function(FUN){
print(eval(match.call()[[2]]))
print(eval(match.call()[[2]]))
}
my_func(runif(1))
#[1] 0.7439711
#[1] 0.5011816
my_func(runif(1))
#[1] 0.7864152
#[1] 0.730453
provide and expression
f = function(EXPR){
print(EXPR)
eval(EXPR)
}
EXPR = expression(runif(1))
> f(EXPR)
expression(runif(1))
[1] 0.1761139
provide an string
f2 = function(STR){
print(STR)
eval(parse(text = STR))
}
STR = "runif(1)"
> f2(STR)
[1] "runif(1)"
[1] 0.7630865

Passing on missing quasiquotation arguments

I am trying to use the quasiquotation to pass a raw variable name to a function, that passes it on to another function. However, the argument is optional, so I need to test if the 1st function was not given the argument and passed on that missing argument to the 2nd function.
In these examples b refers to a variable in a data.frame.
Testing if a function was passed a raw variable expression or no argument, I do
foo <- function(a) {
print(is_missing(enexpr(a)))
}
foo()
# [1] TRUE
foo(b)
# [1] FALSE
Without the enexpr, the variable b will attempt to be evaluated - and when missing - Errors.
Next, I try to pass the missing argument to another function (bar) whom then will test for its presence:
foo2 <- function(a) {
print(is_missing(enexpr(a)))
bar(maybe_missing(a))
}
bar <- function(a) {
print(is_missing(enexpr(a)))
}
foo2()
# [1] TRUE
# [1] FALSE <-- "wrong" (but not unexpected)
foo2(b)
# [1] FALSE
# [1] FALSE
Question: How can I in bar test whether foo2 was passed an argument?
Running R 3.5.1 with rlang 0.3.0.1.
We could do a !! and an enexpr in foo2
foo2 <- function(a) {
print(is_missing(enexpr(a)))
bar(!!maybe_missing(enexpr(a)))
}
foo2()
#[1] TRUE
#[1] TRUE
foo2(b)
#[1] FALSE
#[1] FALSE

R-how to define a function with indefinite number of arguments in R

I'd like to create a function to accept indefinite number of arguments.
Is there a way to define a function in R?
Thanks.
you can use the dots like this for example
f <- function(...) {
arguments <- list(...)
print(arguments)
}
f(a=1, b=2)
## $a
## [1] 1
## $b
## [1] 2

Checking for existence of a variable

I am really struggling to understand the following behaviour of R. Let's say we want to define a function f, which is supposed to return whether its argument exists as a variable; but we want to pass the argument without quotes. So for example to check whether variable y exists, we would call f(y).
f <- function(x) {
xchar <- deparse(substitute(x))
exists(xchar)
}
So I start a brand new R session and define f, but no other variables. I then get
f(y)
# [1] FALSE
f(z)
# [1] FALSE
f(f)
# [1] TRUE
f(x)
# [1] TRUE
The first three calls (on y, z, f) give the expected result. But there is no variable named x
exists("x")
# [1] FALSE
EDIT I now realise that this is because of the use of substitute, which will create the variable x. But is there a way around this?
The object x does exist inside the function since it is the name of the parameter.
If you modify the function
f <- function(...) {
xchar <- deparse(substitute(...))
exists(xchar)
}
you can see the expected output:
f(x)
# FALSE
You may want to just search the global environment
f <- function(x) {
xchar <- deparse(substitute(x))
exists(xchar,where=globalenv())
}
in which case you get:
> f(y)
[1] FALSE
> f(f)
[1] TRUE
> f(x)
[1] FALSE
> f(z)
[1] FALSE
> f(mean)
[1] TRUE

Assigning list attributes in an environment

The title is the self-contained question. An example clarifies it: Consider
x=list(a=1, b="name")
f <- function(){
assign('y[["d"]]', FALSE, parent.frame() )
}
g <- function(y) {f(); print(y)}
g(x)
$a
[1] 1
$b
[1] "name"
whereas I would like to get
g(x)
$a
[1] 1
$b
[1] "name"
$d
[1] FALSE
A few remarks. I knew what is wrong in my original example, but am using it to make clear my objective. I want to avoid <<-, and want x to be changed in the parent frame.
I think my understanding of environments is primitive, and any references are appreciated.
The first argument to assign must be a variable name, not the character representation of an expression. Try replacing f with:
f <- function() with(parent.frame(), y$d <- FALSE)
Note that a, b and d are list components, not list attributes. If we wanted to add an attribute "d" to y in f's parent frame we would do this:
f <- function() with(parent.frame(), attr(y, "d") <- FALSE)
Also, note that depending on what you want to do it may (or may not) be better to have x be an environment or a proto object (from the proto package).
assign's first argument needs to be an object name. Your use of assign is basically the same as the counter-example at the end of the the assign help page. Observe:
> x=list(a=1, b="name")
> f <- function(){
+ assign('x["d"]', FALSE, parent.frame() )
+ }
> g <- function(y) {f(); print(`x["d"]`)}
> g(x)
[1] FALSE # a variable with the name `x["d"]` was created
This may be where you want to use "<<-" but it's generally considered suspect.
> f <- function(){
+ x$d <<- FALSE
+ }
> g <- function(y) {f(); print(y)}
> g(x)
$a
[1] 1
$b
[1] "name"
$d
[1] FALSE
A further thought, offered in the absence of any goal for this exercise and ignoring the term "attributes" which Gabor has pointed out has a specific meaning in R, but may not have been your goal. If all you want is the output to match your specs then this achieves that goal but take notice that no alteration of x in the global environment is occurring.
> f <- function(){
+ assign('y', c(x, d=FALSE), parent.frame() )
+ }
> g <- function(y) {f(); print(y)}
> g(x)
$a
[1] 1
$b
[1] "name"
$d
[1] FALSE
> x # `x` is unchanged
$a
[1] 1
$b
[1] "name"
The parent.frame for f is what might be called the "interior of g but the alteration does not propagate out to the global environment.

Resources