r - 关于 R 中的词法范围

标签 r variables scope lexical lexical-scope

我对 R 相当陌生,在阅读手册时,我偶然发现了一段关于词法范围的文章以及以下代码示例:

 open.account <- function(total) {
   list(
     deposit = function(amount) {
       if(amount <= 0)
         stop("Deposits must be positive!\n")
       total <<- total + amount
       cat(amount, "deposited.  Your balance is", total, "\n\n")
     },
     withdraw = function(amount) {
       if(amount > total)
         stop("You don't have that much money!\n")
       total <<- total - amount
       cat(amount, "withdrawn.  Your balance is", total, "\n\n")
     },
     balance = function() {
       cat("Your balance is", total, "\n\n")
     }
   )
 }

 ross <- open.account(100)
 robert <- open.account(200)

 ross$withdraw(30)
 ross$balance()
 robert$balance()

 ross$deposit(50)
 ross$balance()
 ross$withdraw(500)

所以,我明白上面的代码做了什么,我想我仍然对它的工作原理感到困惑。如果在函数完成执行后您仍然可以访问函数的“局部”变量,那么预测何时不再需要变量是不是非常困难或不可能?在上面的代码中,如果它被用作更大程序的一部分,“total”是否会一直存储在内存中,直到整个程序完成?(本质上成为一个全局变量内存)如果这是真的,不会这会导致内存使用问题吗?

我在这个网站上查看了另外两个问题:“词法范围是如何实现的?”和“为什么编译器更喜欢词法范围?”。那里的答案就在我的脑海里,但它让我想知道:如果(正如我猜测的那样)编译器不只是将所有变量设为全局(内存方面),而是使用一些技术来预测某些变量何时不会不再需要并且可以删除,做这项工作实际上不会使编译器的事情变得更难而不是更容易吗?

我知道这是很多不同的问题,但任何帮助都会很好,谢谢。

最佳答案

OP 似乎正在寻找有关环境的说明。

在 R 中,每个函数[1] 都有一个封闭环境。这是它知道的对象集合,以及作为参数传入或在其代码中创建的对象的集合。

在提示符下创建函数时,其环境是全局环境。这只是您工作区中对象的集合,您可以通过键入 ls() 来查看这些对象。 .例如,如果您的工作区包含一个数据框 Df ,您可以创建如下函数:

showDfRows <- function()
{
    cat("The number of rows in Df is: ", nrow(Df, "\n")
    return(NULL)
}

您的函数知道 Df即使您没有将其作为参数传递;它存在于函数的环境中。环境可以嵌套,这就是包命名空间之类的工作方式。例如,您可以执行 lm(y ~ x, data=Df)以适应回归,即使您的工作区不包含任何名为 lm 的对象.这是因为全局环境的父链包括 stats包,这是lm的地方功能生活。 [2]

当函数在另一个函数内部创建时,它们的封闭环境是其父函数的评估框架。这意味着子函数可以访问父函数已知的所有对象。例如:
f <- function(x)
{
    g <- function()
    {
        cat("The value of x is ", x, "\n")
    }
    return(NULL)
}

请注意 g不包含任何名为 x 的对象,它的任何参数也没有命名为 x .然而,这一切仍然有效,因为它会检索 x来自其父级的评估框架f .

这是上面代码使用的技巧。当您运行时 open_account ,它会创建一个评估框架,在其中执行其代码。 open_account然后创建 3 个函数,deposit , withdrawbalance .这三个中的每一个都有作为其封闭环境的评估框架open_account .在这个评估框架中有一个名为 total 的变量。 ,其值由您传入,将由 deposit 操作, withdrawbalance .

open_account完成,它返回一个列表。如果这是一个常规函数,它的求值框架现在将由 R 处理。然而,在这种情况下,R 可以看到返回的列表包含需要使用该求值框架的函数;所以框架继续存在。

那么,为什么罗斯和罗伯特的账户不会相互冲突呢?每次执行 open_account , R 创建了一个新的评估框架。打开罗斯和罗伯特账户的框架是完全独立的,就像,如果你运行 lm(y ~ x, data=Df) , 如果你运行 lm(y ~ x, data=Df2 ,将会有一个单独的框架)。每次open_account返回,它将带来一个新的环境来存储刚刚创建的余额。 (它还将包含 depositwithdrawbalance 函数的新副本,但通常我们可以忽略用于此的内存。)

[1] 技术上每个闭包,但让我们不要搞混

[2] 同样,命名空间和环境之间存在技术区别,但在这里并不重要

关于r - 关于 R 中的词法范围,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17368267/

相关文章:

R降雪并行,Rscript.exe随着时间的推移逐渐变得不活跃

javascript - 从 Javascript 中的对象调用事物时有哪些限制?

python - 取消屏蔽最后打印的表达式变量

windows - 如何处理 Windows 批处理文件中带引号的变量值

xml - scala.Predef 中 $scope 的用途是什么?

javascript - 为什么我无法访问设置?

oracle - 从 r 连接 oracle

r - 使用 rbind 组合数据帧和缺失数据帧

r - 导入带有显示为第一行数据的标题的 .csv

C# - 匿名函数和事件处理程序