如何访问分配给函数内函数结果的变量名称?

时间:2017-09-13 18:54:38

标签: r variable-assignment operator-keyword

例如,假设我希望能够定义一个函数,该函数返回与第一个参数连接的赋值变量的名称:

a <- add_str("b")
a
# "ab"

上面示例中的函数如下所示:

add_str <- function(x) {
  arg0 <- as.list(match.call())[[1]]
  return(paste0(arg0, x))
}

但是函数的arg0行被一行替换,该行将获得被赋值变量的名称(“a”)而不是函数的名称。

我已经尝试过使用match.call和sys.call,但是我无法让它工作。这里的想法是在变量和函数结果上调用赋值运算符,因此应该是函数调用的父调用。

4 个答案:

答案 0 :(得分:7)

我认为这并不是严格可行的,正如其他解决方案所解释的那样,合理的选择可能是Yosi的答案。

但是我们可以从一些想法中获得乐趣,从简单开始,逐渐变得疯狂。


1-定义外观相似的中缀运算符

`%<-add_str%` <- function(e1, e2) {
  e2_ <- e2
  e1_ <- as.character(substitute(e1))
  eval.parent(substitute(e1 <- paste0(e1_,e2_)))
}

a %<-add_str% "b" 
a
# "ab"

2-重新定义:=,以便它通过..lhs()函数向rhs提供lhs的名称

我认为这是我最喜欢的选项:

`:=` <- function(lhs,rhs){
  lhs_name <- as.character(substitute(lhs))
  assign(lhs_name,eval(substitute(rhs)), envir = parent.frame())
  lhs
}

..lhs <- function(){
  eval.parent(quote(lhs_name),2)
}

add_str <- function(x){
  res <- paste0(..lhs(),x)
  res
}

a := add_str("b")
a
# [1] "ab"

也许可以基于此方法重新定义<-,但是由于递归问题,我无法弄清楚。


3-使用内存地址暗黑魔法来搜寻lhs(如果存在)

这直接来自:Get name of x when defining `(<-` operator

为此,我们需要稍稍更改语法并定义函数fetch_name,该函数才能从*<-函数中获取rhs的名称,其中{{1} }会返回as.character(substitute(lhs))

"*tmp*"

4-使用fetch_name <- function(x,env = parent.frame(2)) { all_addresses <- sapply(ls(env), pryr:::address2, env) all_addresses <- all_addresses[names(all_addresses) != "*tmp*"] all_addresses_short <- gsub("(^|<)[0x]*(.*?)(>|$)","\\2",all_addresses) x_address <- tracemem(x) untracemem(x) x_address_short <- tolower(gsub("(^|<)[0x]*(.*?)(>|$)","\\2",x_address)) ind <- match(x_address_short, all_addresses_short) x_name <- names(all_addresses)[ind] x_name } `add_str<-` <- function(x,value){ x_name <- fetch_name(x) paste0(x_name,value) } a <- NA add_str(a) <- "b" a 的后者的变体:

.Last.value

操作不必排在同一行,但它们需要彼此遵循。


5-还是一种变体,使用打印方法骇客

极度肮脏和令人费解,以取悦被折磨的精神并拖曳其他灵魂。

这是唯一能真正提供预期输出的工具,但它只能在交互模式下工作。

诀窍在于,除了执行第一个操作中的所有工作外,我还使用第二个(打印)。因此,在第一步中,我返回一个值为add_str <- function(value){ x_name <- fetch_name(.Last.value) assign(x_name,paste0(x_name,value),envir = parent.frame()) paste0(x_name,value) } a <- NA;add_str("b") a # [1] "ab" 的对象,但我还为其分配了一个类"b"和一个打印方法,然后该打印方法修改该对象的值,重置其类,并自毁。

"weird"

add_str <- function(x){ class(x) <- "weird" assign("print.weird", function(x) { env <- parent.frame(2) x_name <- fetch_name(x, env) assign(x_name,paste0(x_name,unclass(x)),envir = env) rm(print.weird,envir = env) print(paste0(x_name,x)) },envir = parent.frame()) x } a <- add_str("b") a # [1] "ab" 将具有与上述两行相同的效果。 (a <- add_str("b")也具有相同的效果,但也可以在非交互式代码中使用。

答案 1 :(得分:4)

这通常是不可能的,因为实际上将运算符<-解析为对<-函数的调用:

rapply(as.list(quote(a <- add_str("b"))), 
       function(x) if (!is.symbol(x)) as.list(x) else x,
       how = "list")
#[[1]]
#`<-`
#
#[[2]]
#a
#
#[[3]]
#[[3]][[1]]
#add_str
#
#[[3]][[2]]
#[1] "b"

现在,您可以通过向sys.call传递负数(例如,

)来 访问呼叫堆栈上的较早呼叫
 foo <- function() {
  inner <- sys.call()
  outer <- sys.call(-1)
  list(inner, outer)
}

print(foo())
#[[1]]
#foo()
#[[2]]
#print(foo())

但是,help("sys.call")这样说(强调我的意思):

  

严格来说,sys.parent和parent.frame是指   父解释函数。因此,内部功能(可能或可能   未设置上下文,因此或可能不会出现在通话堆栈中)   S3方法也可以做令人惊讶的事情。

<-就是这样的“内部功能”:

`<-`
#.Primitive("<-")

`<-`(x, foo())
x
#[[1]]
#foo()
#
#[[2]]
#NULL

答案 2 :(得分:3)

我认为该函数无权访问分配给它的变量。它不在函数范围之内,并且您不会传递任何指针或以任何方式指定它。如果要将其指定为参数,则可以执行以下操作:

add_str <- function(x, y) {
  arg0 <-deparse(substitute(x))
  return(paste0(arg0, y))
}

a <- 5
add_str(a, 'b')
#"ab"

答案 3 :(得分:3)

正如Roland所指出的,<-在函数范围之外,只能在查看函数调用堆栈的位置找到,但这会失败。因此,可能的解决方案可能是重新定义'<-',而不是将其重新定义为原语,或者更好的是,定义可以完成相同工作的事物以及其他事物。 我不知道以下代码背后的想法是否可以满足您的需求,但是您可以定义“详细分配”:

`:=` <- function (var, value) 
{
    call = as.list(match.call())
    message(sprintf("Assigning %s to %s.\n",deparse(call$value),deparse(call$var)))
    eval(substitute(var <<- value))
    return(invisible(value))
 }

x := 1:10
# Assigning 1:10 to x.
x
# [1]  1  2  3  4  5  6  7  8  9 10

它可以在其他情况下使用,其中'<-'并不是真正的赋值:

y <- data.frame(c=1:3)
colnames(y) := "b"
# Assigning "b" to colnames(y).
y
#  b
#1 1
#2 2
#3 3

z <- 1:4
dim(z) := c(2,2)
#Assigning c(2, 2) to dim(z).
z
#     [,1] [,2]
#[1,]    1    3
#[2,]    2    4

>