Как локальные() отличаются от других подходов к замыканию в R?
Вчера я узнал от Билла Venables, как 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, где использовался другой подход.
Каковы плюсы и минусы этих двух методов?
Ответы
Ответ 1
Инкапсуляция
Преимущество этого стиля программирования в том, что скрытые объекты вряд ли будут перезаписаны чем-либо еще, поэтому вы можете быть более уверенными в том, что они содержат то, что вы думаете. Они не будут использоваться по ошибке, так как к ним нельзя легко получить доступ. В связанном с сообщением в вопросе есть глобальная переменная 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()
EDIT:
Объектно-ориентированный подход добавляет возможность наследования, например. можно определить дочерний элемент p
, который действует как p
, за исключением того, что он переопределяет fn
.
ch <- p$proto(fn = function(.) cat("Hello from ch\n")) # child
ch$example() # prints: Hello from ch
Ответ 2
local()
может реализовать одноэлементный шаблон - например, пакет snow
использует это для отслеживания одного экземпляра Rmpi, который может создать пользователь.
getMPIcluster <- NULL
setMPIcluster <- NULL
local({
cl <- NULL
getMPIcluster <<- function() cl
setMPIcluster <<- function(new) cl <<- new
})
local()
также может использоваться для управления памятью в script, например, выделение больших промежуточных объектов, необходимых для создания конечного объекта в последней строке предложения. Большие промежуточные объекты доступны для сбора мусора, когда возвращается local
.
Использование функции для создания замыкания - это шаблон factory - пример банковский счет в документации Введение в R, где каждый время open.account
, создается новая учетная запись.
Как упоминает @otsaw, memoization может быть реализована с использованием локальных, например, для кэширования веб-сайтов в искателе
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())
(обычный пример memoization, числа Фибоначчи, не удовлетворяет - диапазон чисел, которые не переполняют числовое число R, мало, поэтому можно было бы, вероятно, использовать справочную таблицу эффективно предварительно рассчитанных значений), Интересно, как гусеница здесь одиночная; может легко следовать шаблону factory, поэтому один искатель на базовый URL.