【问题标题】:Return statement in expression to be used as function body表达式中的返回语句用作函数体
【发布时间】:2021-03-22 15:51:08
【问题描述】:

我在 R 中运行了大量繁重的计算,我正在尝试编写一个函数来帮助我缓存我的结果,并且只在必要时计算它们。我想出了这个:

memoize <- function(filename, fun, ...){
    if(file.exists(filename)){
        message(paste("Reading", filename))
        return(readRDS(filename))
    } else {
        message(paste(filename, "not found, running computations"))
        result <- do.call(fun, list(...))
        message(paste("Writing", filename))
        saveRDS(result, filename)
        return(result)
    }
}

这让我可以这样做:

result <- memoize("test.rds", function(){ return(runif(100))} )

这很好用,但是,我希望能够不使用“function()”部分,即传递表达式而不是函数。这可以按如下方式完成:

memoize <- function(filename, expr){
    if(file.exists(filename)){
        message(paste("Reading", filename))
        return(readRDS(filename))
    } else {
        message(paste(filename, "not found, running computations"))
    
         f <- function(){}
        body(f) <- expr
        environment(f) <- parent.frame()
        result <- do.call(f, list())
    
        message(paste("Writing", filename))
        saveRDS(result, filename)
        return(result)
    }
}

根据capturing an expression as a function body in R

使用时效果很好

result <- memoize("test.rds", { runif(100)} )

但是,我想在该表达式中保留显式的 return() 语句。当我尝试时

result <- memoize("test.rds", { return(runif(100))} )

我明白了

没有函数返回,跳转到顶层

我显然误解了它的工作原理:为什么 expr 似乎在绑定到 f 之前被评估?我怎样才能做到这一点?

【问题讨论】:

标签: r


【解决方案1】:

无论my recommendation not to use return in R 是什么,除非明确要求提前访问,否则以下操作应该有效:

memoize = function (cache_filename, expr) {
    if (file.exists(cache_filename)) {
        message('reading ', cache_filename)
        readRDS(cache_filename)
    } else {
        message(cache_filename, ' not found, rerunning computation')
        f = local(function () NULL, envir = .GlobalEnv)
        body(f) = substitute(expr)
        result = f()
        saveRDS(result, cache_filename)
        result
    }
}

请特别注意,使用 do.call(f, list()) 不是必需的,也没有任何意义:它是一个常规函数,只需使用 f() 调用它。

您当前的解决方案实际上非常有趣:它在使用时评估 expr 并将 result 分配给 f 的函数体。事实上,这只发生在结果是一个简单的 R 值时才有效,而不是当它是一个复杂的对象时。在这种情况下,您根本不需要嵌套函数,您可以自行评估expr,这实际上是一种相当常见的模式(例如,在try 和@987654330 的实现中使用@!)。换句话说,以下将是一个惯用的实现(但不支持使用控制流,例如return()

memoize = function (cache_filename, expr) {
    if (file.exists(cache_filename)) {
        message('reading ', cache_filename)
        readRDS(cache_filename)
    } else {
        message(cache_filename, ' not found, rerunning computation')
        result = expr
        saveRDS(result, cache_filename)
        result
    }
}

【讨论】:

  • 太棒了,感谢您的快速回答! :-)
猜你喜欢
  • 1970-01-01
  • 2019-09-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-11
  • 2020-12-04
相关资源
最近更新 更多