Reputation: 5771
I want to pass down function arguments to recursively call a function within itself (usually with a break condition, of course).
I learnt that match.call
should work to capture all arguments, and it works - until I wrap the original call in another function.
inner <- function(my_arg) {
message(my_arg)
do.call("inner", as.list(match.call()[-1]))
}
# this yields an error ... (unexpected)
outer <- function() {
mydata <- data.frame(1)
inner(mydata)
}
outer()
# ... while this yields an infinite loop (expected)
mydata <- data.frame(1)
inner(mydata)
This outputs:
1
Error in is.data.frame(my_arg) : object 'mydata' not found
Why is that? Is this intended? How can I fix this?
Upvotes: 0
Views: 342
Reputation: 132696
It's really difficult to explain the error because it results from the interaction of do.call
, match.call
and recursion. The problem results from when the promises of the nested calls inner(my_arg = mydata)
are forced. When message
is called, R searches the function scope and, in case the object is not found, the enclosing environments. This appears to fail when a promise in the nested calls hasn't been forced (due to your do.call("inner", as.list(match.call()[-1]))
construct).
> traceback()
5: message(my_arg) at #2
4: inner(my_arg = mydata)
3: do.call("inner", as.list(match.call()[-1])) at #4
2: inner(mydata) at #4
1: outer()
I suggest you study the language definition, e.g. Section 4.3.3.
Also, why do you need match.call
here? Just use inner(my_arg)
instead of that do.call
with match.call
construct. That immediately forces the promise and everything works fine.
Upvotes: 1
Reputation: 173793
This happens because of scoping. Hopefully this modification of your two functions will give a clear picture of what's going on (with no infinite loops!), and how to fix it.
inner <- function(my_arg)
{
mc <- match.call()
cat("Call to inner:\n")
print(mc)
cat("\nSymbol to be evaluated within \"inner\":\n")
print(as.list(mc)$my_arg)
cat("\nSymbol evaluated in scope of \"inner\":\n")
tryCatch(print(eval(as.list(mc)$my_arg)),
error = function(e) cat("**Error** - symbol not found\n"))
cat("\nSymbol evaluated in parent frame of \"inner\":\n")
tryCatch(print(eval(as.list(mc)$my_arg, envir = parent.frame())),
error = function(e) cat("**Error** - symbol not found\n"))
}
outer <- function()
{
my_data <- "outer test string"
inner(my_data)
}
Which we can test as follows:
inner("inner test string")
#> Call to inner:
#> inner(my_arg = "inner test string")
#>
#> Symbol to be evaluated within "inner":
#> [1] "inner test string"
#>
#> Symbol evaluated in scope of "inner":
#> [1] "inner test string"
#>
#> Symbol evaluated in parent frame of "inner":
#> [1] "inner test string"
outer()
#> Call to inner:
#> inner(my_arg = my_data)
#>
#> Symbol to be evaluated within "inner":
#> my_data
#>
#> Symbol evaluated in scope of "inner":
#> **Error** - symbol not found
#>
#> Symbol evaluated in parent frame of "inner":
#> [1] "outer test string"
Upvotes: 1