R сила локальная сфера - PullRequest
36 голосов
/ 02 июня 2011

Возможно, это неправильная терминология, но, надеюсь, я смогу донести свою точку зрения.

Я часто заканчиваю тем, что делаю что-то вроде:

myVar = 1
f <- function(myvar) { return(myVar); }
# f(2) = 1 now

R с радостью использует переменную внеобласть действия функции, которая заставляет меня чесать голову, размышляя о том, как я мог бы получить результаты, которые у меня есть.

Есть ли какая-либо опция, которая говорит: «заставить меня использовать только переменные, которым ранее были присвоены значения в этой функции?объем"?Например, Perl use strict делает нечто подобное.Но я не знаю, что R имеет эквивалент my.


РЕДАКТИРОВАТЬ: Спасибо, я знаю, что я их прописными буквами по-разному.Действительно, пример был создан специально для иллюстрации этой проблемы!

Я хочу знать, есть ли способ, которым R может автоматически предупреждать меня, когда я это делаю.

РЕДАКТИРОВАТЬ 2: Кроме того, еслиRkward или другая IDE предлагает эту функциональность, я бы тоже хотел это знать.

Ответы [ 11 ]

26 голосов
/ 18 ноября 2011

Насколько я знаю, R не обеспечивает режим "использования строгого режима". Таким образом, у вас есть два варианта:

1 - убедитесь, что все ваши "строгие" функции не имеют globalenv в качестве окружения. Для этого вы можете определить красивую функцию-оболочку, но самое простое - вызвать local:

# Use "local" directly to control the function environment
f <- local( function(myvar) { return(myVar); }, as.environment(2))
f(3) # Error in f(3) : object 'myVar' not found

# Create a wrapper function "strict" to do it for you...
strict <- function(f, pos=2) eval(substitute(f), as.environment(pos))
f <- strict( function(myvar) { return(myVar); } )
f(3) # Error in f(3) : object 'myVar' not found

2 - Выполните анализ кода, который предупреждает вас о «плохом» использовании.

Вот функция checkStrict, которая, надеюсь, делает то, что вы хотите. Он использует превосходный пакет codetools.

# Checks a function for use of global variables
# Returns TRUE if ok, FALSE if globals were found.
checkStrict <- function(f, silent=FALSE) {
    vars <- codetools::findGlobals(f)
    found <- !vapply(vars, exists, logical(1), envir=as.environment(2))
    if (!silent && any(found)) {
        warning("global variables used: ", paste(names(found)[found], collapse=', '))
        return(invisible(FALSE))
    }

    !any(found)
}

И попробовал:

> myVar = 1
> f <- function(myvar) { return(myVar); }
> checkStrict(f)
Warning message:
In checkStrict(f) : global variables used: myVar
12 голосов
/ 02 июня 2011

checkUsage в пакете codetools полезен, но не поможет вам в этом.В чистом сеансе, где myVar не определено,

f <- function(myvar) { return(myVar); }
codetools::checkUsage(f)

дает

<anonymous>: no visible binding for global variable ‘myVar’

, но после определения myVar, checkUsage будет счастлив.* См. ?codetools в пакете codetools: возможно, есть что-то полезное:

> findGlobals(f)
[1] "{"      "myVar"  "return"
> findLocals(f)
character(0)
8 голосов
/ 19 января 2013

Использование get(x, inherits=FALSE) вызовет локальную область.

 myVar = 1

 f2 <- function(myvar) get("myVar", inherits=FALSE)


f3 <- function(myvar){
 myVar <- myvar
 get("myVar", inherits=FALSE)
}

Вывод:

> f2(8)    
Error in get("myVar", inherits = FALSE) : object 'myVar' not found
> f3(8)
[1] 8
7 голосов
/ 27 апреля 2016

В CRAN есть новый пакет modules, который решает эту распространенную проблему (см. Виньетка здесь ). При modules функция выдает ошибку, вместо того чтобы молча возвращать неправильный результат.

