Добавить объект в список в R в амортизированном постоянном времени, O (1)?
Если у меня есть список 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-функции, чтобы выйти за пределы вашей функции и мутировать вызывающую среду, но это похоже на большой молот, чтобы написать простую функцию append.
Может ли кто-нибудь предложить более красивый способ сделать это? Бонусные очки, если он работает как для векторов, так и для списков.
Ответы
Ответ 1
Если это список строк, просто используйте функцию 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>
Это тоже работает на векторах, поэтому я получаю бонусные очки?
Edit (2015-Feb-01): Этот пост подходит к пятому юбилею. Некоторые читатели продолжают повторять с ним какие-либо недостатки, поэтому обязательно ознакомьтесь с некоторыми комментариями ниже. Одно предложение для типов list
:
newlist <- list(oldlist, list(someobj))
В общем, типы R могут затруднить наличие одной и только одной идиомы для всех типов и использования.
Ответ 2
OP (в обновленной редакции вопроса в апреле 2012 года) интересуется тем, есть ли способ добавить к списку в амортизированное постоянное время, например, можно сделать, например, с контейнером С++ vector<>
, Лучший ответ (s?) Здесь пока показывает только относительное время выполнения для различных решений с учетом проблемы фиксированного размера, но не относится к алгоритму эффективности напрямую. Комментарии ниже многих ответов обсуждают алгоритмическую эффективность некоторых решений, но в каждом случае на сегодняшний день (по состоянию на апрель 2015 года) они пришли к неверному завершению.
Алгоритмическая эффективность фиксирует характеристики роста как во времени (время выполнения), так и в пространстве (объем потребляемой памяти) по мере увеличения размера проблемы. Выполнение теста производительности для различных решений с учетом проблемы фиксированного размера не учитывает темпы роста различных решений. OP заинтересован в том, чтобы узнать, есть ли способ добавить объекты к списку R в "амортизированное постоянное время". Что это значит? Чтобы объяснить, сначала позвольте мне описать "постоянное время":
-
Постоянный или O (1) рост:
Если время, требуемое для выполнения заданной задачи, остается таким же, как размер проблемы удваивается, то мы говорим, что алгоритм демонстрирует постоянный рост времени или указан в нотации "Big O", показывает рост времени O (1). Когда ОП говорит "амортизированное" постоянное время, он просто означает "в конечном счете"... т.е. Если выполнение одной операции иногда занимает намного больше времени, чем обычно (например, если предварительно выделенный буфер исчерпан и иногда требует изменения размера до большего размер буфера), если долгосрочная средняя производительность является постоянным временем, мы все равно будем называть ее O (1).
Для сравнения я также опишу "линейное время" и "квадратичное время":
-
Линейный или O (n) рост:
Если время, требуемое для выполнения заданной задачи, удваивается по мере удвоения размера проблемы, то мы говорим, что алгоритм имеет линейное время или рост O (n).
-
Квадратичный или O (n 2) рост:
Если время, необходимое для выполнения заданной задачи, увеличивается на квадрат размера задачи, то мы говорим, что алгоритм имеет квадратичное время или рост O (n 2).
Существует много других классов эффективности алгоритмов; Я откладываю статью Википедии для дальнейшего обсуждения.
Я благодарю @CronAcronis за его ответ, поскольку я новичок в R, и было неплохо иметь полностью построенный блок кода для выполнения анализа производительности различных решений, представленных на этой странице. Я заимствую его код для моего анализа, который я дублирую (завернутый в функцию) ниже:
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}
runBenchmark <- function(n) {
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
}
)
}
Результаты, опубликованные @CronAcronis, определенно показывают, что метод a <- list(a, list(i))
является самым быстрым, по крайней мере, для размера проблемы 10000, но результаты для одного размера проблемы не учитывают рост решения. Для этого нам нужно выполнить минимум два теста профилирования с разными размерами проблем:
> runBenchmark(2e+3)
Unit: microseconds
expr min lq mean median uq max neval
env_with_list_ 8712.146 9138.250 10185.533 10257.678 10761.33 12058.264 5
c_ 13407.657 13413.739 13620.976 13605.696 13790.05 13887.738 5
list_ 854.110 913.407 1064.463 914.167 1301.50 1339.132 5
by_index 11656.866 11705.140 12182.104 11997.446 12741.70 12809.363 5
append_ 15986.712 16817.635 17409.391 17458.502 17480.55 19303.560 5
env_as_container_ 19777.559 20401.702 20589.856 20606.961 20939.56 21223.502 5
> runBenchmark(2e+4)
Unit: milliseconds
expr min lq mean median uq max neval
env_with_list_ 534.955014 550.57150 550.329366 553.5288 553.955246 558.636313 5
c_ 1448.014870 1536.78905 1527.104276 1545.6449 1546.462877 1558.609706 5
list_ 8.746356 8.79615 9.162577 8.8315 9.601226 9.837655 5
by_index 953.989076 1038.47864 1037.859367 1064.3942 1065.291678 1067.143200 5
append_ 1634.151839 1682.94746 1681.948374 1689.7598 1696.198890 1706.683874 5
env_as_container_ 204.134468 205.35348 208.011525 206.4490 208.279580 215.841129 5
>
Прежде всего, слово о значении min/lq/mean/median/uq/max: поскольку мы выполняем ту же самую задачу для каждого из 5 прогонов в идеальном мире, мы могли бы ожидать, что это будет ровно столько же времени для каждого прогона. Но первый запуск обычно предвзято к более длительным временам из-за того, что тестируемый код еще не загружен в кеш процессора. После первого запуска мы ожидаем, что время будет достаточно последовательным, но иногда наш код может быть выведен из кеша из-за прерываний таймера или других аппаратных прерываний, которые не связаны с тестируемым кодом. Проверяя фрагменты кода 5 раз, мы разрешаем загрузку кода в кеш во время первого запуска, а затем предоставляем каждому фрагменту 4 возможности выполнить до конца без помех от внешних событий. По этой причине и потому, что каждый раз мы выполняем один и тот же код в тех же самых входных условиях, мы будем рассматривать только "минимальные" времена для наилучшего сравнения между различными вариантами кода.
Обратите внимание, что я решил сначала запустить с размером проблемы 2000, а затем 20000, поэтому размер моей проблемы увеличился в 10 раз от первого запуска до второго.
Производительность решения list
: O (1) (постоянное время)
Сначала рассмотрим рост решения list
, так как сразу можно сказать, что это самое быстрое решение в обоих профайлингах: в первом прогоне потребовалось 854 микро секунд ( 0.854 милли секунд) для выполнения 2000 задач "добавить". Во втором прогоне потребовалось 8,746 миллисекунды для выполнения 20000 задач "добавить". Наивный наблюдатель сказал бы: "Ах, решение list
демонстрирует рост O (n), поскольку, поскольку размер проблемы рос в десять раз, так же как и время, необходимое для выполнения теста". Проблема с этим анализом заключается в том, что то, что хочет OP, - это темп роста вставки одного объекта, а не темпы роста общей проблемы. Зная это, ясно, что решение list
обеспечивает именно то, что хочет OP: метод добавления объектов в список в O (1) время.
Производительность других решений
Ни одно из других решений не приближается к скорости решения list
, но в любом случае полезно исследовать их:
Большинство других решений выглядят как O (n) в производительности. Например, решение by_index
, очень популярное решение, основанное на частоте, с которой я нахожу его в других сообщениях SO, заняло 11,6 миллисекунды, чтобы добавить 2000 объектов, и 953 миллисекунды, чтобы добавить в десять раз больше многих объектов. Общее время проблемы увеличилось в 100 раз, поэтому наивный наблюдатель мог бы сказать: "Ах, решение by_index
имеет рост O (n 2), поскольку, поскольку размер проблемы вырос в десять, время, необходимое для выполнения теста, выросло в 100 раз". Как и раньше, этот анализ является ошибочным, поскольку ОП заинтересован в росте вставки одного объекта. Если мы разделим общий рост времени на рост размера проблемы, мы обнаружим, что рост времени добавления объектов увеличился в 10 раз, а не в 100 раз, что соответствует росту размера проблемы, поэтому by_index
решение O (n). В списке нет решений, которые показывают рост O (n 2) для добавления одного объекта.
Ответ 3
В других ответах только подход list
приводит к добавлению O (1), но он приводит к глубоко вложенной структуре списка, а не простому одиночному списку. Я использовал приведенные ниже структуры данных, они поддерживают добавление O (1) (амортизируется) и позволяют вернуть результат в простой список.
expandingList <- function(capacity = 10) {
buffer <- vector('list', capacity)
length <- 0
methods <- list()
methods$double.size <- function() {
buffer <<- c(buffer, vector('list', capacity))
capacity <<- capacity * 2
}
methods$add <- function(val) {
if(length == capacity) {
methods$double.size()
}
length <<- length + 1
buffer[[length]] <<- val
}
methods$as.list <- function() {
b <- buffer[0:length]
return(b)
}
methods
}
и
linkedList <- function() {
head <- list(0)
length <- 0
methods <- list()
methods$add <- function(val) {
length <<- length + 1
head <<- list(head, val)
}
methods$as.list <- function() {
b <- vector('list', length)
h <- head
for(i in length:1) {
b[[i]] <- head[[2]]
head <- head[[1]]
}
return(b)
}
methods
}
Используйте их следующим образом:
> l <- expandingList()
> l$add("hello")
> l$add("world")
> l$add(101)
> l$as.list()
[[1]]
[1] "hello"
[[2]]
[1] "world"
[[3]]
[1] 101
Эти решения могут быть расширены в полные объекты, которые сами по себе поддерживают все связанные с списком операции, но это останется как упражнение для читателя.
Другой вариант для именованного списка:
namedExpandingList <- function(capacity = 10) {
buffer <- vector('list', capacity)
names <- character(capacity)
length <- 0
methods <- list()
methods$double.size <- function() {
buffer <<- c(buffer, vector('list', capacity))
names <<- c(names, character(capacity))
capacity <<- capacity * 2
}
methods$add <- function(name, val) {
if(length == capacity) {
methods$double.size()
}
length <<- length + 1
buffer[[length]] <<- val
names[length] <<- name
}
methods$as.list <- function() {
b <- buffer[0:length]
names(b) <- names[0:length]
return(b)
}
methods
}
Бенчмарки
Сравнение производительности с использованием кода @phonetagger (который основан на коде @Cron Arconis). Я также добавил better_env_as_container
и немного изменил env_as_container_
. Оригинал env_as_container_
был сломан и фактически не хранит все числа.
library(microbenchmark)
lPtrAppend <- function(lstptr, lab, obj) {lstptr[[deparse(lab)]] <- obj}
### Store list inside new environment
envAppendList <- function(lstptr, obj) {lstptr$list[[length(lstptr$list)+1]] <- obj}
env2list <- function(env, len) {
l <- vector('list', len)
for (i in 1:len) {
l[[i]] <- env[[as.character(i)]]
}
l
}
envl2list <- function(env, len) {
l <- vector('list', len)
for (i in 1:len) {
l[[i]] <- env[[paste(as.character(i), 'L', sep='')]]
}
l
}
runBenchmark <- function(n) {
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(hash=TRUE, parent=globalenv())
for(i in 1:n) {lPtrAppend(listptr, i, i)}
envl2list(listptr, n)
},
better_env_as_container = {
env <- new.env(hash=TRUE, parent=globalenv())
for(i in 1:n) env[[as.character(i)]] <- i
env2list(env, n)
},
linkedList = {
a <- linkedList()
for(i in 1:n) { a$add(i) }
a$as.list()
},
inlineLinkedList = {
a <- list()
for(i in 1:n) { a <- list(a, i) }
b <- vector('list', n)
head <- a
for(i in n:1) {
b[[i]] <- head[[2]]
head <- head[[1]]
}
},
expandingList = {
a <- expandingList()
for(i in 1:n) { a$add(i) }
a$as.list()
},
inlineExpandingList = {
l <- vector('list', 10)
cap <- 10
len <- 0
for(i in 1:n) {
if(len == cap) {
l <- c(l, vector('list', cap))
cap <- cap*2
}
len <- len + 1
l[[len]] <- i
}
l[1:len]
}
)
}
# We need to repeatedly add an element to a list. With normal list concatenation
# or element setting this would lead to a large number of memory copies and a
# quadratic runtime. To prevent that, this function implements a bare bones
# expanding array, in which list appends are (amortized) constant time.
expandingList <- function(capacity = 10) {
buffer <- vector('list', capacity)
length <- 0
methods <- list()
methods$double.size <- function() {
buffer <<- c(buffer, vector('list', capacity))
capacity <<- capacity * 2
}
methods$add <- function(val) {
if(length == capacity) {
methods$double.size()
}
length <<- length + 1
buffer[[length]] <<- val
}
methods$as.list <- function() {
b <- buffer[0:length]
return(b)
}
methods
}
linkedList <- function() {
head <- list(0)
length <- 0
methods <- list()
methods$add <- function(val) {
length <<- length + 1
head <<- list(head, val)
}
methods$as.list <- function() {
b <- vector('list', length)
h <- head
for(i in length:1) {
b[[i]] <- head[[2]]
head <- head[[1]]
}
return(b)
}
methods
}
# We need to repeatedly add an element to a list. With normal list concatenation
# or element setting this would lead to a large number of memory copies and a
# quadratic runtime. To prevent that, this function implements a bare bones
# expanding array, in which list appends are (amortized) constant time.
namedExpandingList <- function(capacity = 10) {
buffer <- vector('list', capacity)
names <- character(capacity)
length <- 0
methods <- list()
methods$double.size <- function() {
buffer <<- c(buffer, vector('list', capacity))
names <<- c(names, character(capacity))
capacity <<- capacity * 2
}
methods$add <- function(name, val) {
if(length == capacity) {
methods$double.size()
}
length <<- length + 1
buffer[[length]] <<- val
names[length] <<- name
}
methods$as.list <- function() {
b <- buffer[0:length]
names(b) <- names[0:length]
return(b)
}
methods
}
результат:
> runBenchmark(1000)
Unit: microseconds
expr min lq mean median uq max neval
env_with_list_ 3128.291 3161.675 4466.726 3361.837 3362.885 9318.943 5
c_ 3308.130 3465.830 6687.985 8578.913 8627.802 9459.252 5
list_ 329.508 343.615 389.724 370.504 449.494 455.499 5
by_index 3076.679 3256.588 5480.571 3395.919 8209.738 9463.931 5
append_ 4292.321 4562.184 7911.882 10156.957 10202.773 10345.177 5
env_as_container_ 24471.511 24795.849 25541.103 25486.362 26440.591 26511.200 5
better_env_as_container 7671.338 7986.597 8118.163 8153.726 8335.659 8443.493 5
linkedList 1700.754 1755.439 1829.442 1804.746 1898.752 1987.518 5
inlineLinkedList 1109.764 1115.352 1163.751 1115.631 1206.843 1271.166 5
expandingList 1422.440 1439.970 1486.288 1519.728 1524.268 1525.036 5
inlineExpandingList 942.916 973.366 1002.461 1012.197 1017.784 1066.044 5
> runBenchmark(10000)
Unit: milliseconds
expr min lq mean median uq max neval
env_with_list_ 357.760419 360.277117 433.810432 411.144799 479.090688 560.779139 5
c_ 685.477809 734.055635 761.689936 745.957553 778.330873 864.627811 5
list_ 3.257356 3.454166 3.505653 3.524216 3.551454 3.741071 5
by_index 445.977967 454.321797 515.453906 483.313516 560.374763 633.281485 5
append_ 610.777866 629.547539 681.145751 640.936898 760.570326 763.896124 5
env_as_container_ 281.025606 290.028380 303.885130 308.594676 314.972570 324.804419 5
better_env_as_container 83.944855 86.927458 90.098644 91.335853 92.459026 95.826030 5
linkedList 19.612576 24.032285 24.229808 25.461429 25.819151 26.223597 5
inlineLinkedList 11.126970 11.768524 12.216284 12.063529 12.392199 13.730200 5
expandingList 14.735483 15.854536 15.764204 16.073485 16.075789 16.081726 5
inlineExpandingList 10.618393 11.179351 13.275107 12.391780 14.747914 17.438096 5
> runBenchmark(20000)
Unit: milliseconds
expr min lq mean median uq max neval
env_with_list_ 1723.899913 1915.003237 1921.23955 1938.734718 1951.649113 2076.910767 5
c_ 2759.769353 2768.992334 2810.40023 2820.129738 2832.350269 2870.759474 5
list_ 6.112919 6.399964 6.63974 6.453252 6.910916 7.321647 5
by_index 2163.585192 2194.892470 2292.61011 2209.889015 2436.620081 2458.063801 5
append_ 2832.504964 2872.559609 2983.17666 2992.634568 3004.625953 3213.558197 5
env_as_container_ 573.386166 588.448990 602.48829 597.645221 610.048314 642.912752 5
better_env_as_container 154.180531 175.254307 180.26689 177.027204 188.642219 206.230191 5
linkedList 38.401105 47.514506 46.61419 47.525192 48.677209 50.952958 5
inlineLinkedList 25.172429 26.326681 32.33312 34.403442 34.469930 41.293126 5
expandingList 30.776072 30.970438 34.45491 31.752790 38.062728 40.712542 5
inlineExpandingList 21.309278 22.709159 24.64656 24.290694 25.764816 29.158849 5
Я добавил linkedList
и expandingList
и встроенную версию обоих. inlinedLinkedList
- это в основном копия list_
, но она также преобразует вложенную структуру обратно в простой список. Кроме того, разница между встроенной и неинтенсивной версиями связана с накладными вызовами функций.
Все варианты expandingList
и linkedList
показывают, что O (1) добавляет производительность с линейным масштабированием времени линейки с количеством добавленных элементов. linkedList
работает медленнее, чем expandingList
, а служебные данные вызова функции также видны. Поэтому, если вам действительно нужна вся скорость, которую вы можете получить (и хотите придерживаться R-кода), используйте встроенную версию expandingList
.
Я также посмотрел на реализацию C в R, и оба подхода должны быть O (1) добавлены для любого размера до тех пор, пока вы не исчерпаете память.
Я также изменил env_as_container_
, исходная версия сохранит каждый элемент под индексом "i", перезаписав ранее добавленный элемент. Добавленный better_env_as_container
очень похож на env_as_container_
, но без материала deparse
. Обе демонстрируют производительность O (1), но у них есть накладные расходы, которые немного больше, чем связанные/расширяющиеся списки.
Накладные расходы памяти
В реализации C R есть накладные расходы на 4 слова и 2 int на выделенный объект. Подход linkedList
выделяет один список длиной два в каждом приложении, в общей сложности (4 * 8 + 4 + 4 + 2 * 8 =) 56 байтов на добавленный элемент на 64-разрядных компьютерах (исключая накладные расходы памяти, поэтому, вероятно, ближе к 64 байтам). Подход expandingList
использует одно слово для добавленного элемента плюс копию при удвоении длины вектора, поэтому общее использование памяти составляет до 16 байт на элемент. Так как память все в одном или двух объектах, служебные данные на один объект несущественны. Я не смотрел на использование памяти env
, но думаю, что он будет ближе к linkedList
.
Ответ 4
В Lisp мы сделали это следующим образом:
> l <- c(1)
> l <- c(2, l)
> l <- c(3, l)
> l <- rev(l)
> l
[1] 1 2 3
хотя это были "минусы", а не только "c". Если вам нужно начать с списка empy, используйте l < - NULL.
Ответ 5
Вам может понадобиться что-то подобное?
> push <- function(l, x) {
lst <- get(l, parent.frame())
lst[length(lst)+1] <- x
assign(l, lst, envir=parent.frame())
}
> a <- list(1,2)
> push('a', 6)
> a
[[1]]
[1] 1
[[2]]
[1] 2
[[3]]
[1] 6
Это не очень вежливая функция (присвоение parent.frame()
довольно грубо), но IIUYC это то, о чем вы просите.
Ответ 6
Если вы перечислите переменную списка в качестве строки с кавычками, вы можете получить ее из функции, например:
push <- function(l, x) {
assign(l, append(eval(as.name(l)), x), envir=parent.frame())
}
так:
> 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
>
Ответ 7
Не уверен, почему вы не думаете, что ваш первый метод не будет работать. У вас есть ошибка в функции lappend: length (list) должна быть длиной (lst). Это отлично работает и возвращает список с добавленным объектом.
Ответ 8
попробуйте эту функцию lappend
lappend <- function (lst, ...){
lst <- c(lst, list(...))
return(lst)
}
и другие предложения с этой страницы Добавить именованный вектор в список
Bye.
Ответ 9
Я сделал небольшое сравнение методов, упомянутых здесь.
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
}
)
Результаты:
Unit: milliseconds
expr min lq mean median uq max neval cld
env_with_list_ 188.9023 198.7560 224.57632 223.2520 229.3854 282.5859 5 a
c_ 1275.3424 1869.1064 2022.20984 2191.7745 2283.1199 2491.7060 5 b
list_ 17.4916 18.1142 22.56752 19.8546 20.8191 36.5581 5 a
by_index 445.2970 479.9670 540.20398 576.9037 591.2366 607.6156 5 a
append_ 1140.8975 1316.3031 1794.10472 1620.1212 1855.3602 3037.8416 5 b
env_as_container_ 355.9655 360.1738 399.69186 376.8588 391.7945 513.6667 5 a
Ответ 10
Я думаю, что вы хотите сделать на самом деле передать ссылку (указатель) на функцию - создать новую среду (которая передается по ссылке на функции) с добавленным в нее списком:
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
}
Теперь вы только изменяете существующий список (не создавая новый)
Ответ 11
на самом деле существует подэлемент с функцией 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)
Ответ 12
Это простой способ добавить элементы в список 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
Ответ 13
> 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
Ответ 14
Это очень интересный вопрос, и я надеюсь, что моя мысль ниже может помочь в решении этой проблемы. Этот метод дает плоский список без индексации, но у него есть список и список, чтобы избежать структур вложенности. Я не уверен в скорости, так как я не знаю, как сравнить ее.
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