还在试着进入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

——被编辑来回答谢恩的问题——

我真的不需要给结果值部分命名。我将一个聚合函数应用于第一个组件,另一个应用于第二个组件(最小值和最大值)。如果这两个组件是相同的函数,我就不需要拆分它们了)。


当前回答

2021年,我经常用这个词。

Tidyverse包有一个名为LST的函数,它在创建列表时为列表元素分配名称。 Post,我使用list2env()来分配变量或直接使用列表

library(tidyverse)
fun <- function(){
  a<-1
  b<-2
  lst(a,b)
}
list2env(fun(), envir=.GlobalEnv)#unpacks list key-values to variable-values into the current environment

其他回答

(一) 如果foo和bar都是一个数字,那么c(foo,bar)没有问题;你也可以命名这些组件:c(Foo= Foo,Bar= Bar)。所以你可以访问结果res的组件res[1] res[2];或在命名的情况下,如res["Foo"], res["BAR"]。

[B] 如果foo和bar是相同类型和长度的向量,那么返回cbind(foo,bar)或rbind(foo,bar)也没有问题;同样值得注意的。在'cbind'情况下,您将访问foo和bar为res[,1], res[,2]或res[," foo "], res[," bar "]。你可能更喜欢返回一个数据框架而不是一个矩阵:

data.frame(Foo=foo,Bar=bar)

并访问它们为res$Foo res$Bar如果foo和bar的长度相同但类型不同(例如,foo是一个数字向量,bar是一个字符串向量),这也可以很好地工作。

[C] 如果foo和bar完全不同,不能像上面那样方便地组合,那么你肯定应该返回一个列表。

例如,你的函数可能适合线性模型 还要计算预测值,这样你就可以

LM<-lm(....) ; foo<-summary(LM); bar<-LM$fit

然后你会返回list(Foo= Foo, Bar= Bar),然后访问摘要res$Foo,预测值res$Bar

来源:http://r.789695.n4.nabble.com/How-to-return-multiple-values-in-a-function-td858528.html

通常我把输出包装成一个列表,这是非常灵活的(你可以有任何组合的数字,字符串,向量,矩阵,数组,列表,对象输出)

就像:

func2<-function(input) {
   a<-input+1
   b<-input+2
   output<-list(a,b)
   return(output)
}

output<-func2(5)

for (i in output) {
   print(i)
}

[1] 6
[1] 7

我组装了一个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

这只是为了完整,而不是因为我个人喜欢它。你可以用管道%>%输出结果,用花括号{}求值,用双箭头<<-将变量写入父环境。

library(tidyverse)
functionReturningTwoValues() %>% {a <<- .[1]; b <<- .[2]}

更新: 您还可以使用zeallot包中的多重赋值操作符::%<-%

c(a, b) %<-% list(0, 1)