在 R6Class 中创建动态方法,print(ls.str()) 的魔力

Creating dynamically methods in R6Class, magic of print(ls.str())

在 R 中,我想创建一个 class (R6Class),在调用初始化时创建一些动态方法(方法的数量及其名称取决于初始化中的参数)。但是我遇到了一个奇怪的环境问题。

这是一个不起作用的代码的简化版本。

library(R6)

ffactory <- function(i) {
  function() i
}

A <- R6Class(
  lock_objects=FALSE,
  public=list(
    initialize=function(args) {
      for (i in args) {
        self[[i]] <- ffactory(i)
      }
    }
  )
)

a <- A$new(c('a', 'b', 'c'))

现在:

> a$a()
[1] "c"
> a$b()
[1] "c"
> a$c()
[1] "c"

为了找出问题所在,我在 ffactory 函数中添加了打印环境的行。即

ffactory <- function(i) {
  print(ls.str())
  function() i
}

现在已经开始工作了!!!

> a$a()
[1] "a"
> a$b()
[1] "b"
> a$c()
[1] "c"

那为什么呢?应该有什么我不明白的地方。观察者效应还是什么? :)

print(ls.str())这行有什么魔力?实际上我不能从这一行中删除 printstr。当然,有这样的台词是很愚蠢的。更不用说屏幕上的垃圾了。

您遇到了懒惰求值 - R 在求值 i 之前会一直等待 - 在前一种情况下,i 将在所有实例中以其最后一个值求值。 printls.str 的组合并没有什么特别之处;在您的方法调用(a$a()a$b() 等)之前强制评估 i 的任何内容都会执行相同的操作。

形式上,这就是 force 的用途:

ffactory <- function(i) {
  force(i);
  function() i
}

R> a$a()
#[1] "a"
R> a$b()
#[1] "b"
R> a$c()
#[1] "c"

然而,这也恰好完成了工作:

ffactory <- function(i) {
  #force(i);
  .z <- capture.output(cat(i, "\n"))
  function() i
}

R> a$a()
#[1] "a"
R> a$b()
#[1] "b"
R> a$c()
#[1] "c"

想必有无数种强制求值的方法;不过,我认为使用 force 会使您的意图最清楚。

直接引用帮助文件,

force forces the evaluation of a formal argument. This can be useful if the argument will be captured in a closure by the lexical scoping rules and will later be altered by an explicit assignment or an implicit assignment in a loop or an apply function.

随后,

This is semantic sugar: just evaluating the symbol will do the same thing (see the examples).

事实上,看看 force 是如何定义的,

R> force
#function (x) 
#  x
#<bytecode: 0x3b7b528>
#<environment: namespace:base>

你甚至可以逃脱

ffactory <- function(i) {
  i; function() i
}

但如前所述,我认为显式调用 force 将使您的代码更具可读性。