もし私が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()
の範囲外では見えません。関数の範囲を超えて呼び出し環境を変化させるのですが、それは単純な追加関数を書くための大きなハンマーのようです。
これを行うためのより美しい方法を誰かが提案できますか?ベクトルとリストの両方で機能すればボーナスポイント。
もしそれが文字列のリストなら、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年2月1日):この投稿は、5歳の誕生日を迎えます。何人かの親切な読者はそれに関してどんな欠点でも繰り返し続けます、それで必ずまた以下のコメントのいくつかを見てください。 list
型に対する1つの提案:
newlist <- list(oldlist, list(someobj))
一般的に、R型はすべての型と用途に対して1つのイディオムを持つことを難しくします。
OP(質問の2012年4月更新改訂版)は、たとえばC++ vector<>
コンテナで実行できるなど、償却された一定時間でリストに追加する方法があるかどうかを知りたいと考えています。ここでの最良の答えは、これまでのところ、固定サイズの問題に対するさまざまなソリューションの相対的な実行時間のみを示していますが、さまざまなソリューションの アルゴリズム効率 に直接対処していません。多くの回答の下のコメントでは、いくつかのソリューションのアルゴリズムの効率について説明していますが、これまでのすべてのケースで(2015年4月現在) )それらは間違った結論に達します。
アルゴリズムの効率は、時間(実行時間)またはスペース(消費されたメモリの量)のいずれかの成長特性をキャプチャします問題サイズの成長。固定サイズの問題を想定して、さまざまなソリューションのパフォーマンステストを実行しても、さまざまなソリューションの成長率に対処できません。 OPは、「償却された一定時間」でオブジェクトをRリストに追加する方法があるかどうかを知りたいと考えています。どういう意味ですか?説明するには、まず「一定の時間」について説明させてください。
ConstantまたはO(1)成長:
特定のタスクを実行するのに必要な時間が問題のサイズと同じのままである場合doublesアルゴリズムがconstant timeの成長を示す、または「Big O」表記で示されている場合、O(1)の時間の成長を示します。 OPが一定時間「償却」と言うとき、彼は単に「長期的に」を意味します。つまり、単一の操作を実行するのに通常よりもはるかに時間がかかる場合バッファサイズ)、長期的な平均パフォーマンスが一定時間である限り、O(1)と呼びます。
比較のために、「線形時間」と「2次時間」についても説明します。
LinearまたはO(n)成長:
特定のタスクの実行に必要な時間がdoubles問題のサイズdoublesである場合、アルゴリズムはlinear time、またはO(n)の成長を示します。
QuadraticまたはO(n2)成長:
特定のタスクを実行するのに必要な時間が問題サイズの2乗増加する場合、アルゴリズムはquadratic time、またはO(n2)成長。
アルゴリズムには他にも多くの効率クラスがあります。 ウィキペディアの記事 をさらに議論するために延期します。
Rの初心者であり、このページに掲載されているさまざまなソリューションのパフォーマンス分析を行うための完全に構築されたコードブロックがあることは素晴らしいことであるため、@ CronAcronisに感謝します。私は分析のために彼のコードを借りています。これを以下に複製します(関数にラップします)。
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によって投稿された結果は、少なくとも[10000]の問題サイズに対してa <- list(a, list(i))
メソッドが最速であることを確実に示唆しているようですが、単一の問題サイズの結果はソリューションの成長に対応していません。そのためには、問題のサイズが異なる最低2つのプロファイリングテストを実行する必要があります。
> 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値についてのWord:5回の実行ごとにまったく同じタスクを実行しているため、理想的な世界では、まったく同じことを期待できます。各実行の時間。しかし、通常、最初の実行は、テストするコードがまだCPUのキャッシュにロードされていないという事実により、より長い時間に偏っています。最初の実行後、時間はかなり一貫していると予想されますが、テストするコードとは無関係のタイマーティック割り込みまたはその他のハードウェア割り込みにより、コードがキャッシュから削除される場合があります。コードスニペットを5回テストすることで、最初の実行中にコードをキャッシュにロードし、各スニペットに外部イベントの干渉なしに4回実行できるようにします。このため、また毎回まったく同じ入力条件で実際にまったく同じコードを実行しているため、さまざまなコードオプション間の最適な比較を行うには「min」回だけで十分であると見なします。
最初に問題サイズ2000で実行し、次に20000で実行することにしたことに注意してください。したがって、問題サイズは最初の実行から2番目の実行で10倍に増加しました。
list
ソリューションのパフォーマンス:O(1)(一定時間)
最初にlist
ソリューションの成長を見てみましょう。これは、両方のプロファイリング実行で最速のソリューションであることがすぐにわかるからです。最初の実行では、854microsecondsかかりました(0.854milliseconds)2000の「追加」タスクを実行します。 2回目の実行では、20000の「追加」タスクを実行するのに8.746ミリ秒かかりました。ナイーブなオブザーバーは、"Ah、list
ソリューションはO(n)成長を示します。問題のサイズが10倍に増加したため、テストの実行に必要な時間もそうでした。」この分析の問題は、OPが望むのは、成長率ではなく単一オブジェクト挿入の成長率であるということです。全体的な問題の。それを知っているので、list
ソリューションがOPが望むものを正確に提供することは明らかです:O(1)時間でリストにオブジェクトを追加する方法。
他のソリューションのパフォーマンス
他のソリューションはどれもlist
ソリューションの速度に近づきませんが、とにかくそれらを調べることは有益です:
他のほとんどのソリューションは、パフォーマンスがO(n)のように見えます。たとえば、by_index
ソリューションは、他のSO投稿で見つける頻度に基づいた非常に人気のあるソリューションで、2000個のオブジェクトを追加するのに11.6ミリ秒、10回追加するのに953ミリ秒かかりました。多くのオブジェクト。全体的な問題の時間は100倍に増加したため、ナイーブな観察者は"Ah、by_index
ソリューションはO(n2)成長。問題のサイズが10倍に増加したため、テストの実行に必要な時間が100倍に増加したため。 "前と同様、この分析には欠陥があります。単一のオブジェクト挿入の増加。全体の時間の増加を問題のサイズの増加で除算すると、追加するオブジェクトの時間の増加は、問題の成長に一致する100倍ではなく、10倍だけ増加することがわかりますサイズなので、by_index
ソリューションはO(n)です。O(n2)単一のオブジェクトを追加するための成長。
他の回答では、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
のインラインバージョンを使用してください。
RのC実装も見てきましたが、どちらのアプローチも、メモリがなくなるまで任意のサイズでO(1) appendする必要があります。
env_as_container_
も変更しました。元のバージョンでは、すべてのアイテムがインデックス「i」の下に保存され、以前に追加されたアイテムが上書きされます。私が追加したbetter_env_as_container
はenv_as_container_
と非常に似ていますが、deparse
がありません。両方ともO(1)パフォーマンスを示しますが、リンク/拡張リストよりもかなり大きなオーバーヘッドがあります。
メモリオーバーヘッド
C Rの実装では、割り当てられたオブジェクトごとに4ワードと2 intのオーバーヘッドがあります。 linkedList
アプローチは、追加ごとに長さ2のリストを1つ割り当てます。64ビットコンピューターでは、追加アイテムごとに合計(4 * 8 + 4 + 4 + 2 * 8 =)56バイト(メモリ割り当てオーバーヘッドを除き、おそらく64バイトに近いでしょう)。 expandingList
アプローチは、追加されたアイテムごとに1つのWordを使用し、ベクトルの長さを2倍にするときにコピーを使用するため、アイテムごとの合計メモリ使用量は最大16バイトです。メモリはすべて1つまたは2つのオブジェクトにあるため、オブジェクトごとのオーバーヘッドはわずかです。 env
のメモリ使用量については詳しく調べていませんが、linkedList
に近いと思います。
LISPでは、このようにしました。
> l <- c(1)
> l <- c(2, l)
> l <- c(3, l)
> l <- rev(l)
> l
[1] 1 2 3
それは 'c'ではなく 'cons'でした。空リストから始める必要がある場合は、l < - NULLを使用してください。
あなたはおそらくこのようなものが欲しいですか?
> 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はあなたが求めているものです。
List変数を引用符付きの文字列として渡すと、次のように関数内から到達できます。
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
>
ここで述べた方法を少し比較しました。
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
あなたの最初の方法がうまくいかないとあなたが思わない理由がよくわからない。 lappend関数にバグがあります:length(list)はlength(lst)であるべきです。これはうまく機能し、追加されたobjを含むリストを返します。
この機能を試してください
lappend <- function (lst, ...){
lst <- c(lst, list(...))
return(lst)
}
そしてこのページからの他の提案 リストに名前付きベクトルを追加する
さようなら。
これは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
やりたいことは実際には関数への参照渡し(ポインタ) - リストを追加して新しい環境(関数への参照渡し)を作成することです。
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
}
今、あなたは既存のリストを変更しているだけです(新しいものを作成するのではありません)
実際、c()
関数には微妙なところがあります。もしあなたがそうするなら:
x <- list()
x <- c(x,2)
x = c(x,"foo")
期待どおりに入手できます。
[[1]]
[1]
[[2]]
[1] "foo"
しかしx <- c(x, matrix(5,2,2)
を使って行列を追加すると、あなたのリストはさらに5
という値の4つの要素を持つことになります。あなたはもっとやる:
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)
検証のために@Cronが提供するベンチマークコードを実行しました。 1つの大きな違いがあります(より新しい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
}
)
rlist
からのlist.append
もあります( ドキュメントへのリンク )
require(rlist)
LL <- list(a="Tom", b="Dick")
list.append(LL,d="Pam",f=c("Joe","Ann"))
とてもシンプルで効率的です。
これは非常に興味深い質問であり、以下の私の考えがそれに対する解決策になることを願っています。このメソッドはインデックスを作成せずにフラットリストを作成しますが、ネスト構造を避けるためにlistとunlistを使用します。ベンチマークの方法がわからないので、速度についてはよくわかりません。
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
> 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