【问题标题】:Playing with R environments玩 R 环境
【发布时间】:2019-03-20 19:44:26
【问题描述】:

我有一个奇怪的环境/范围动态,我一直在试图弄清楚,并寻找正确的或推荐的方法来实现这一点。

我在下面为我的问题制作了一个玩具示例,纯粹是为了说明。 (我知道这个特定问题可以更简单地解决,但它说明了我试图实现的动态)。

当前功能代码:

master_function <- 
  function(x, iter = 100){
    x_p1 <- function(){ x <<- x + 1 }
    x_m1 <- function(){ x <<- x - 1 }

    path <- numeric(iter)
    for(i in 1:iter){
      next_step <- sample(c('p', 'm'), 1)
      if(next_step == 'p'){
        x_p1()
      } else { 
        x_m1()
      }
      path[i] <- x
    }
    path
  }

这段代码的问题(特别是对于一个真正困难的问题)是它使使用 RStudio 调试实用程序调试 x_p1x_m1 函数内容变得不可能。

希望将代码重组为如下所示:

master_function <- 
  function(x, iter = 100){
    master_env <- environment()
    path <- numeric(iter)
    for(i in 1:iter){
      next_step <- sample(c('p', 'm'), 1)
      if(next_step == 'p'){
        x_p1(master_env)
      } else { 
        x_m1(master_env)
      }
      path[i] <- x
    }
    path
  }

x_p1 <- function(env){ assign('x', get('x', envir = env) + 1, envir = env) }
x_m1 <- function(env){ assign('x', get('x', envir = env) - 1, envir = env) }

但这也很丑陋。有没有办法增加搜索路径,例如,使对master_env 的访问更清晰?

编辑:@MrFlick 要求的更多信息 基本上我有很多移动部件的模拟。随着它的进行,不同的事件(被引用的子函数)被触发以修改模拟的状态。这些函数当前为每个函数调用修改许多不同的状态对象。由于这些函数是在主函数调用中创建的,因此我可以利用词法范围和&lt;&lt;- 运算符,但我无法在这些函数中进行调试。

试图弄清楚如何在主模拟之外创建这些功能。如果我理解正确的话,如果我让函数消耗模拟状态并返回修改后的版本,那么内存开销会很大。

【问题讨论】:

  • 我无法理解此示例的要求。这似乎不是很有说明性。也许用语言准确地说出您希望完成的事情?您似乎想优化调试,但我不确定在这种情况下这意味着什么。
  • @MrFlick 更详细地编辑了问题
  • 我不明白为什么您不只在列表中跟踪所有状态信息,然后将当前状态传递给您的函数,如果需要,它们可以输出更新的状态列表。这将更像 R。只有发生变化的值才会在内存中更新。您是否有一个显示大内存成本的示例?

标签: r environment scoping


【解决方案1】:

1) 跟踪 使用tracex_p1x_m1 的定义之后插入debug 语句,然后在运行master_function 时可以单步执行。 p>

trace(master_function, at = 4, quote({debug(x_p1); debug(x_m1) }))

untrace(master_function) 将其关闭。使用body(master_function)[4]查看哪一行对应于4。更多信息请查看?trace

2) 仪器 另一种可能性是像这样检测您的函数,然后使用master(function(x, DEBUG = TRUE) 调用它以打开调试。

master_function <- 
  function(x, iter = 100, DEBUG = FALSE){
    x_p1 <- function(){ x <<- x + 1 }
    x_m1 <- function(){ x <<- x - 1 }
    if (DEBUG) {
      debug(x_p1)
      debug(x_m1)
    }

    path <- numeric(iter)
    for(i in 1:iter){
      next_step <- sample(c('p', 'm'), 1)
      if(next_step == 'p'){
        x_p1()
      } else { 
        x_m1()
      }
      path[i] <- x
    }
    path
  }

【讨论】:

    【解决方案2】:

    为什么 x 需要驻留在替代环境中?以下完全内化并避免了多种环境。

    x_p1 <- function(z){ z + 1 }
    x_m1 <- function(z){ z - 1 }
    master_function <- 
      function(x, iter = 100){
        new_x <- x
    
    
        path <- numeric(iter)
        for(i in 1:iter){
          next_step <- sample(c('p', 'm'), 1)
          if(next_step == 'p'){
            new_x <- x_p1(new_x)
          } else { 
            new_x <- x_m1(new_x)
          }
          path[i] <- new_x
        }
        path
      }
    

    【讨论】:

    • 这是我实际尝试实现的极其简化的版本。在master_function 中有几十个对象和5-6 个函数,旨在有条件地修改master_function 环境中的部分或全部对象。如果我坚持使用与上述类似的方法,则很难调试任何错误,因为我无法在这些函数中放置断点
    • 我已经更新了将 x_p1 和 x_m1 完全定位在 master_function 之外的提议,这可能会进一步推动原始建议将每个函数隔离到一个独立的环境范围内。通过一起避免“全局”或跨范围的环境变量,也简化了调试的目标。鉴于示例的简单性,很难评估,但在大多数情况下,函数可以仅使用输入变量进行操作;甚至可以通过返回一个列表来操作并返回多个变量
    • @jameselmore:在代码中的不同位置修改“远程”环境中的变量是不明智的,因为它会导致代码逻辑混乱。将所有变量放在一个列表中,然后将整个列表传递给 5-6 个函数以修改特定元素。每个函数都应该返回修改后的列表。这被称为copy-on-modify semantics,它自然适用于像 R 这样的函数式语言。
    • 一般来说,R 和大多数流行的包都提供了修改时复制的语义,但只有在幕后需要时才实际复制。与往常一样,衡量代码更改对内存影响的最佳方法是profiling
    • @jameselmore 重写你的代码,如果它真的成为问题,还要担心其他事情
    猜你喜欢
    • 2018-11-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-06
    相关资源
    最近更新 更多