需要全局赋值作为R中重用代码的一种方式的函数——好的还是坏的做法



所以我在R中运行一个模拟,每次发生某个事件时,我都需要重新计算一些变量,而这个事件可能在循环的一次迭代中发生多次。代码看起来像这样。

nums = a+b*c;
nums[v:(v+3)] = 1
listicle = lapply(stuff, func)
n = sum(nums)-t

这似乎很明智,为了不重复它,我用一些函数包装它,所以它看起来可能像。。。

updateVars = function(){
nums <<- a+b*c;
nums[v:(v+3)] <<- 1
listicle <<- lapply(stuff, func)
n <<- sum(nums)-t
}

然后每当我需要更新这些变量时,我就会调用updateVars()

不过,我在读R推理,在第6章中,他们认为全球分配是一种糟糕的做法。

不过,我不确定如果没有这样的函数,我将如何保持代码的简短性和可读性。是我遗漏了一个技巧,还是这是全局赋值的有效例外?

谢谢,Nathan

编辑:可复制代码。

a=rnorm(100,3,2)
b=rnorm(100,1,3)
c=rnorm(100,4,2)
updatedef = function()
{
d<<-a+b-c^2
e<<-var(a^b)-mean(c)
f<<-lapply(a+b+c, function(t){rnorm(10,t,t/3)})
}
for (i in 1:400){
a=a+1
updatedef()
print(d+e)
print(f)
b=2*b
updatedef()
print(d+e)
print(f)
c=runif(100)
print(d+e)
print(f)
}

我在您的示例中看到了两个"全局变量"问题,我已经重写了它以避免这两个问题。它并没有让代码变得更容易阅读(尤其是对于不熟悉代码的人来说),而且对我来说肯定感觉更安全

我看到的问题是:

  • updatedef取决于在某些父环境中找到abc的正确值。我更改了定义,使它们成为明确的论点

  • updatedef没有return,它只是替换了最近父环境中的def的值。我将这些值的列表作为显式返回。

这是代码:

update_def = function(a, b, c) {
d = a + b - c^2
e = var(a^b) - mean(c)
f = lapply(a + b + c, function(t) rnorm(10, t, t / 3))
return(list(d = d, e = e, f = f))
}
for (i in 1:4) {
a = a + 1
def = update_def(a, b, c)
with(def, print(d + e))
print(def$f)
b = 2 * b
def = update_def(a, b, c)
with(def, print(d + e))
print(def$f)
c = runif(100)
def = update_def(a, b, c)
with(def, print(d + e))
print(def$f)
}   

泛化

我知道这是一个有趣的例子-也许你的真实代码需要计算的变量比a, b, c还多。在这种情况下,就像我创建的def列表一样,我敦促你使用list来存储它们。你甚至可能想创建自己的类,扩展一个有一些方法的列表。

在上面的例子中,我们可以使用自定义的print方法来创建def类,该方法执行代码中经常重复的两行:

update_def_c = function(a, b, c) {
d = a + b - c^2
e = var(a^b) - mean(c)
f = lapply(a + b + c, function(t) rnorm(10, t, t / 3))
def = list(d = d, e = e, f = f)
class(def) = c("def", "list")
return(def)
}
print.def = function(def, ...) {
print(def$d + def$e, ...)
print(def$f, ...)
}

然后,我们可以直接调用print(def),而不是重复print(d+e); print(f)。最重要的是,如果以后我们希望更改打印行为,只需要在一个地方进行更改。这使您的代码更加模块化。

我认为这些更改使您的代码对普通R受众更具可读性。我们总是假设函数没有的副作用(比如在没有显式赋值的情况下更改值)。您的updatedef是一个命名良好的函数,因为它的名称表明了它的作用,但从用法上看,它对abc的依赖性并不清楚,这可能会让不熟悉代码的人感到困惑(甚至可能在一两年后回头看!)。