Почему моя функция ищет вход с тем же именем, а не работает с переменной, которую я передал? - PullRequest
1 голос
/ 02 марта 2012

test - это функция, которая проверяет, существует ли объект в глобальной среде, не является ли он пустым и относится ли к определенному классу.

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)
}

Теперь в foobar и дюжине других функций я хочу убедиться, что foo - это правильный тип объекта, который я хочу, прежде чем приступить к чему-либо еще.

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!
 >

Ответы [ 2 ]

3 голосов
/ 02 марта 2012

Объекты теряют свои первоначальные имена при передаче более одного раза.Копии получают новые локальные имена.Вам нужно получить имя при первом проходе, а затем протестировать с помощью 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! 
2 голосов
/ 02 марта 2012

Чтобы проверить, проблема в том, что substitute является вложенным.

Когда R смотрит на foobar(a), он запускает test(foo) в foobarфункция, и поэтому переменная, на которую смотрит функция test, называется foo.

Я начну с игрушечного примера, чтобы упростить объяснение.Функция library, как и ваша test функция, интерпретирует свой аргумент через имя переменной.то есть library(MASS) загружает библиотеку 'MASS', а не строку, которая содержится внутри переменной 'MASS'.

Теперь я создам функцию f, которая просто вызывает library - это отражает вашеfoobar функция:

f <- function(x) {
    library(x)
}

Теперь давайте попробуем:

> f(MASS)
Error in library(x) : there is no package called ‘x’

О нет!Это не сработало!Как так?Поскольку помните, в коде library, он подставляет переданную переменную. То есть library <- function(lib,...) substitute(lib).

Так что f(MASS) переходит к function(x) library(x),и, следовательно, я набрал library(x) прямо в командной строке - library только пытается загрузить x, а не * x 1040 * значение , MASS.

OKмы можем это исправить: нам просто нужно изменить library(x) на library(substitute(x)), так как substitute(x) равно MASS, и мы тогда получим library(MASS), верно?

f <- function(x) {
    library(substitute(x))
}

Давайте попробуем:

> f(MASS)
Error in library(substitute(x)) : 'package' must be of length 1

Ух, что случилось?В пределах f, substitute(x) - это , который не оценивается , потому что library целенаправленно не оценивает выражение, которое он получает, потому что затем набирает library(MASS) в командестрока не будет работать.

Поэтому мы действительно хотим сохранить substitute(x) в качестве переменной и затем выполнить library для этой переменной.

Единственная проблема в том, что даже если мы делаем y <- substitute(x); library(y) в f, мы всегда сталкиваемся с этой проблемой, что аргумент, введенный в library, никогда не оценивается .Выполнение этого приведет к тому же, что и первая ошибка: «нет пакета с именем y».

Как мы можем это исправить?Нам нужно как-то косвенно вызвать library с substitute(x) в качестве аргумента, где substitute(x) равно оценивается .

Ага!Мы можем использовать do.call!(примечание: я не придумал это самостоятельно, я руководствовался этим сообщением в списке рассылки R по вложенным заменителям :

f <- function(x) {
    do.call(library,list(substitute(x)))
}

Это ровно то, что мы хотим - он вызывает library, но передает substitute(x) в качестве библиотеки. Сначала оценивает substitute(x), поскольку мы прямо не написали library(substitute(x)).

> f(MASS) # no error!
# see if MASS is loaded - check if function 'lda' is there:
> exists('lda',mode='function') 
[1] TRUE # huzzah!

Решение для вашего дела

Итак, применяя этот урок к вашему вопросу, попробуйте:

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")
}

Давайте посмотрим:

> 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! (Кстати: спасибо, что задали этот вопрос, потому что ответ - это то, что я всегда хотел знать).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...