定义 `(<-` 运算符时获取 x 的名称

Posted

技术标签:

【中文标题】定义 `(<-` 运算符时获取 x 的名称【英文标题】:Get name of x when defining `(<-` operator 【发布时间】:2018-12-17 04:33:31 【问题描述】:

我想定义(&lt;- 并访问左侧参数的名称:

*&lt;- 函数在内部使用中间的'*tmp*' 变量。是否还能得到 x 的名字?

`(<-` <- function(x,value)
  print(deparse(substitute(value)))
  print(deparse(substitute(x)))
  print(match.call())
  value


foo <- 0
(foo) <- 3
# [1] "3"
# [1] "*tmp*"
# `(<-`(x = `*tmp*`, value = 3)# [1] "3"

我想从函数内部获取“foo”。

我试图通过使用tracemem 来破解它,即在函数内部调用sapply(ls(envir = parent.frame()),tracemem)tracemem(x)foo*temp*x 的地址都是不同的。

【问题讨论】:

【参考方案1】:

1)如果您愿意更改它,以便调用是:

fooify[foo] <- 99

那么我们可以这样做,foo 不需要事先存在:

fooify <- structure(NA, class = "fooify")
"[<-.fooify" <- function(x, var, value) 
  print(deparse(substitute(var)))
  eval.parent(substitute(var <- value))
  x


# test

if (exists("foo")) rm(foo)
fooify[foo] <- 99
## [1] "foo"  <-- this comes from the print statement
foo
## [1] 99

2) := 如果使用:= 没问题,那么:

`:=` <- function(lhs, rhs) 
  print(deparse(substitute(lhs)))
  eval.parent(substitute(lhs <- rhs))


# test
if (exists("foo")) rm(foo)
foo := 99
## [1] foo   <-- this comes from print statement
foo
## [1] 99

【讨论】:

确实更干净,我真的很喜欢这个对象不需要存在。主要问题是,当我的目标是进行具有副作用的分配时,您确实看起来像是在为对象 fooify 分配一个值,但我认为如果名称 fooify . 或 @ 可能是可以接受的987654328@ 添加了第二种方法。 太棒了!没想到这个运营商!或者假设我对*temp* 有同样的问题,如果我在它们的功能之外使用它,它不会与data.tabletidyverse 冲突? 您可能会遇到冲突。 dplyr 和 tidyr 实际上并没有在搜索路径上加载:=,即使它们允许使用它,所以在那里你应该没问题,但如果你加载 rlang,你可能会遇到冲突。我尝试在全局环境中使用 := 运行 data.table ,当我加载 data.table 时它抱怨冲突,但在 data.table 和我尝试过的我们自己的 := 中一切仍然有效。不过,如果您担心此类冲突,答案中的第一条会更安全。【参考方案2】:

我破解了它,虽然我不明白我所做的一切。

我注意到pryr::address 给出的结果与tracemem 不同,并尝试了它(我必须深入研究代码才能使用pryr:::address2,因为pryr::address 没有环境参数)。

然后我注意到,将 x 上的 tracemempryr:::address2 的结果混合在其余对象上,有一个匹配项(在基本重新格式化之后):

`(<-` <- function(x,value)
  pf <- parent.frame()
  all_addresses       <- sapply(ls(pf), pryr:::address2, pf)
  all_addresses       <- all_addresses[names(all_addresses) != "*tmp*"]
  all_addresses_short <- gsub("(^|<)[0x]*(.*?)(>|$)","\\2",all_addresses)

  x_address       <- tracemem(x)
  x_address_short <- tolower(gsub("(^|<)[0x]*(.*?)(>|$)","\\2",x_address))

  ind    <- match(x_address_short, all_addresses_short)
  x_name <- names(all_addresses)[ind]

  message("all_addresses, using pryr::address2")
  print(all_addresses)
  print(all_addresses_short)

  message("x_address, using tracemem")
  print(x_address)
  print(x_address_short)

  message("x_name, matching substrings")
  print(x_name)

  value

gsub 调用中使用的正则表达式试图解释我们在不同系统中获得的地址格式,我不能 100% 确定它是通用的。

输出:

foo <- 1
bar <- 2
(foo) <- foo

# all_addresses, using pryr::address2
# (<-          bar          foo 
# "0x1433df50" "0x14937678" "0x14937708" 
# (<-        bar        foo 
# "1433df50" "14937678" "14937708" 
# x_address, using tracemem
# [1] "<0000000014937708>"
# [1] "14937708"
# x_name, matching substrings
# [1] "foo"

如果 x 不是变量名则中断,例如:

foo <- iris
(foo$species) <- 3

我们可以假设如果找不到地址 x 是一个列表项,然后在 parent.frame 中的所有列表项的地址中查找它的地址(递归),但我认为这是今天的丑陋技巧已经够多了。

【讨论】:

以上是关于定义 `(<-` 运算符时获取 x 的名称的主要内容,如果未能解决你的问题,请参考以下文章

R 写作风格 - 要求 vs. ::

数据类型转换

四则运算林正扬

获取数组中的游戏对象名称

JavaScript使用构造函数获取变量的类型名

JavaScript使用构造函数获取变量的类型名