R сила локальная сфера

r

7177 просмотра

11 ответа

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

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

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

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

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


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

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

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

Автор: Xodarap Источник Размещён: 22.10.2019 01:51

Ответы (11)


28 плюса

Насколько я знаю, 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
Автор: Tommy Размещён: 18.11.2011 04:59

12 плюса

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)
Автор: Ben Bolker Размещён: 02.06.2011 06:04

8 плюса

Использование 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
Автор: Michael Размещён: 18.01.2013 08:20

7 плюса

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

Автор: Spacedman Размещён: 02.06.2011 06:19

7 плюса

В modulesCRAN есть новый пакет, который решает эту распространенную проблему (см. Виньетку здесь ). При 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

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

Автор: Uwe Размещён: 27.04.2016 08:13

6 плюса

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

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

Edit Теперь, когда вы прояснили свой вопрос, посмотрите на 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> 
Автор: Dirk Eddelbuettel Размещён: 02.06.2011 04:10

4 плюса

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

a <- 1

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

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

f()

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

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

Автор: kohske Размещён: 02.06.2011 04:53

3 плюса

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

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

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

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

Автор: Andre Michaud Размещён: 02.06.2011 04:40

3 плюса

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

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

Автор: c-urchin Размещён: 17.11.2011 10:03

2 плюса

@Tommy дал очень хороший ответ, и я использовал его, чтобы создать 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))
}
Автор: Moody_Mudskipper Размещён: 16.05.2017 09:21

1 плюс

Для меня, основываясь на ответе @ c-urchin, мне нужно определить скрипт, который читает все мои функции, а затем исключает глобальную среду:

filenames <- Sys.glob('fun/*.R')
for (filename in filenames) {
    source(filename, local=T)
    funname <- sub('^fun/(.*).R$', "\\1", filename)
    eval(parse(text=paste('environment(',funname,') <- parent.env(globalenv())',sep='')))
}

Я предполагаю что

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

Уловка в том, что если одна из моих функций вызывает другую из моих функций, то внешняя функция должна сначала вызвать этот скрипт, и его необходимо вызывать с помощью local=T:

source('readfun.R', local=T)

при условии, конечно, что файл сценария называется readfun.R.

Автор: Daniel Sparing Размещён: 30.01.2013 07:51
Вопросы из категории :
32x32