还在试着进入R逻辑…什么是解包(在LHS上)一个函数返回多个值的结果的“最佳”方法?
显然我不能这么做:
R> functionReturningTwoValues <- function() { return(c(1, 2)) }
R> functionReturningTwoValues()
[1] 1 2
R> a, b <- functionReturningTwoValues()
Error: unexpected ',' in "a,"
R> c(a, b) <- functionReturningTwoValues()
Error in c(a, b) <- functionReturningTwoValues() : object 'a' not found
我真的必须做以下事情吗?
R> r <- functionReturningTwoValues()
R> a <- r[1]; b <- r[2]
或者R程序员会这样写:
R> functionReturningTwoValues <- function() {return(list(first=1, second=2))}
R> r <- functionReturningTwoValues()
R> r$first
[1] 1
R> r$second
[1] 2
——被编辑来回答谢恩的问题——
我真的不需要给结果值部分命名。我将一个聚合函数应用于第一个组件,另一个应用于第二个组件(最小值和最大值)。如果这两个组件是相同的函数,我就不需要拆分它们了)。
(1)列表[…我十多年前就在r-help上发过这篇文章。从那时起,它被添加到gsubfn包中。它不需要一个特殊的操作符,但要求左边使用list[…]像这样:
library(gsubfn) # need 0.7-0 or later
list[a, b] <- functionReturningTwoValues()
如果你只需要第一个或第二个组件,这些也可以工作:
list[a] <- functionReturningTwoValues()
list[a, ] <- functionReturningTwoValues()
list[, b] <- functionReturningTwoValues()
(当然,如果你只需要一个值,那么functionReturningTwoValues()[[1]]或functionReturningTwoValues()[[2]]就足够了。)
有关更多示例,请参阅引用的r-help线程。
如果意图仅仅是随后组合多个值,并且返回值被命名,那么一个简单的替代方法是使用with:
myfun <- function() list(a = 1, b = 2)
list[a, b] <- myfun()
a + b
# same
with(myfun(), a + b)
另一种替代方法是attach:
attach(myfun())
a + b
附加:与和附加
我组装了一个R包热机来解决这个问题。Zeallot包含一个多重赋值或解包赋值操作符%<-%。运算符的LHS是要赋值的任意数量的变量,通过调用c()构建。操作符的RHS是一个向量、列表、数据帧、日期对象或任何具有已实现的解构方法的自定义对象(参见?zeallot::destructure)。
以下是一些基于原始帖子的例子,
library(zeallot)
functionReturningTwoValues <- function() {
return(c(1, 2))
}
c(a, b) %<-% functionReturningTwoValues()
a # 1
b # 2
functionReturningListOfValues <- function() {
return(list(1, 2, 3))
}
c(d, e, f) %<-% functionReturningListOfValues()
d # 1
e # 2
f # 3
functionReturningNestedList <- function() {
return(list(1, list(2, 3)))
}
c(f, c(g, h)) %<-% functionReturningNestedList()
f # 1
g # 2
h # 3
functionReturningTooManyValues <- function() {
return(as.list(1:20))
}
c(i, j, ...rest) %<-% functionReturningTooManyValues()
i # 1
j # 2
rest # list(3, 4, 5, ..)
有关更多信息和示例,请查看软件包插图。
这个问题没有正确答案。这取决于你如何处理数据。在上面的简单例子中,我强烈建议:
让事情尽可能简单。
在任何可能的情况下,保持函数向量化是一个最佳实践。从长远来看,这提供了最大程度的灵活性和速度。
上面的值1和2有名字很重要吗?换句话说,为什么在这个例子中,1和2被命名为a和b很重要,而不是r[1]和r[2]?在这种情况下需要理解的一件重要的事情是a和b也是长度为1的向量。所以在赋值的过程中你并没有改变任何东西,除了有两个不需要下标引用的新向量:
> r <- c(1,2)
> a <- r[1]
> b <- r[2]
> class(r)
[1] "numeric"
> class(a)
[1] "numeric"
> a
[1] 1
> a[1]
[1] 1
如果你更愿意引用字母而不是索引,你也可以将名称分配给原始向量:
> names(r) <- c("a","b")
> names(r)
[1] "a" "b"
> r["a"]
a
1
[编辑]假设你将分别对每个向量应用min和max,我建议要么使用矩阵(如果a和b将是相同的长度和相同的数据类型),要么使用数据帧(如果a和b将是相同的长度,但可以是不同的数据类型),要么使用像你上一个例子中那样的列表(如果它们可以是不同的长度和数据类型)。
> r <- data.frame(a=1:4, b=5:8)
> r
a b
1 1 5
2 2 6
3 3 7
4 4 8
> min(r$a)
[1] 1
> max(r$b)
[1] 8
我在网上偶然发现了这个聪明的黑客…我不确定它是令人讨厌的还是漂亮的,但它允许您创建一个“神奇的”操作符,允许您将多个返回值解压缩到它们自己的变量中。:=函数定义在这里,并在后代中包含:
':=' <- function(lhs, rhs) {
frame <- parent.frame()
lhs <- as.list(substitute(lhs))
if (length(lhs) > 1)
lhs <- lhs[-1]
if (length(lhs) == 1) {
do.call(`=`, list(lhs[[1]], rhs), envir=frame)
return(invisible(NULL))
}
if (is.function(rhs) || is(rhs, 'formula'))
rhs <- list(rhs)
if (length(lhs) > length(rhs))
rhs <- c(rhs, rep(list(NULL), length(lhs) - length(rhs)))
for (i in 1:length(lhs))
do.call(`=`, list(lhs[[i]], rhs[[i]]), envir=frame)
return(invisible(NULL))
}
有了它,你可以做你想做的事情:
functionReturningTwoValues <- function() {
return(list(1, matrix(0, 2, 2)))
}
c(a, b) := functionReturningTwoValues()
a
#[1] 1
b
# [,1] [,2]
# [1,] 0 0
# [2,] 0 0
我不知道我是怎么想的。也许你会发现它对你的交互式工作空间很有帮助。使用它来构建(可重用的)可用的库(用于大众消费)可能不是最好的主意,但我想这取决于您。
... 你知道人们是怎么说责任和权力的吗?