例如,假设我希望能够定义一个函数,该函数返回与第一个参数连接的赋值变量的名称:
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,但是我无法让它工作。这里的想法是在变量和函数结果上调用赋值运算符,因此应该是函数调用的父调用。
答案 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
>