local() 与 R 中的其他闭包方法有何不同?
昨天,我从 Bill 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 中的静态变量,其中采用了不同的方法。
这两种方法各有什么优缺点?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
封装
这种编程风格的优点是隐藏的对象不太可能被其他任何内容覆盖,因此您可以更加确信它们包含您的想法。它们不会被错误使用,因为它们不容易被访问。在问题的链接帖子中,有一个全局变量
count
,可以从任何地方访问和覆盖它,因此如果我们正在调试代码并查看count
和看到它的改变,我们无法真正确定代码的哪一部分改变了它。相反,在问题的示例代码中,我们可以更好地保证不涉及代码的其他部分。请注意,我们实际上可以访问隐藏函数,尽管它并不那么容易:
面向对象编程
另请注意,这非常接近面向对象编程,其中
example
和hidden .fn
是方法,hidden.x
是属性。我们可以这样做以使其明确:proto 不会隐藏
x
和fn
但它并不容易错误地访问它们,因为您必须使用p $x
和p$fn()
来访问它们,这与能够编写e <-environment(example); 没有什么不同。 e$hidden.fn()
编辑:
面向对象的方法确实增加了继承的可能性,例如,可以定义
p
的子级,其行为类似于p
只不过它覆盖了fn
。Encapsulation
The advantage of this style of programming is that the hidden objects won't likely be overwritten by anything else so you can be more confident that they contain what you think. They won't be used by mistake since they can't readily be accessed. In the linked-to post in the question there is a global variable,
count
, which could be accessed and overwritten from anywhere so if we are debugging code and looking atcount
and see its changed we cannnot really be sure what part of the code has changed it. In contrast, in the example code of the question we have greater assurance that no other part of the code is involved.Note that we actually can access the hidden function although its not that easy:
Object Oriented Programming
Also note that this is very close to object oriented programming where
example
andhidden.fn
are methods andhidden.x
is a property. We could do it like this to make it explicit:proto does not hide
x
andfn
but its not that easy to access them by mistake since you must usep$x
andp$fn()
to access them which is not really that different than being able to writee <- environment(example); e$hidden.fn()
EDIT:
The object oriented approach does add the possibility of inheritance, e.g. one could define a child of
p
which acts likep
except that it overridesfn
.local()
可以实现单例模式——例如,snow
包使用它来跟踪用户可能创建的单个 Rmpi 实例。local()
还可以用于管理脚本中的内存,例如,分配在子句的最后一行创建最终对象所需的大型中间对象。当local
返回时,大型中间对象可用于垃圾回收。使用函数创建闭包是一种工厂模式——bank R 简介文档中的 account 示例,每次调用
open.account
时,都会创建一个新帐户。正如@otsaw提到的,记忆化可以使用本地实现,例如,在爬虫中缓存网站
(记忆化的常见例子,斐波那契数,并不令人满意——不溢出R的数字表示的数字范围很小,因此人们可能会使用有效预先计算值的查找表)。有趣的是,这里的爬虫是一个单例;可以很容易地遵循工厂模式,因此每个基本 URL 都有一个爬虫。
local()
can implement a singleton pattern -- e.g., thesnow
package uses this to track the single Rmpi instance that the user might create.local()
might also be used to manage memory in a script, e.g., allocating large intermediate objects required to create a final object on the last line of the clause. The large intermediate objects are available for garbage collection whenlocal
returns.Using a function to create a closure is a factory pattern -- the bank account example in the Introduction To R documentation, where each time
open.account
is invoked, a new account is created.As @otsaw mentions, memoization might be implemented using local, e.g., to cache web sites in a crawler
(the usual example of memoization, Fibonacci numbers, is not satisfying -- the range of numbers that don't overflow R's numeric representation is small , so one would probably use a look-up table of efficiently pre-calculated values). Interesting how crawler here is a singleton; could as easily have followed a factory pattern, so one crawler per base URL.