Reputation: 32996
test is a function to check if an object exists in the global environment, is not empty, and belongs to a particular class.
test <- function(foo, response=TRUE) {
if (missing(foo)) {
response <- FALSE
}
if (response) {
if (!exists(as.character(substitute(foo)), envir = .GlobalEnv)) {
response <- FALSE
}
}
if (response) {
response <- ifelse(class(foo) != "numeric", FALSE, TRUE)
}
return(response)
}
Now in foobar
and a dozen other functions, I want to make sure foo
is the right kind of object I want before proceeding with anything else.
foobar <- function(foo)
{
if(test(foo)) {
cat ("Yes, I have foo! \n")
}
if(!test(foo)) {
cat("Sorry, not a valid foo")
}
}
>ls()
[1] "foobar" "test"
>test(a)
[1] FALSE
>a <- "foobar"
>test(a)
[1] FALSE
>a <- 1
>test(a)
[1] TRUE
>foobar(a)
Sorry, not a valid foo
>
# what the???
>ls()
[1] "a" "foobar" "test"
>foo <- 1
>foobar(foo)
Yes, I have foo!
>
Upvotes: 1
Views: 124
Reputation: 263451
Objects loose their original names when handed off more than once. The copies get assigned new localnames. You need to grab the name on the first pass and then test with ls()
foobar <- function(foo)
{ fooname <- deparse(substitute(foo)); print(fooname)
if(test(fooname) ) {
cat ("Yes, I have foo! \n")
}
if(!test(fooname) ) {
cat("Sorry, not a valid foo")
}
}
test <- function(foo, response=TRUE) {
if (missing(foo)) {
response <- FALSE
}
if (response) {
if ( foo %in% ls( envir = .GlobalEnv) ) {
response <- TRUE }else {response <- FALSE}
}
return(response)
}
foobar(after)
# [1] "after"
#Yes, I have foo!
Upvotes: 3
Reputation: 56935
To verify, the problem is in the fact that the substitute
is nested.
When R looks at foobar(a)
, it runs test(foo)
within the foobar
function, and so the variable that the test
function looks at is called foo
.
I'll start with a toy example to make things easier to explain. The library
function, like your test
function, interprets its argument via the variable name. i.e. library(MASS)
loads the 'MASS' library, not the string that is contained inside the variable 'MASS'.
Now I'll make a function f
that just calls library
- this mirrors your foobar
function:
f <- function(x) {
library(x)
}
Now let's try:
> f(MASS)
Error in library(x) : there is no package called ‘x’
Oh no! It didn't work! How come? Because remember, within the library
code, it substitutes the variable passed in. i.e. library <- function(lib,...) substitute(lib)
.
So f(MASS)
goes to function(x) library(x)
, and hence it's like I typed library(x)
straight into the command line -- library
is only trying to load x
, and not x's value, MASS
.
OK, we can fix this: we just need to change library(x)
to library(substitute(x))
, since substitute(x)
is MASS
and we'll then end up with library(MASS)
, right?
f <- function(x) {
library(substitute(x))
}
Let's try:
> f(MASS)
Error in library(substitute(x)) : 'package' must be of length 1
Urgh, what happened? Within f
, the substitute(x)
is not being evaluated, because library
purposefully doesn't evaluate the expression it gets fed, because then typing library(MASS)
in the command line wouldn't work.
So we really want to save substitute(x)
as a variable and then perform library
on that variable.
The only problem is that even if we do y <- substitute(x); library(y)
within f
, we always run into this problem that the argument fed into library
is never evaluated. So doing this will cause the same as the first error: 'there is no package called y
'.
How can we fix this? We need to somehow indirectly call library
with substitute(x)
as the argument, where substitute(x)
is evaluated.
Aha! We can use do.call
! (note: I didn't come up with this on my own, I was guided by this post to the R mailing list on nested substitutes:
f <- function(x) {
do.call(library,list(substitute(x)))
}
This does exactly what we want - it calls library
but passes substitute(x)
in as the library. It evaluates substitute(x)
first, since we haven't directly written library(substitute(x))
. nifty right?
> f(MASS) # no error!
# see if MASS is loaded - check if function 'lda' is there:
> exists('lda',mode='function')
[1] TRUE # huzzah!
So, applying this lesson to your question, try:
foobar <- function(foo)
{
if ( do.call(test,list(substitute(foo))) ) # see the do.call & substitute?
cat ("Yes, I have foo! \n")
else
cat("Sorry, not a valid foo")
}
Let's see:
> ls()
[1] "foobar" "test"
> test(a)
[1] FALSE
> a <- 'foobar'
> test(a)
[1] FALSE
> a <- 1
> test(a)
[1] TRUE
> foobar(a)
Yes, I have foo!
Huzzah! (by the way: thanks for asking this question, because the answer is something I've always wanted to know).
Upvotes: 2