【问题标题】:How does R evaluate these weird expressions?R 如何评估这些奇怪的表达式?
【发布时间】:2020-06-29 19:20:47
【问题描述】:

我试图在 R 中使 Python 3 样式的赋值解包成为可能(例如,a, *b, c = [1,2,3], "C"),虽然我得到了 所以 接近(你可以查看我的代码 here),我最终遇到了一些(奇怪的)问题。

我的代码是这样工作的:

a %,*% b %,% c <- c(1,2,3,4,5)

并将分配 a = 1、b = c(2,3,4) 和 c = 5(我的代码实际上确实这样做了,但我遇到了一个小障碍以后再说)。

为了让它做任何事情,我必须定义:

`%,%` <- function(lhs, rhs) {
   ...
}

和

`%,%<-` <- function(lhs, rhs, value) {
   ...
}

(以及%,*% 和%,*%&lt;-,它们是之前函数的轻微变体)。

第一个问题:为什么 R 用 *tmp* 代替 lhs 参数

据我所知,R 首先从左到右评估此代码(即从a 到c,直到它到达最后一个%,%,然后从右返回向左,沿途分配值。但我注意到的第一件奇怪的事情是,当我在x %infix% y &lt;- z 中执行match.call() 或substitute(lhs) 时,它表示lhs 参数的输入%infix% 是 *tmp*,而不是 a 或 x。

这对我来说奇怪,我在 R 手册或文档中找不到任何提及。我实际上在我的代码中使用这个奇怪的约定(即,它没有在赋值的右侧显示这种行为,所以我可以使用*tmp* 输入的存在来制作%,% 在作业的这一边表现不同),但我不知道为什么它会这样做。

第二个问题:为什么 R 在 anything else

之前检查对象是否存在

我的第二个问题是我的代码最终无法正常工作的原因。我注意到,如果你从任何赋值左侧的变量名开始,R 似乎甚至不会开始计算表达式——它返回错误object '&lt;variable name&gt;' not found。即,如果未定义 x,则 x %infix% y &lt;- z 将不会计算,即使 %infix% 并未实际使用或计算 x。

为什么 R 会有这样的行为,我可以更改或绕过它吗?如果我可以在 %,% 中运行代码之前 R 检查以查看如果x 存在,我可能会破解它,这样我就不会成为问题,而且我的 Python 解包代码将非常有用,可以实际共享。但就像现在一样,第一个变量需要已经存在,我认为这太局限了。我知道我可以通过将&lt;- 更改为%&lt;-% 之类的自定义中缀运算符来做一些事情,但是我的代码将与zeallot 包非常相似,我认为它不值得。 (它的功能已经非常接近了,但我更喜欢我的风格。)

编辑:

按照 Ben Bolker 的出色建议,我能够找到解决问题的方法...通过覆盖 &lt;-。

`<-` <- function(x, value) {
  base::`<-`(`=`, base::`=`)
  find_and_assign(match.call(), parent.frame())
  do.call(base::`<-`, list(x = substitute(x), value = substitute(value)),
          quote = FALSE, envir = parent.frame())
}
find_and_assign <- function(expr, envir) {
  base::`<-`(`<-`, base::`<-`)
  base::`<-`(`=`, base::`=`)
  while (is.call(expr))  expr <- expr[[2]]
  if (!rlang::is_symbol(expr)) return()
  var <- rlang::as_string(expr) # A little safer than `as.character()`
  if (!exists(var, envir = envir)) {
    assign(var, NULL, envir = envir)
  }
}

我很确定这将是一个致命的罪过,对吧?我无法确切地看到它会如何搞砸任何事情,但我的程序员感觉的刺痛告诉我这不适合分享像包这样的东西。 . 这会有多糟糕?

【问题讨论】:

  • 您是否需要在 LHS 上使用replacement functions 做一些事情...? (您的第二个示例函数应该是 %,%&lt;- 吗?
  • 糟糕,是的,我会改正的
  • 这看起来很糟糕/很危险:-(你当然可以在 github 包中使用它,但我敢打赌它很难被 CRAN 接受......CRAN policies 中没有任何内容这说明了关于屏蔽常用base 函数的任何内容(tidyverse 已经做了很多这样的事情,尽管没有像&lt;- 那样基本 - 另一方面与您的代码不同,它打算改变不兼容的行为(例如filter())

标签: r evaluation iterable-unpacking infix-operator


【解决方案1】:

对于您的第一个问题,关于*tmp*(可能与您的第二个问题有关):

来自Section 3.4.4 of the R Language definition:

对结构子集的赋值是复杂赋值的一般机制的一个特例:

x[3:5] <- 13:15

这个命令的结果就像执行了下面的一样

`*tmp*` <- x
x <- "[<-"(`*tmp*`, 3:5, value=13:15)
rm(`*tmp*`)

请注意,首先将索引转换为数字索引,然后沿数字索引顺序替换元素,就像使用了for 循环一样。任何名为*tmp* 的现有变量都将被覆盖和删除,并且该变量名不应在代码中使用。

同样的机制可以应用于[以外的函数。替换函数与粘贴的&lt;- 同名。它的最后一个参数必须称为value,是要分配的新值。

我可以想象您的第二个问题与“好像”代码的第一步有关:如果 R 在内部尝试评估 *tmp* <- x,则可能无法阻止尝试评估 x at这点……

如果你想深入挖掘,我认为用于处理“复杂赋值”的内部评估代码​​(它似乎在内部 cmets 中被调用)大约是 here ...

【讨论】:

  • 该死,我没看到这个!我挖了一下,发现我基本上可以通过执行 ``` &lt;- <-, c(x=substitute(x), value =substitute(value))) } ```.但是做这样的事情通常被认为是非常非常糟糕的,对吧?我认为这对于共享使用是不可接受的?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-11-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多