如果我有一些R列表mylist,你可以像这样添加一个obj项:

mylist[[length(mylist)+1]] <- obj

但肯定有一些更紧凑的方式。当我刚在R工作时,我试着像这样写lappend():

lappend <- function(lst, obj) {
    lst[[length(lst)+1]] <- obj
    return(lst)
}

但是,由于R的按名调用语义(lst在调用时被有效地复制,因此对lst的更改在lappend()的作用域之外是不可见的),这当然是行不通的。我知道您可以在R函数中进行环境入侵,从而超出函数的作用域并改变调用环境,但对于编写一个简单的附加函数来说,这似乎是一个巨大的打击。

有谁能提出一个更漂亮的方法吗?如果它对向量和列表都适用,那就更好了。


当前回答

为了验证,我运行了@Cron提供的基准测试代码。有一个主要的区别(除了在更新的i7处理器上运行更快):by_index现在的性能几乎和list_一样好:

Unit: milliseconds
              expr        min         lq       mean     median         uq
    env_with_list_ 167.882406 175.969269 185.966143 181.817187 185.933887
                c_ 485.524870 501.049836 516.781689 518.637468 537.355953
             list_   6.155772   6.258487   6.544207   6.269045   6.290925
          by_index   9.290577   9.630283   9.881103   9.672359  10.219533
           append_ 505.046634 543.319857 542.112303 551.001787 553.030110
 env_as_container_ 153.297375 154.880337 156.198009 156.068736 156.800135

这里是从@Cron的回答中逐字复制的基准代码(以防他后来更改内容):

n = 1e+4
library(microbenchmark)
### Using environment as a container
lPtrAppend <- function(lstptr, lab, obj) {lstptr[[deparse(substitute(lab))]] <- obj}
### Store list inside new environment
envAppendList <- function(lstptr, obj) {lstptr$list[[length(lstptr$list)+1]] <- obj}

microbenchmark(times = 5,
        env_with_list_ = {
            listptr <- new.env(parent=globalenv())
            listptr$list <- NULL
            for(i in 1:n) {envAppendList(listptr, i)}
            listptr$list
        },
        c_ = {
            a <- list(0)
            for(i in 1:n) {a = c(a, list(i))}
        },
        list_ = {
            a <- list(0)
            for(i in 1:n) {a <- list(a, list(i))}
        },
        by_index = {
            a <- list(0)
            for(i in 1:n) {a[length(a) + 1] <- i}
            a
        },
        append_ = {
            a <- list(0)
            for(i in 1:n) {a <- append(a, i)}
            a
        },
        env_as_container_ = {
            listptr <- new.env(parent=globalenv())
            for(i in 1:n) {lPtrAppend(listptr, i, i)}
            listptr
        }
)

其他回答

如果它是一个字符串列表,只需使用c()函数:

R> LL <- list(a="tom", b="dick")
R> c(LL, c="harry")
$a
[1] "tom"

$b
[1] "dick"

$c
[1] "harry"

R> class(LL)
[1] "list"
R> 

这也适用于向量,我得到额外的分数了吗?

编辑(2015-02-01):这篇文章即将迎来它的第五个生日。一些好心的读者一直在重复它的缺点,所以无论如何也要看看下面的一些评论。关于列表类型的一个建议:

newlist <- list(oldlist, list(someobj))

一般来说,R类型使得所有类型和用途都很难有一个或只有一个习语。

如果你将列表变量作为一个带引号的字符串传入,你可以像这样从函数内部到达它:

push <- function(l, x) {
  assign(l, append(eval(as.name(l)), x), envir=parent.frame())
}

so:

> a <- list(1,2)
> a
[[1]]
[1] 1

[[2]]
[1] 2

> push("a", 3)
> a
[[1]]
[1] 1

[[2]]
[1] 2

[[3]]
[1] 3

> 

或者为了获得额外的学分:

> v <- vector()
> push("v", 1)
> v
[1] 1
> push("v", 2)
> v
[1] 1 2
> 

事实上,c()函数有一个微妙之处。如果你有:

x <- list()
x <- c(x,2)
x = c(x,"foo")

如你所料,你将获得:

[[1]]
[1]

[[2]]
[1] "foo"

但是如果你添加一个x <- c(x, matrix(5,2,2))的矩阵,你的列表将有另外4个值为5的元素! 你最好做:

x <- c(x, list(matrix(5,2,2))

它适用于任何其他对象,你将获得预期的:

[[1]]
[1]

[[2]]
[1] "foo"

[[3]]
     [,1] [,2]
[1,]    5    5
[2,]    5    5

最后,你的函数变成:

push <- function(l, ...) c(l, list(...))

它适用于任何类型的对象。你可以更聪明地去做:

push_back <- function(l, ...) c(l, list(...))
push_front <- function(l, ...) c(list(...), l)

这是一个非常有趣的问题,我希望我下面的想法可以为解决这个问题提供一种方式。这个方法给出了一个没有索引的平面列表,但是它有列表和反列表来避免嵌套结构。我不确定速度,因为我不知道如何基准。

a_list<-list()
for(i in 1:3){
  a_list<-list(unlist(list(unlist(a_list,recursive = FALSE),list(rnorm(2))),recursive = FALSE))
}
a_list

[[1]]
[[1]][[1]]
[1] -0.8098202  1.1035517

[[1]][[2]]
[1] 0.6804520 0.4664394

[[1]][[3]]
[1] 0.15592354 0.07424637

试试这个函数lappend

lappend <- function (lst, ...){
  lst <- c(lst, list(...))
  return(lst)
}

将命名向量添加到列表中

Bye.