# without modules
myVar <- 1
f <- function(myvar) { return(myVar) }
f(2)
[1] 1

# with modules
library(modules)
m <- module({
  f <- function(myvar) { return(myVar) }
})
m$f(2)
Error in m$f(2) : object 'myVar' not found

Я впервые использую его. Это кажется простым, поэтому я могу включить его в свой обычный рабочий процесс, чтобы избежать трудоемких неудач.

7 голосов
/ 02 июня 2011

Вы, конечно, делаете это неправильно.Не ждите, что инструменты проверки статического кода найдут все ваши ошибки.Проверьте свой код с помощью тестов.И еще тесты.Любой достойный тест, написанный для запуска в чистой среде, обнаружит такую ​​ошибку.Напишите тесты для ваших функций и используйте их.Посмотрите на славу пакета testthat на CRAN.

6 голосов
/ 02 июня 2011

Вам нужно исправить опечатку: myvar! = myVar.Тогда все это будет работать ...

Разрешение области «изнутри», начиная с текущего, затем ограждающие и т. Д.

Редактировать Сейчасчтобы вы прояснили свой вопрос, посмотрите на codetools пакета (который является частью R Base set):

R> library(codetools)
R> f <- function(myVAR) { return(myvar) }
R> checkUsage(f)
<anonymous>: no visible binding for global variable 'myvar'
R> 
4 голосов
/ 02 июня 2011

вы можете динамически изменять дерево среды следующим образом:

a <- 1

f <- function(){
    b <- 1
    print(b)
    print(a)
}

environment(f) <- new.env(parent = baseenv())

f()

Внутри f, b можно найти, в то время как a не может.

Но, вероятно, это будетприносить больше вреда, чем пользы.

3 голосов
/ 18 ноября 2011

environment(fun) = parent.env(environment(fun))

удалит «рабочее пространство» из вашего пути поиска, оставит все остальное.Это, вероятно, ближе всего к тому, что вы хотите.

3 голосов
/ 02 июня 2011

Вы можете проверить, определена ли переменная локально:

myVar = 1
f <- function(myvar) { 
if( exists('myVar', environment(), inherits = FALSE) ) return( myVar) else cat("myVar was not found locally\n")
}

> f(2)
myVar was not found locally

Но я нахожу это очень искусственным, если единственное, что вы пытаетесь сделать, это защитить себя от орфографических ошибок.

Существующая функция ищет имя переменной в конкретной среде. наследует = ЛОЖЬ говорит ему не заглядывать в окружающие кадры.

2 голосов
/ 16 мая 2017

@ Томми дал очень хороший ответ, и я использовал его, чтобы создать 3 функции, которые, на мой взгляд, более удобны на практике.

строго

чтобы сделать функцию строгой, вам просто нужно вызвать

strict(f,x,y)

вместо

f(x,y)

пример:

my_fun1 <- function(a,b,c){a+b+c}
my_fun2 <- function(a,b,c){a+B+c}
B <- 1
my_fun1(1,2,3)        # 6
strict(my_fun1,1,2,3) # 6
my_fun2(1,2,3)        # 5
strict(my_fun2,1,2,3) # Error in (function (a, b, c)  : object 'B' not found

checkStrict1

Чтобы получить диагноз, выполните checkStrict1 (f) с необязательными логическими параметрами, чтобы показать больше или меньше.

checkStrict1("my_fun1") # nothing
checkStrict1("my_fun2") # my_fun2  : B

Более сложный случай:

A <- 1 # unambiguous variable defined OUTSIDE AND INSIDE my_fun3
# B unambiguous variable defined only INSIDE my_fun3
C <- 1 # defined OUTSIDE AND INSIDE with ambiguous name (C is also a base function)
D <- 1 # defined only OUTSIDE my_fun3 (D is also a base function)
E <- 1 # unambiguous variable defined only OUTSIDE my_fun3
# G unambiguous variable defined only INSIDE my_fun3
# H is undeclared and doesn't exist at all
# I is undeclared (though I is also base function)
# v defined only INSIDE (v is also a base function)
my_fun3 <- function(a,b,c){
  A<-1;B<-1;C<-1;G<-1
  a+b+A+B+C+D+E+G+H+I+v+ my_fun1(1,2,3)
}
checkStrict1("my_fun3",show_global_functions = TRUE ,show_ambiguous = TRUE , show_inexistent = TRUE)

