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)...
Related
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
Within a function, how can we reliably return an object that contains the function itself?
For example with:
functionBuilder <- function(wordToSay) {
function(otherWordToSay) {
print(wordToSay)
print(otherWordToSay)
get(as.character(match.call()[[1]]))
}
}
I can build a function like so:
functionToRun <- functionBuilder("hello nested world")
... and run it ...
functionToRun("A")
#[1] "hello nested world"
#[1] "A"
#
#function(otherWordToSay) {
# print(wordToSay)
# print(otherWordToSay)
# get(as.character(match.call()[[1]]))
# }
#<environment: 0x1e313678>
... as you can see functionToRun returns itself. However, this approach appears to break if I call functionToRun via sapply:
> sapply(LETTERS, functionToRun)
#[1] "hello nested world"
#[1] "A"
#Error in get(as.character(match.call()[[1]])) : object 'FUN' not found
I can see that this is because the actual call when using sapply is FUN but that FUN doesn't exist at pos = -1 (the default for get). Code that works in that position looks like:
get(as.character(match.call()[[1]]),envir = sys.frame(sys.parent()))
But that same code fails if the function hasn't been called via sapply because sys.frame(sys.parent())) goes too far back and ends up referring to R_GlobalEnv.
From the documentation (R 3.2.2) I'd have expected dynGet to perhaps solve the issue of only going as far back in the stack as needed. Although this works for an sapply call of the function, it fails when the function is called on its own. (Besides, it is marked as 'somewhat experimental'). Inversely getAnywhere seems promising, but doesn't seem to work for the sapply called function.
Is there a reliable way to return the function that is currently being processed, i.e. works for both a bare and sapply wrapped function call?
What I'm doing right now is wrapping the attempt to grab the function in a tryCatch; but I'm a little uncertain whether I can trust that get(as.character(match.call()[[1]]),envir = sys.frame(sys.parent())) will work in all wrapping cases (not just sapply). So, I'm looking for a more reasonable way to approach this problem.
Potentially Related Questions:
How to access a variable stored in a function in R
How to get the name of the calling function inside the called routine?
I can't guarantee that this will work in all cases, but it looks okay:
fun <- function(x) {
print(x)
y <- exp(x)
print(y)
sys.function(0)
}
fun(1)
# [1] 1
# [1] 2.718282
# function(x) {
# print(x)
# y <- exp(x)
# print(y)
# sys.function(0)
# }
lapply(1:5, fun)[[3]]
# [1] 1
# [1] 2.718282
# [1] 2
# [1] 7.389056
# [1] 3
# [1] 20.08554
# [1] 4
# [1] 54.59815
# [1] 5
# [1] 148.4132
# function(x) {
# print(x)
# y <- exp(x)
# print(y)
# sys.function(0)
# }
Of course, I don't understand what you need this for.
Could someone explain the following result :
R> lapply(1:2, function(v) substitute(v))
[[1]]
1:2[[2L]]
[[2]]
1:2[[2L]]
Given the definition of lapply, I thought that this should have returned a list with elements substitute(1) and substitute(2). It is as if substitute(v) was not evaluated in the anonymous function scope, but in the "parent" one.
What I find even more strange is that if I add eval to substitute, I get the result I was waiting for :
R> lapply(1:2, function(v) eval(substitute(v)))
[[1]]
[1] 1
[[2]]
[1] 2
I know these questions are a bit tricky, but there is clearly something I don't get here.
I am using the bit64 package in some R code. I have created a vector
of 64 bit integers and then tried to use sapply to iterate over these
integers in a vector. Here is an example:
v = c(as.integer64(1), as.integer64(2), as.integer64(3))
sapply(v, function(x){is.integer64(x)})
sapply(v, function(x){print(x)})
Both the is.integer64(x) and print(x) give the incorrect
(or at least) unexpected answers (FALSE and incorrect float values).
I can circumvent this by directly indexing the vector c but I have
two questions:
Why the type conversion? Is their some rule R uses in such a scenario?
Any way one can avoid this type conversion?
TIA.
Here is the code of lapply:
function (X, FUN, ...)
{
FUN <- match.fun(FUN)
if (!is.vector(X) || is.object(X))
X <- as.list(X)
.Internal(lapply(X, FUN))
}
Now check this:
!is.vector(v)
#TRUE
as.list(v)
#[[1]]
#[1] 4.940656e-324
#
#[[2]]
#[1] 9.881313e-324
#
#[[3]]
#[1] 1.482197e-323
From help("as.list"):
Attributes may be dropped unless the argument already is a list or
expression.
So, either you creaste a list from the beginning or you add the class attributes:
v_list <- lapply(as.list(v), function(x) {
class(x) <- "integer64"
x
})
sapply(v_list, function(x){is.integer64(x)})
#[1] TRUE TRUE TRUE
The package authours should consider writing a method for as.list. Might be worth a feature request ...
Why doesn't this work? or is just the way R works?
Thanks
JJ
a <- c(1,2,3)
b <- 5
lapply(a, function(x) print(x)) # works
lapply(a, function(x,b) print(b)) # doesn't work.
I get --
Error in FUN(c(1, 2, 3)[[1L]], ...) :
argument "b" is missing, with no default
lapply only passes one argument on, because it's only designed to have one argument vary. If you just want to pass extra arguments along, put them as additional options to lapply:
lapply(a, function(x,y) print(y), y=b)
[1] 5
[1] 5
[1] 5
[[1]]
[1] 5
[[2]]
[1] 5
[[3]]
[1] 5
From the lapply help file:
... optional arguments to FUN.
If you want more than one varying argument to be passed to your function, look at mapply.
You could try putting a and b together in a list as follows:
lapply(list(a, b), function(x) print(b))
or specifying an argumant to pass b to as in:
lapply(a, function(x, y=b) print(y))
But I'm not really sure what you're after.