Добавить объект в список в R в амортизированном постоянном времени, O (1)? - PullRequest
233 голосов
/ 13 марта 2010

Если у меня есть список 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, чтобы выйти за пределы вашей функции и видоизменить вызывающую среду, но это кажется большим молотком для написания простой функции добавления.

Может кто-нибудь предложить более красивый способ сделать это? Бонусные баллы, если он работает как для векторов, так и для списков.

Ответы [ 17 ]

2 голосов
/ 24 августа 2011

Я думаю, что вы хотите сделать на самом деле передать по ссылке (указателю) на функцию - создать новую среду (которая передается по ссылке на функции) с добавленным в нее списком:

listptr=new.env(parent=globalenv())
listptr$list=mylist

#Then the function is modified as:
lPtrAppend <- function(lstptr, obj) {
    lstptr$list[[length(lstptr$list)+1]] <- obj
}

Теперь вы только изменяете существующий список (не создавая новый)

2 голосов
/ 13 марта 2010

Это простой способ добавить элементы в список R:

# create an empty list:
small_list = list()

# now put some objects in it:
small_list$k1 = "v1"
small_list$k2 = "v2"
small_list$k3 = 1:10

# retrieve them the same way:
small_list$k1
# returns "v1"

# "index" notation works as well:
small_list["k2"]

Или программно:

kx = paste(LETTERS[1:5], 1:5, sep="")
vx = runif(5)
lx = list()
cn = 1

for (itm in kx) { lx[itm] = vx[cn]; cn = cn + 1 }

print(length(lx))
# returns 5
1 голос
/ 25 июня 2018

Для проверки я запустил тестовый код, предоставленный @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
        }
)
1 голос
/ 01 марта 2018

Существует также list.append из rlist ( ссылка на документацию )

require(rlist)
LL <- list(a="Tom", b="Dick")
list.append(LL,d="Pam",f=c("Joe","Ann"))

Это очень просто и эффективно.

0 голосов
/ 26 ноября 2018

mylist<-list(1,2,3) mylist<-c(mylist,list(5))

Таким образом, мы можем легко добавить элемент / объект, используя приведенный выше код

0 голосов
/ 14 апреля 2016

Это очень интересный вопрос, и я надеюсь, что мои размышления, приведенные ниже, могут помочь найти способ его решения. Этот метод дает плоский список без индексации, но у него есть списки и списки, чтобы избежать структур вложенности. Я не уверен насчет скорости, так как не знаю, как это сделать.

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
0 голосов
/ 24 февраля 2011
> LL<-list(1:4)

> LL

[[1]]
[1] 1 2 3 4

> LL<-list(c(unlist(LL),5:9))

> LL

[[1]]
 [1] 1 2 3 4 5 6 7 8 9
...