# my_fun3  : E 
# my_fun3  Ambiguous : D 
# my_fun3  Inexistent : H 
# my_fun3  Global functions : my_fun1

Я выбрал отображение только несуществующих по умолчанию из 3 дополнительных дополнений. Вы можете легко изменить его в определении функции.

checkStrictAll

Получите диагностику всех ваших потенциально проблемных функций с теми же параметрами.

checkStrictAll()
my_fun2         : B 
my_fun3         : E 
my_fun3         Inexistent : H

источники

strict <- function(f1,...){
  function_text <- deparse(f1)
  function_text <- paste(function_text[1],function_text[2],paste(function_text[c(-1,-2,-length(function_text))],collapse=";"),"}",collapse="") 
  strict0 <- function(f1, pos=2) eval(substitute(f1), as.environment(pos))
  f1 <- eval(parse(text=paste0("strict0(",function_text,")")))
  do.call(f1,list(...))
}

checkStrict1 <- function(f_str,exceptions = NULL,n_char = nchar(f_str),show_global_functions = FALSE,show_ambiguous = FALSE, show_inexistent = TRUE){
  functions <-  c(lsf.str(envir=globalenv()))
  f <- try(eval(parse(text=f_str)),silent=TRUE)
  if(inherits(f, "try-error")) {return(NULL)}
  vars <- codetools::findGlobals(f)
  vars <- vars[!vars %in% exceptions]
  global_functions <- vars %in% functions

  in_global_env <- vapply(vars, exists, logical(1), envir=globalenv())
  in_local_env  <- vapply(vars, exists, logical(1), envir=as.environment(2))
  in_global_env_but_not_function <- rep(FALSE,length(vars))
  for (my_mode in c("logical", "integer", "double", "complex", "character", "raw","list", "NULL")){
    in_global_env_but_not_function <- in_global_env_but_not_function | vapply(vars, exists, logical(1), envir=globalenv(),mode = my_mode)
  }
  found     <- in_global_env_but_not_function & !in_local_env
  ambiguous <- in_global_env_but_not_function & in_local_env
  inexistent <- (!in_local_env) & (!in_global_env)
  if(typeof(f)=="closure"){
    if(any(found))           {cat(paste(f_str,paste(rep(" ",n_char-nchar(f_str)),collapse=""),":",                  paste(names(found)[found], collapse=', '),"\n"))}
    if(show_ambiguous        & any(ambiguous))       {cat(paste(f_str,paste(rep(" ",n_char-nchar(f_str)),collapse=""),"Ambiguous :",        paste(names(found)[ambiguous], collapse=', '),"\n"))}
    if(show_inexistent       & any(inexistent))      {cat(paste(f_str,paste(rep(" ",n_char-nchar(f_str)),collapse=""),"Inexistent :",       paste(names(found)[inexistent], collapse=', '),"\n"))}
    if(show_global_functions & any(global_functions)){cat(paste(f_str,paste(rep(" ",n_char-nchar(f_str)),collapse=""),"Global functions :", paste(names(found)[global_functions], collapse=', '),"\n"))}
    return(invisible(FALSE)) 
  } else {return(invisible(TRUE))}
}

checkStrictAll <-  function(exceptions = NULL,show_global_functions = FALSE,show_ambiguous = FALSE, show_inexistent = TRUE){
  functions <-  c(lsf.str(envir=globalenv()))
  n_char <- max(nchar(functions))  
  invisible(sapply(functions,checkStrict1,exceptions,n_char = n_char,show_global_functions,show_ambiguous, show_inexistent))
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...