10

使用空环境开始新的 R 会话。编写一系列带有参数的函数,该参数将用作times调用中的参数值rep()

f <- function(n) {
  rep("hello", times = n)
}
f(x)

有人期望这会失败,实际上有人会得到:

# Error in f(x) : object 'x' not found

稍微修改一下函数:

f2 <- function(n) {
  ls.str()
  rep("hello", times = n)
}

f2(x)

正如预期的那样,它仍然失败:

# Error in f2(x) : object 'x' not found

再修改一点(在控制台中查看环境):

f3 <- function(n) {
  print(ls.str())
  rep("hello", times = n)
}

f3(x)

我仍然期待失败,而是得到:

## n : <missing>
## [1] "hello"

就好像调用print()使 rep 工作好像times被设置为 1。

4

3 回答 3

6

这不是答案,但太长了,无法作为评论发表。一个最小的可重现示例是:

f3 <- function(n) {
  try(get("n", environment(), inherits=FALSE))
  rep("hello", times = n)
}
f3(x)
## Error in get("n", environment(), inherits = FALSE) : object 'x' not found
## [1] "hello"

以下内容是推测性的,并且基于对do_rep. get开始 promise 评估,但在没有找到“missing”符号时似乎使 promise 部分未评估。 rep,作为一个原语,然后尝试在n没有意识到它是一个部分评估的承诺的情况下进行操作,并且基本上这隐含地导致了“n == 1”的假设。

此外,这表明 Promise 处于一种奇怪的状态(必须使用browser/debug才能看到它):

f3a <- function(n) {
  try(get("n", environment(), inherits=FALSE))
  browser()
  rep("hello", times = n)
}
f3a(x)
## Error in get("n", environment(), inherits = FALSE) : object 'x' not found
## Called from: f3a(x)
# Browse[1]> (n)
## Error: object 'x' not found
## In addition: Warning message:
## restarting interrupted promise evaluation 
## Browse[1]> c
## [1] "hello"
于 2017-09-18T16:17:43.310 回答
6

我今天早些时候收到一份报告,该错误已在 R-devel 和 R-patched 中修复。

问题是 R 源中的缺失测试没有考虑中断的promise 评估的情况。Luke Tierney已经提交了一个修复程序,可以在 GitHub 上查看。

于 2017-09-20T02:01:36.443 回答
2
f4 <- function(n) {
  print('test')
  print(ls.str())
  print('end test')
  rep("hello", times = n)
}
f4(x)

## [1] "test"
## n : <missing>
## [1] "end test"
## [1] "hello"

里面有一些东西print.ls_str,从弗兰克的聊天测试中,下面的代码表现出同样的问题:

f6 <- function(n) {
  z = tryCatch(get("n", new.env(), mode = "any"), error = function(e) e)
  rep("A", n)
}

在 R 源代码中挖掘了一下,我发现了以下代码

#     define GET_VALUE(rval)                      \
    /* We need to evaluate if it is a promise */  \ 
    if (TYPEOF(rval) == PROMSXP) {                \
        PROTECT(rval);                            \
        rval = eval(rval, genv);                  \
        UNPROTECT(1);                             \
    }                                             \
                                                  \
    if (!ISNULL(rval) && NAMED(rval) == 0)        \
        SET_NAMED(rval, 1)


    GET_VALUE(rval);
    break;


    case 2: // get0(.)
    if (rval == R_UnboundValue)
        return CAD4R(args);// i.e.  value_if_not_exists
    GET_VALUE(rval);
    break;
    }
    return rval;
}
#undef GET_VALUE

我很惊讶这个编译正确,据我记得(我的 C 远远落后)#define不允许在#和定义之间有空格。

在挖掘之后,我错了,来自gcc doc

'#' 前后也允许有空格。

所以这部分代码可能有一些东西,但我无法确定到底是什么。

于 2017-09-18T15:31:04.533 回答