2011-10-26 12 views
16

Wczoraj dowiedziałem się od Billa Venables jak miejscowy() może pomóc w tworzeniu funkcji i zmiennych statycznych, npW jaki sposób local() różni się od innych podejść do zamknięcia w R?

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

który zachowuje się jak następuje z wiersza poleceń:

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

widziałem to coś w rodzaju omawianego w Static Variables in R, gdzie zastosowano inne podejście.

Jakie są plusy i minusy tych dwóch metod?

Odpowiedz

12

Encapsulation

Zaletą tego stylu programowania jest to, że ukryte obiekty nie mogą zostać zastąpione przez coś innego, dzięki czemu można mieć większą pewność, że zawierają one co myślisz. Nie będą używane przez pomyłkę, ponieważ nie mogą być łatwo dostępne. W odnośniku do postu w pytaniu znajduje się zmienna globalna, count, do której można uzyskać dostęp i zastąpić ją z dowolnego miejsca, więc jeśli debugujemy kod i patrzymy na count i widzimy jego zmienioną wersję, nie możemy naprawdę stwierdzić, która część kodu ma zmienił to. Natomiast w przykładowym kodzie pytania mamy większą pewność, że żadna inna część kodu nie jest zaangażowana.

Zauważ, że faktycznie mają dostęp do ukrytych funkcji, chociaż jej nie takie proste:

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

Object Oriented Programming

Należy również pamiętać, że jest bardzo blisko do programowania obiektowego, gdzie example i hidden.fn są metodami i hidden.x jest właściwością. Moglibyśmy zrobić to w ten sposób, aby to jednoznacznie:

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

proto nie ukrywa x i fn ale nie jest to łatwy dostęp do nich przez pomyłkę, ponieważ trzeba użyć p$x i p$fn() aby dostęp do nich nie jest naprawdę inaczej niż jest w stanie napisać e <- environment(example); e$hidden.fn()

EDIT:

podejście obiektowe ma dodać możliwość dziedziczenia, np można zdefiniować dziecko o numerze p, które zachowuje się jak p z tą różnicą, że zastępuje fn.

ch <- p$proto(fn = function(.) cat("Hello from ch\n")) # child 
ch$example() # prints: Hello from ch 
6

local() można realizować Singleton - przykład opakowanie snow wykorzystuje to śledzenie jednego wystąpienia RMPI, które użytkownik może tworzyć.

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

local() może być również używany do zarządzania pamięcią w skrypcie przykład przydzielania dużych obiektów pośrednich potrzebnych do uzyskania końcowego przedmiotu na ostatniej linii punktu. Duże obiekty pośrednie są dostępne do zbierania śmieci po zwrocie local.

Użycie funkcji do utworzenia zamknięcia to wzór fabryczny - przykład bank account w dokumentacji Wprowadzenie do R, gdzie za każdym razem wywoływana jest open.account, tworzone jest nowe konto.

Jak @otsaw wspomina, memoization mogą być realizowane z wykorzystaniem lokalnych, na przykład, do pamięci podręcznej stron internetowych w robota

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

(zwykle przykładem memoization, liczby Fibonacciego, nie jest satysfakcjonująca - zakres liczby, które nie przepełniają liczbowej reprezentacji R, są małe, więc prawdopodobnie użyjemy tabeli przeglądowej efektywnie wyliczonych wartości). Interesujące, jak robota tutaj jest singleton; równie dobrze mógł być zgodny ze wzorcem fabrycznym, więc jeden robot na podstawowy adres URL.

+1

Innym wzorem, który "lokalnie" sprawia, że ​​jest wygodny, jest zapamiętywanie. Jest taki przykład w The R Inferno. – otsaw

+0

Twoja myśl o robocie będącym singletonem jest interesująca, ponieważ alternatywą dla lokalnego jest natychmiastowa ocena anonimowej funkcji bez argumentów - wzorzec fabryki może wykorzystywać zamknięcie ponad bazowym adresem URL. – hadley

Powiązane problemy