首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在R中使用高阶函数的callCC

在R中使用高阶函数的callCC
EN

Stack Overflow用户
提问于 2013-10-17 15:05:41
回答 3查看 251关注 0票数 5

我试图弄清楚如何得到R的callCC函数,用于短路计算一个函数,以便与像lapply和Reduce这样的函数一起工作。

动机

这将使Reduce和lapply具有渐近效率> O(n),因为它允许您提前退出计算。

例如,如果我在列表中搜索一个值,我可以在列表中映射一个'finder‘函数,第二次发现它就会停止运行并返回这个值(就像中断一个循环,或者使用一个return语句提前爆发)。

问题是,我很难使用callCC所需的样式编写lapply和Reduce应该使用的函数。

示例

假设我正在编写一个函数,以便在列表中找到'100‘值:相当于

代码语言:javascript
复制
imperativeVersion <- function (xs) {
    for (val in xs) if (val == 100) return (val)
}

传递给lapply的函数如下所示:

代码语言:javascript
复制
find100 <- function (val) { if (val == 100) SHORT_CIRCUIT(val)  }
functionalVersion <- function (xs) lapply(xs, find100)

这(显然)崩溃,因为短路功能还没有定义。

代码语言:javascript
复制
callCC( function (SHORT_CIRCUIT) lapply(1:1000, find100) )

问题是,这也会崩溃,因为在定义find100时,短路函数并不存在。我希望类似的东西能起作用。

以下操作之所以有效,是因为SHORT_CIRCUIT是在创建传递给lapply的函数时定义的。

代码语言:javascript
复制
callCC(
    function (SHORT_CIRCUIT) {
        lapply(1:1000, function (val) {
             if (val == 100) SHORT_CIRCUIT(val)
        })
)

如何在传递给lapply的函数中定义SHORT_CIRCUIT,而不像上面那样内联地定义它呢?

我知道这个例子可以使用循环、减少或任何其他方法来实现。我正在寻找一个解决方案的问题,使用callCC与lapply和减少在具体。

如果我是模糊的,或需要任何澄清,请在下面留下评论。我希望有人能帮上忙:)

编辑一:方法应该是“生产-质量”;没有偏离功能或类似的黑魔法。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2013-10-29 03:43:05

我找到了解决这个问题的方法:

代码语言:javascript
复制
find100 <- function (val) {
    if (val == 100) SHORT_CIRCUIT(val)
}

short_map <- function (fn, coll) {


    callCC(function (SHORT_CIRCUIT) {

        clone_env <- new.env(parent = environment(fn))
        clone_env$SHORT_CIRCUIT <- SHORT_CIRCUIT

        environment(fn) <- clone_env
        lapply(coll, fn)

    })
}

short_map(find100, c(1,2,100,3))

使高阶函数与callCC一起工作的诀窍是,在执行程序的其余部分之前,将短路函数分配到输入函数环境中。我复制了一个环境以避免意外的副作用。

票数 3
EN

Stack Overflow用户

发布于 2021-08-13 07:17:57

您可以在R @alexis_laz中使用元编程来实现这一点,实际上,这种方法已经是元编程了。然而,他使用的字符串是一个肮脏的黑客和容易出错的字符串。所以你很好地拒绝了它。

接近@alexis_laz方法的正确方法是在代码级别上进行争论。在基本R中,这是使用substitute()完成的。然而,还有更好的包,如哈德利韦翰的rlang。但是我给出了一个基本的R解决方案(更少的依赖性)。

代码语言:javascript
复制
lapply_ <- function(lst, FUN) {
  eval.parent(
    substitute(
      callCC(function(return_) {
        lapply(lst_, FUN_)
      }), 
      list(lst_ = lst, FUN_=substitute(FUN))))
}

您的SHORT_CIRCUIT函数实际上是一个更通用的控制流return函数(或者一个使用参数返回它的break函数)。因此,我称它为return_

我们希望有一个lapply_函数,其中我们可以在FUN=部分中使用return_来从通常的lapply()中提取break

正如你所表明的,这就是目标:

代码语言:javascript
复制
callCC(
  function (return_) {
    lapply(1:1000, function (x) if (x == 100) return_(x))
  }
)

关于这个问题,我们希望能够概括这个表达式。我们想要

代码语言:javascript
复制
callCC(
  function(return_) lapply(lst, FUN_)
)

在这里,我们可以在函数定义中使用我们为FUN_提供的return_。但是,只有在将函数定义代码插入到该表达式中时,函数定义才能看到return_。这正是@alexis_laz尝试使用字符串和eval的方法。或者你通过操纵环境变量来做到这一点。

我们可以使用substitute(expr, replacer_list)安全地实现文字代码的插入,其中expr是要操作的代码,replacer_list是替换代码的查找表。通过substitute(FUN),我们使用为FUN=给出的用于lapply_的字面代码,而不计算它。这个表达式返回引用文字的代码(比@alexis_laz方法中的字符串更好)。

大的substitute命令说:“接受表达式callCC(function(return_) lapply(lst_, FUN_)),并将表达式中的lst_替换为为collFUN_提供的列表,将其替换为为FUN提供的引用表达式。

然后在父环境(eval.parent())中计算这个替换的表达式,意思是:得到的表达式替换了lapply_()调用,并准确地在放置的位置执行。

eval.parent() (或eval( ... , envir=parent.frame()))的这种使用是愚蠢的证明。(否则,tidyverse软件包就不会是生产级.)。

因此,通过这种方式,您可以泛化callCC()调用。

代码语言:javascript
复制
lapply_(1:1000, FUN=function(x) if (x==100) return_(x))
## [1] 100
票数 2
EN

Stack Overflow用户

发布于 2013-10-17 19:10:42

我不知道它是否有用,但是:

代码语言:javascript
复制
    find100 <- "function (val) { if (val == 100) SHORT_CIRCUIT(val)  }"
    callCC( function (SHORT_CIRCUIT) lapply(1:1000, eval(parse(text = find100))) )
    #[1] 100
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/19430410

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档