Чем local () отличается от других подходов к замыканию в R? - PullRequest
16 голосов
/ 26 октября 2011

Вчера я узнал от Билла Венеблса, как local () может помочь в создании статических функций и переменных, например,

example <- local({
  hidden.x <- "You can't see me!"
  hidden.fn <- function(){
    cat("\"hidden.fn()\"")
  }
  function(){
    cat("You can see and call example()\n")
    cat("but you can't see hidden.x\n")
    cat("and you can't call ")
    hidden.fn()
    cat("\n")
  }
})

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

> ls()
[1] "example"
> example()
You can see and call example()
but you can't see hidden.x
and you can't call "hidden.fn()"
> hidden.x                 
Error: object 'hidden.x' not found
> hidden.fn()
Error: could not find function "hidden.fn"

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

Какие плюсы и минусы этих двух методов?

Ответы [ 2 ]

12 голосов
/ 26 октября 2011

Инкапсуляция

Преимущество этого стиля программирования состоит в том, что скрытые объекты, вероятно, не будут перезаписаны чем-то другим, поэтому вы можете быть более уверены, что они содержат то, что вы думаете.Они не будут использованы по ошибке, так как к ним нельзя легко получить доступ.В связанном посте в вопросе есть глобальная переменная count, к которой можно получить доступ и перезаписать откуда угодно, поэтому, если мы отлаживаем код и смотрим на count и видим его изменение, мы не можем быть уверены, какая частькода изменил его.Напротив, в примере кода вопроса у нас есть большая уверенность, что никакая другая часть кода не задействована.

Обратите внимание, что на самом деле мы можем получить доступ к скрытой функции, хотя это не так просто:

# run hidden.fn
environment(example)$hidden.fn()

Объектно-ориентированное программирование

Также обратите внимание, что это очень близко к объектно-ориентированному программированию, где example и hidden.fn являются методами, а hidden.x является свойством.Мы можем сделать это следующим образом:

library(proto)
p <- proto(x = "x", 
  fn = function(.) cat(' "fn()"\n '),
  example = function(.) .$fn()
)
p$example() # prints "fn()"

proto не скрывает x и fn, но получить доступ к ним по ошибке не так просто, так как вы должны использовать p$x и p$fn() для доступа к ним, который на самом деле не отличается от возможности писать e <- environment(example); e$hidden.fn()

РЕДАКТИРОВАТЬ:

Объектно-ориентированный подход добавляет возможность наследования, например, можно определить ребенкаp, который действует как p, за исключением того, что он переопределяет fn.

ch <- p$proto(fn = function(.) cat("Hello from ch\n")) # child
ch$example() # prints: Hello from ch
6 голосов
/ 26 октября 2011

local() может реализовать шаблон синглтона - например, пакет snow использует это для отслеживания одного экземпляра Rmpi, который может создать пользователь.

getMPIcluster <- NULL
setMPIcluster <- NULL
local({
    cl <- NULL
    getMPIcluster <<- function() cl
    setMPIcluster <<- function(new) cl <<- new
})

local() также может использоваться для управления памятью в сценарии, например, для выделения больших промежуточных объектов, необходимых для создания конечного объекта в последней строке предложения. Большие промежуточные объекты доступны для сборки мусора, когда local возвращает.

Использование функции для создания закрытия - это фабричный шаблон - пример банковский счет в документации Введение в R, где каждый раз, когда вызывается open.account, создается новая учетная запись.

Как упоминает @otsaw, запоминание может быть реализовано с использованием локального, например, для кэширования веб-сайтов в сканере

library(XML)
crawler <- local({
    seen <- new.env(parent=emptyenv())
    .do_crawl <- function(url, base, pattern) {
        if (!exists(url, seen)) {
            message(url)
            xml <- htmlTreeParse(url, useInternal=TRUE)
            hrefs <- unlist(getNodeSet(xml, "//a/@href"))
            urls <-
                sprintf("%s%s", base, grep(pattern, hrefs, value=TRUE))
            seen[[url]] <- length(urls)
            for (url in urls)
                .do_crawl(url, base, pattern)
        }
    }
    .do_report <- function(url) {
        urls <- as.list(seen)
        data.frame(Url=names(urls), Links=unlist(unname(urls)),
                   stringsAsFactors=FALSE)
    }
    list(crawl=function(base, pattern="^/.*html$") {
        .do_crawl(base, base, pattern)
    }, report=.do_report)
})

crawler$crawl(favorite_url)
dim(crawler$report())

(обычный пример запоминания, числа Фибоначчи, не удовлетворяет - диапазон чисел, который не переполняет числовое представление R, мал, поэтому, вероятно, следует использовать справочную таблицу с эффективно предварительно вычисленными значениями) , Интересно, насколько гусеничным является синглтон; можно было бы легко следовать заводскому шаблону, поэтому один сканер на базовый URL.

...