【发布时间】: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) {
...
}
(以及%,*% 和%,*%<-,它们是之前函数的轻微变体)。
第一个问题:为什么 R 用 *tmp* 代替 lhs 参数
据我所知,R 首先从左到右评估此代码(即从a 到c,直到它到达最后一个%,%,然后从右返回向左,沿途分配值。但我注意到的第一件奇怪的事情是,当我在x %infix% y <- z 中执行match.call() 或substitute(lhs) 时,它表示lhs 参数的输入%infix% 是 *tmp*,而不是 a 或 x。
这对我来说奇怪,我在 R 手册或文档中找不到任何提及。我实际上在我的代码中使用这个奇怪的约定(即,它没有在赋值的右侧显示这种行为,所以我可以使用*tmp* 输入的存在来制作%,% 在作业的这一边表现不同),但我不知道为什么它会这样做。
第二个问题:为什么 R 在 anything else
之前检查对象是否存在我的第二个问题是我的代码最终无法正常工作的原因。我注意到,如果你从任何赋值左侧的变量名开始,R 似乎甚至不会开始计算表达式——它返回错误object '<variable name>' not found。即,如果未定义 x,则 x %infix% y <- z 将不会计算,即使 %infix% 并未实际使用或计算 x。
为什么 R 会有这样的行为,我可以更改或绕过它吗?如果我可以在 %,% 中运行代码之前 R 检查以查看如果x 存在,我可能会破解它,这样我就不会成为问题,而且我的 Python 解包代码将非常有用,可以实际共享。但就像现在一样,第一个变量需要已经存在,我认为这太局限了。我知道我可以通过将<- 更改为%<-% 之类的自定义中缀运算符来做一些事情,但是我的代码将与zeallot 包非常相似,我认为它不值得。 (它的功能已经非常接近了,但我更喜欢我的风格。)
编辑:
按照 Ben Bolker 的出色建议,我能够找到解决问题的方法...通过覆盖 <-。
`<-` <- 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 做一些事情...? (您的第二个示例函数应该是
%,%<-吗? -
糟糕,是的,我会改正的
-
这看起来很糟糕/很危险:-(你当然可以在 github 包中使用它,但我敢打赌它很难被 CRAN 接受......CRAN policies 中没有任何内容这说明了关于屏蔽常用
base函数的任何内容(tidyverse 已经做了很多这样的事情,尽管没有像<-那样基本 - 另一方面与您的代码不同,它打算改变不兼容的行为(例如filter())
标签: r evaluation iterable-unpacking infix-operator