【问题标题】:Mutating a variable in a closure [duplicate]在闭包中改变变量[重复]
【发布时间】:2014-06-12 08:54:18
【问题描述】:

我对 R 很陌生,但是来自 Scheme——它也是词法作用域并且有闭包——我希望能够在闭包中改变外部变量。

例如,在

foo <- function() {
  s <- 100

  add <- function() {
    s <- s + 1
  }

  add()
  s
}

cat(foo(), "\n") # prints 100 and not 101

我希望foo() 返回 101,但它实际上返回 100:

$ Rscript foo.R
100

我知道 Python 有 global 关键字来声明变量的范围(但不适用于此示例)。 R 是否需要类似的东西?

我做错了什么?

更新

啊,问题是我在add 中创建了一个,局部变量s 遮蔽了外部s?如果是这样,我如何在不创建局部变量的情况下改变s

【问题讨论】:

  • 版主可以重复关闭,或者您可以加入并投票以关闭会员资格。
  • @BondedDust Gotcha,我也投票赞成关闭它。只是在谷歌搜索时遇到了问题——实际上直到我在这里发布之前我都没有找到解决方案。所以其他模组可能会决定如何处理它。
  • 再投两票,无论版主是否注意,它将被关闭。您可能会发现引用类工具是一种适合您的编程风格的范例,并且不会被常规 R 使用视为危险。它使用命名环境并让您拥有“全局”变量的一些特性。
  • 谢谢。我将支持我的投票来关闭它。谢谢大家!

标签: r scope closures


【解决方案1】:

add() 函数中使用&lt;&lt;- 运算符进行赋值。

来自?"&lt;&lt;-"

运算符&lt;&lt;--&gt;&gt; 通常只用在函数中,并导致通过父环境搜索被分配变量的现有定义。如果找到这样的变量(并且其绑定未锁定),则重新定义其值,否则在全局环境中进行赋值。请注意,它们的语义与 S 语言中的不同,但与 R 的范围规则结合使用时很有用。有关更多详细信息和示例,请参阅“R 语言定义”手册。

【讨论】:

  • 你说“任务”。我应该总是使用&lt;&lt;- 进行分配吗?只使用&lt;- 进行声明?
  • 仅当您希望访问函数外部的名称绑定时(非正式地说)。否则你根本不应该使用它:它非常“危险”。
  • 它说如果&lt;&lt;- 在父环境中找不到任何引用,它将在 global 环境中分配。对我来说听起来很可怕,我希望它能够本地定义它。这是个问题吗?
  • 大多数有经验的 R 用户会惊恐地看到您采用始终使用 &lt;&lt;- 进行作业的策略。您应该将函数的值分配给工作区中的名称。
  • 这里会找到foo()中的绑定,这样就OK了。否则,它可能真的令人毛骨悚然,因此不鼓励使用它(除非你知道自己在做什么)。
【解决方案2】:

您还可以使用assign 并使用envir 参数精确定义范围,在这种情况下,与add 函数中的&lt;&lt;- 工作方式相同,但会使您的意图更加清晰:

foo <- function() {
  s <- 100

  add <- function() {
   assign("s", s + 1, envir = parent.frame())
  }

  add()
  s
}

cat(foo(), "\n") 

当然,在 R 中处理这种事情的更好方法是让你的函数返回它修改的变量(或多个变量),并将它们显式地重新分配给原始变量:

foo <- function() {
  s <- 100
  add <- function(x) x + 1
  s <- add(s)
  s
}

cat(foo(), "\n") 

【讨论】:

    【解决方案3】:

    这里还有一种比assign&lt;&lt;- 方法更安全的方法:

    foo <- function() {
        e <- environment()
        s <- 100
    
        add <- function() {
            e$s <- e$s + 1
        }
    
        add()
        s
    }
    
    foo()
    

    &lt;&lt;- 赋值可能会导致问题,如果你不小心拼错了你的变量名,它仍然会做一些事情,但它不会是你所期望的,并且很难找到问题的根源。 assign 方法可能会很棘手,如果您想将 add 函数移动到另一个函数内部,或者从另一个函数调用它。总体而言,最好的方法是不要让函数修改它们自己范围之外的变量,并让函数返回任何重要的东西。但是当这不可能时,上面的方法使用词法作用域来访问环境e,然后分配到环境中,所以它总是专门分配给那个函数,从不高于或低于。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-10-27
      • 2015-09-18
      • 2020-08-28
      • 2018-01-24
      • 2017-05-14
      • 2020-12-06
      • 2019-02-15
      • 1970-01-01
      相关资源
      最近更新 更多