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
Related
If I understand, rlang::quo_is_missing evaluates a quosure and checks whether it contains a missing value. If it does, it should return TRUE, FALSE if not. Yet, I've tried the following combinations and it always returns FALSE:
rlang::quo_is_missing(quo(NA))
rlang::quo_is_missing(quo(NA_character_))
rlang::quo_is_missing(quo(NA_integer_))
If I try non-NA values, it also returns FALSE, as expected:
rlang::quo_is_missing(quo("hello"))
Why is it returning FALSE when the value is obviously missing?
"Missing" is a special term that refers to values that are not present at all. NA is not the same as "missing" -- NA is itself a value. In base R you can compare the functions is.na() and missing() each of which do different things. quo_is_missing is like the missing() function, not is.na and returns true only when there is no value at all:
rlang::quo_is_missing(quo())
If you want to check for NA, you could write a helper
quo_is_na <- function(x) {
!rlang::quo_is_symbolic(x) &&
!rlang::quo_is_missing(x) &&
!rlang::quo_is_null(x) &&
is.na(rlang::quo_get_expr(x))
}
quo_is_na(quo())
# [1] FALSE
quo_is_na(quo(x+y))
# [1] FALSE
quo_is_na(quo(NULL))
# [1] FALSE
quo_is_na(quo(42))
# [1] FALSE
quo_is_na(quo(NA))
# [1] TRUE
quo_is_na(quo(NA_character_))
# [1] TRUE
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
Assume we have a vector and want to check if a variable is part of that vector. This is going to be used in a function as a error handling and the input can also be NULL
values <- c("ALL", "NON")
parameter <- "ALL"
the easy solution is to use is.element() or %in% but the parameter can be NULL and in that case we get an error in if()
parameter <- NULL
is.element(parameter, values)
logical(0)
if(is.element(parameter, values)){stop("invalid parameter")}
Error in if (is.element(parameter, values)) { :
argument is of length zero
If you want to use %in% you can reverse the call and use any on top :
any(c("ALL", "NON") %in% NULL) # [1] FALSE
c("ALL", "NON") %in% NULL returns a logical of length 2 so we need anyto get the output.
Another possibility is to use purrr::has_element, which handles this case :
library(purrr)
has_element(c("ALL", "NON"), NULL) # [1] FALSE
It's also stricter :
has_element(1, "1") # [1] FALSE
is.elements(1, "1") # [1] TRUE
has_element(list(iris), list(as.list(iris))) # [1] FALSE
is.element(list(iris), list(as.list(iris))) # [1] TRUE
There are multiple ways to check. The exact function to check is is.null which returns TRUE if it is NULL
!is.null(parameter) && is.element(parameter, values)
Other option is length which returns 0 for NULL
length(parameter) > 0 && is.element(parameter, values)
NOTE: Both the methods evaluate the second argument only if the first condition is TRUE. So it will return FALSE whenever 'parameter' is NULL. We can evaluate this in the same argument order as in the OP's is.element as
1:3 %in% 2
#[1] FALSE TRUE FALSE
2 %in% 1:3
#[1] TRUE
returns different results.
NOTE: No packages used and here we assume that the OP pass vectors of same type
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
How can I get the formals (arguments) from a call object? formals() only seems to work with functions.
Well, a call does not have formals, only actual arguments... The difference being that a function like foo <- function(x, y, ..., z=42) can be called with actual arguments like foo(42, bar=13).
...But getting the arguments can be done like this:
a <- call('foo', a=42, 13)
as.list(a)[-1]
#$a
#[1] 42
#
#[[2]]
#[1] 13
...on the other hand, you can usually (not always) find the actual function and find the formals for it:
a <- quote(which(letters=='g'))
formals(match.fun(a[[1]]))
#$x
#
#$arr.ind
#[1] FALSE
#
#$useNames
#[1] TRUE
Here you'd need to add some error handling if the function can't be found (as with the call to foo above)...