상각된 상수 시간 동안 R의 목록에 개체를 추가하시겠습니까, O(1)?
리스트가 R 리스트가 있습니다.mylist
, , , , , , , , 을 붙일 수 .obj
다음 중 하나:
mylist[[length(mylist)+1]] <- obj
이치노릇을 하다 때 을 쓰려고 요.lappend()
다음과 같이 합니다.
lappend <- function(lst, obj) {
lst[[length(lst)+1]] <- obj
return(lst)
}
semantics(Call-by-name semantics) 때문에 lst
「」로 됩니다.lst
의 밖에는 않는다lappend()
R 함수로 환경 해킹을 하면 기능 범위 밖에 도달하여 호출 환경을 변형시킬 수 있다는 것은 알고 있습니다만, 간단한 추가 함수를 쓰기에는 큰 망치처럼 보입니다.
더 아름다운 방법을 제안해 주실 수 있나요?벡터와 리스트 모두에서 동작하는 경우 보너스 포인트.
'열'을 하시면 됩니다.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-01년 2월):이 게시물은 5번째 생일에 올라옵니다.일부 친절한 독자들은 계속해서 그것의 결점을 반복하고 있기 때문에, 반드시 아래의 코멘트도 봐 주세요.에 대한 제안 1개요list
삭제:
newlist <- list(oldlist, list(someobj))
일반적으로 R타입은 모든 타입과 용도에 대해 하나의 사자성어를 갖는 것을 어렵게 만들 수 있다.
OP 문제는 예를 OP(2012년 4월 4일)를 시간 내에 수 여부를 하는 데 .vector<>
컨테이너.가장 좋은 답변은?여기에서는 고정 크기의 문제가 주어진 다양한 솔루션의 상대적인 실행 시간만 보여줄 뿐 다양한 솔루션의 알고리즘 효율성을 직접 다루지는 않습니다.아래의 많은 답변에서는 일부 솔루션의 알고리즘 효율성에 대해 논의하고 있지만, 지금까지(2015년 4월 현재) 모든 경우에서 잘못된 결론에 도달하고 있습니다.
알고리즘 효율은 문제 크기가 커짐에 따라 시간(실행 시간) 또는 공간(사용 메모리 양)으로 증가 특성을 캡처합니다.고정 크기의 문제가 있는 경우 다양한 솔루션에 대해 성능 테스트를 실행해도 다양한 솔루션의 증가율을 해결할 수 없습니다.OP는 "변환된 상수 시간"에 개체를 R 목록에 추가할 수 있는 방법이 있는지 확인하는 데 관심이 있습니다.그게 무슨 의미죠?설명을 위해 먼저 "정수 시간"을 설명하겠습니다.
지속적인 성장 또는 O(1):
주어진 작업을 수행하는 데 필요한 시간이 문제의 크기가 두 배로 증가하더라도 동일한 경우 알고리즘은 지속적인 시간 성장을 나타내거나 "빅 O" 표기로 명시되어 있으며 O(1) 시간 성장을 나타냅니다.OP가 "변화된" 상수 시간을 의미하는 것은 단순히 "장기적으로"를 의미합니다. 즉, 단일 작업을 수행하는 데 보통보다 시간이 더 오래 걸리는 경우(예: 사전 할당된 버퍼가 소진되고 경우에 따라 더 큰 버퍼 크기로 크기가 조정되어야 하는 경우) 장기 평균 성능이 일정하다면,그래도 O(1)라고 부르죠.
비교를 위해 "선형 시간"과 "4차 시간"에 대해서도 설명하겠습니다.
선형 또는 O(n) 증가:
주어진 작업을 수행하는 데 필요한 시간이 문제의 크기가 두 배로 증가하면 알고리즘은 선형 시간, 즉 O(n) 증가를 나타냅니다.
2차 또는 O(n2) 성장:
주어진 작업을 수행하는 데 필요한 시간이 문제 크기의 제곱만큼 증가하면 알고리즘이 2차 시간, 즉 O(n2) 성장을 나타낸다고 합니다.
알고리즘에는 다른 많은 효율성 클래스가 있다; 나는 더 많은 논의를 위해 위키피디아 기사를 참조한다.
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는 @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회 주행마다 동일한 작업을 수행하므로 이상적인 환경에서는 각 주행마다 동일한 시간이 소요될 것으로 예상할 수 있습니다.그러나 테스트 중인 코드가 아직 CPU 캐시에 로드되지 않았기 때문에 첫 번째 실행은 일반적으로 더 긴 시간으로 치우쳐 있습니다.첫 번째 실행 후에는 시간이 상당히 일치할 것으로 예상되지만 테스트 중인 코드와 무관한 타이머 틱 인터럽트 또는 기타 하드웨어 인터럽트로 인해 코드가 캐시에서 제거될 수 있습니다.코드 스니펫을 5회 테스트함으로써 첫 번째 실행 시 코드를 캐시에 로드하고 각 스니펫이 외부 이벤트의 간섭 없이 4회 실행되도록 합니다.이러한 이유로 매번 정확히 동일한 입력 조건에서 동일한 코드를 실행하고 있기 때문에 다양한 코드 옵션을 가장 잘 비교하기 위해서는 "min" 시간만으로 충분하다고 간주합니다.
처음에 문제 크기를 2000으로 설정하고 다음으로 20000으로 실행하기로 선택했기 때문에 문제 크기는 첫 번째 실행에서 두 번째 실행으로 10배 증가했습니다.
of포의 list
O(1) (O)
IT의 성장을 list
두 프로파일링 모두에서 가장 빠른 솔루션임을 바로 알 수 있습니다.첫 번째 실행에서는 2000개의 "추가" 작업을 수행하는 데 854마이크로초(0.854밀리초)가 걸렸습니다.두 번째 실행에서는 20000개의 "추가" 작업을 수행하는 데 8.746밀리초가 걸렸습니다.순진한 관찰자는 "아, 문제의 크기가 10배로 커짐에 따라 테스트 실행에 필요한 시간도 증가했기 때문에 솔루션이 O(n) 성장을 보인다"고 말할 것이다.이 분석의 문제는 OP가 원하는 것은 전체 문제의 증가율이 아니라 단일 개체 삽입의 증가율이라는 것입니다.그걸 안다면, 그땐 분명해list
솔루션은 OP가 정확히 원하는 것을 제공합니다. 즉, O(1) 시간에 개체를 목록에 추가하는 방법입니다.
기타 솔루션의 퍼포먼스
to none none none 、 none none 、 른 none 、 none none 、 none none 、 none none the none none none none none none none none none 。list
어쨌든 검토하는 것이 도움이 됩니다.
다른 솔루션의 대부분은 퍼포먼스가 O(n)인 것 같습니다.를 들면, 「」는,by_index
솔루션이란 다른 SO 게시물에서 발견한 빈도에 따라 매우 인기 있는 솔루션이며, 2000개의 개체를 추가하는 데 11.6밀리초가 걸렸고, 10배의 개체를 추가하는 데 953밀리초가 걸렸습니다.전체 문제의 시간이 100배 증가했기 때문에 순진한 관찰자는 "아, 문제의 크기가 10배 증가할수록 테스트 실행에 필요한 시간이 100배 증가했기 때문에 해결책은 O(n2) 성장을 보인다"고 말할 수 있다.이전과 마찬가지로 OP는 단일 개체 삽입의 성장에 관심이 있기 때문에 이 분석은 결함이 있습니다.전체적인 시간 증가를 문제의 크기 증가로 나누면 추가 객체의 시간 증가가 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의 @phonetagger입니다.★도했습니다.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
가가 i i i i i를 했습니다.linkedList
★★★★★★★★★★★★★★★★★」expandingList
둘 다 인라인 버전입니다.inlinedLinkedList
으로 '복사본'입니다.list_
도 합니다.게다가 인라인 버전과 비인라인 버전의 차이는 함수 호출의 오버헤드에 기인합니다.
의 모든 expandingList
★★★★★★★★★★★★★★★★★」linkedList
O(1) 추가 성능을 보여주며, 추가된 항목 수에 따라 벤치마크 시간이 선형으로 조정됩니다. linkedList
expandingList
함수 콜 오버헤드도 표시됩니다.따라서한 모든 R ), 의 "R"을 합니다.expandingList
.
또, R의 C 실장에 대해서도 설명했습니다.메모리가 부족할 때까지, 어느 사이즈에서도, 양쪽의 어프로치를 O(1) append로 할 필요가 있습니다.
도 ★★★★★★★★★★★★★★★★★★★★★★★env_as_container_
원래 버전은 인덱스 "i" 아래에 모든 항목을 저장하여 이전에 추가된 항목을 덮어씁니다.better_env_as_container
가 덧붙인 것은 '비슷하다', '', '비슷하다' 입니다.env_as_container_
, ,이 이가 없습니다.deparse
둘 다 O(1) 성능을 나타내지만 링크/확장 목록보다 오버헤드가 상당히 큽니다.
메모리 오버헤드
CR 실장에서는 할당된 오브젝트당4개의 워드와2개의 int의 오버헤드가 있습니다.linkedList
접근법은 64비트 컴퓨터에서 추가된 항목당 총 56바이트(4*8+4+4+2*8=)에 대해 추가당 길이 2개의 목록을 하나씩 할당합니다(메모리 할당 오버헤드는 제외되므로 64바이트에 가까울 수 있음).expandingList
로 했을 메모리 입니다.따라서 항목당 최대 16바이트의 메모리 사용량이 됩니다.메모리는 모두 1개 또는2개의 오브젝트에 있기 때문에 오브젝트 단위의 오버헤드는 중요하지 않습니다.하지 않았다env
사용 이지만, 「메모리 사용 상황」에이라고 생각합니다.linkedList
리스프에서는 다음과 같이 했습니다.
> l <- c(1)
> l <- c(2, l)
> l <- c(3, l)
> l <- rev(l)
> l
[1] 1 2 3
'c'가 아니라 'cons'였음에도 불구하고요empy 목록으로 시작해야 할 경우 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를 사용하다
목록 변수를 따옴표로 묶은 문자열로 전달하면 함수 내에서 다음과 같이 액세스할 수 있습니다.
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
>
왜 첫 번째 방법이 효과가 없다고 생각하는지 모르겠네요.lapend 함수에 length(list)는 length(lst)여야 합니다.그러면 정상적으로 동작하며 obj가 추가된 목록이 반환됩니다.
나는 여기에 언급된 방법들을 조금 비교했다.
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 <- function (lst, ...){
lst <- c(lst, list(...))
return(lst)
}
이 페이지의 다른 제안 및 명명된 벡터를 목록에 추가합니다.
안녕.
에는 미묘한 점이 있다c()
다음 중 하나:
x <- list()
x <- c(x,2)
x = c(x,"foo")
예상대로 얻을 수 있습니다.
[[1]]
[1]
[[2]]
[1] "foo"
x <- c(x, matrix(5,2,2)
이 가치 가 더 있습니다.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)
실제로 함수에 대한 참조(포인터)를 통해 전달해야 할 작업은 목록에 추가된 새로운 환경(함수에 대한 참조로 전달됨)을 만드는 것입니다.
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
}
이제 기존 목록만 수정합니다(새 목록을 만들지 않음).
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
, there있있 there 도 있다.list.append
rlist
(문서 링크)
require(rlist)
LL <- list(a="Tom", b="Dick")
list.append(LL,d="Pam",f=c("Joe","Ann"))
매우 심플하고 효율적입니다.
> 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
이것은 매우 흥미로운 질문이며, 아래의 제 생각이 해결방법에 도움이 되었으면 합니다.이 메서드는 인덱싱 없이 플랫리스트를 제공하지만 네스트 구조를 피하기 위해 리스트와 언리스트가 있습니다.벤치마킹하는 방법을 몰라서 속도를 잘 모르겠어요.
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
검증을 위해 @Cron에서 제공한 벤치마크 코드를 실행했습니다.(하는 것 에) (으로 동작하는 것 외에)1의 큰 차이가 .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
}
)
다음 벤치마크를 실행했습니다.
bench=function(...,n=1,r=3){
a=match.call(expand.dots=F)$...
t=matrix(ncol=length(a),nrow=n)
for(i in 1:length(a))for(j in 1:n){t1=Sys.time();eval(a[[i]],parent.frame());t[j,i]=Sys.time()-t1}
o=t(apply(t,2,function(x)c(median(x),min(x),max(x),mean(x))))
round(1e3*`dimnames<-`(o,list(names(a),c("median","min","max","mean"))),r)
}
ns=10^c(3:7)
m=sapply(ns,function(n)bench(n=5,
`vector at length + 1`={l=c();for(i in 1:n)l[length(l)+1]=i},
`vector at index`={l=c();for(i in 1:n)l[i]=i},
`vector at index, initialize with type`={l=integer();for(i in 1:n)l[i]=i},
`vector at index, initialize with length`={l=vector(length=n);for(i in 1:n)l[i]=i},
`vector at index, initialize with type and length`={l=integer(n);for(i in 1:n)l[i]=i},
`list at length + 1`={l=list();for(i in 1:n)l[[length(l)+1]]=i},
`list at index`={l=list();for(i in 1:n)l[[i]]=i},
`list at index, initialize with length`={l=vector('list',n);for(i in 1:n)l[[i]]=i},
`list at index, initialize with double length, remove null`={l=vector("list",2*n);for(i in 1:n)l[[i]]=i;l=head(l,i)},
`list at index, double when full, get length from variable`={len=1;l=list();for(i in 1:n){l[[i]]=i;if(i==len){len=len*2;length(l)=len}};l=head(l,i)},
`list at index, double when full, check length inside loop`={len=1;l=list();for(i in 1:n){l[[i]]=i;if(i==length(l)){length(l)=i*2}};l=head(l,i)},
`nested lists`={l=list();for(i in 1:n)l=list(l,i)},
`nested lists with unlist`={if(n<=1e5){l=list();for(i in 1:n)l=list(l,i);o=unlist(l)}},
`nested lists with manual unlist`={l=list();for(i in 1:n)l=list(l,i);o=integer(n);for(i in 1:n){o[n-i+1]=l[[2]];l=l[[1]]}},
`JanKanis better_env_as_container`={env=new.env(hash=T,parent=globalenv());for(i in 1:n)env[[as.character(i)]]=i},
`JanKanis 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]]}},
`JanKanis 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]},
`c`={if(n<=1e5){l=c();for(i in 1:n)l=c(l,i)}},
`append vector`={if(n<=1e5){l=integer(n);for(i in 1:n)l=append(l,i)}},
`append list`={if(n<=1e9){l=list();for(i in 1:n)l=append(l,i)}}
)[,1])
m[rownames(m)%in%c("nested lists with unlist","c","append vector","append list"),4:5]=NA
m2=apply(m,2,function(x)formatC(x,max(0,2-ceiling(log10(min(x,na.rm=T)))),format="f"))
m3=apply(rbind(paste0("1e",log10(ns)),m2),2,function(x)formatC(x,max(nchar(x)),format="s"))
writeLines(apply(cbind(m3,c("",rownames(m))),1,paste,collapse=" "))
출력:
1e3 1e4 1e5 1e6 1e7
2.35 24.5 245 2292 27146 vector at length + 1
0.61 5.9 60 590 7360 vector at index
0.61 5.9 64 587 7132 vector at index, initialize with type
0.56 5.6 54 523 6418 vector at index, initialize with length
0.54 5.5 55 522 6371 vector at index, initialize with type and length
2.65 28.8 299 3955 48204 list at length + 1
0.93 9.2 96 1605 13480 list at index
0.58 5.6 57 707 8461 list at index, initialize with length
0.62 5.8 59 739 9413 list at index, initialize with double length, remove null
0.88 8.4 81 962 11872 list at index, double when full, get length from variable
0.96 9.5 92 1264 15813 list at index, double when full, check length inside loop
0.21 1.9 22 426 3826 nested lists
0.25 2.4 29 NA NA nested lists with unlist
2.85 27.5 295 3065 31427 nested lists with manual unlist
1.65 20.2 293 6505 8835 JanKanis better_env_as_container
1.11 10.1 110 1534 27119 JanKanis inlineLinkedList
2.66 26.3 266 3592 47120 JanKanis inlineExpandingList
1.22 118.6 15466 NA NA c
3.64 512.0 45167 NA NA append vector
6.35 664.8 71399 NA NA append list
위의 표는 평균 시간이 아닌 각 방법의 중앙 시간을 보여 줍니다. 한 번의 실행이 평균 실행 시간을 왜곡하는 일반적인 실행보다 훨씬 오래 걸리는 경우가 있기 때문입니다.그러나 첫 번째 실행 후 후속 실행에서는 어떤 방법도 훨씬 빨라지지 않았기 때문에 일반적으로 각 방법에 대해 최소 시간과 중앙값이 비슷했습니다.
index벡터 at index") .l=c();for(i in 1:n)l[i]=i
은 ' + 1 ('길이에 따라 + 1')보다5배 빨랐습니다.l=c();for(i in 1:n)l[length(l)]=i
벡터의 길이를 구하는 데 요소를 벡터에 추가하는 것보다 시간이 더 걸렸기 때문입니다.소정의 길이로 벡터를 초기화했을 때는 20% 정도 빨라졌지만, 특정 타입으로 초기화해도 벡터에 첫 번째 아이템을 추가할 때 타입을 한 번만 변경하면 되기 때문에 차이가 없습니다., 「index」와 「list length length하면, 늘리면, 코드의 약, 약에, . 「list at index」와 「list at length initialized with length」의 방법을 비교할 때, 리스트의 길이를 소정의 길이로 초기화하면, 코드의 길이가 1e6의 약 2배, 1e7의 약 3배 빨라지기 때문에, 리스트의 길이가 증가했을 때에, 큰 차이가 생긴다.
index ("list at index") l=list();for(i in 1:n)l[[i]]=i
는 ' + ('길이에 리스트 + 1' (길이에 리스트 + 1)보다 3~.l=list();for(i in 1:n)l[[length(l)+1]]=i
를 참조해 주세요.
JanKanis의 링크 목록 및 확장 목록 메서드는 "list at index"보다는 느리지만 "list at length + 1"보다는 빨랐다.링크된 목록이 확장 목록보다 빨랐습니다.
은 이 책이 '아예'라고 합니다.append
, 기능이 빠르다.c
제 append
, , 느리다, 느리다, 느리다, 느리다, 느리다, 느리다, 느리다, 느리다, 느리다, 느리다, 느리다, 느리다.c
.
위의 표에서 길이 1e6 및 길이 1e7은 3가지 메서드의 경우, 즉 "c", "추가 벡터" 및 "추가 목록"은 2차 시간 복잡성이 있기 때문에, 그리고 "내스트 목록"은 스택 오버플로가 발생했기 때문에 누락되었습니다.
"내포 목록" 옵션이 가장 빨랐지만 목록을 정리하는 데 걸리는 시간은 포함되지 않았습니다.★★★★★★★★★를 사용했을 때unlist
리스트를 하게 하는 약 1.때은, 1.26e5 의 1.26e5 의 경우 1.26e5 의 경우입니다.★★★★★★★★★★★★★★★★★★,unlist
는 자신을 합니다.「 」 。n=1.26e5;l=list();for(i in 1:n)l=list(l,list(i));u=unlist(l)
, 「 」의 「 」unlist(recursive=F)
10,항목만 약 10,000개의 항목만 있는 목록.for(i in 1:n)l=unlist(l,recursive=F)
수동으로의 아이템이 있는 약 0.밖에 되지 않았습니다.o=integer(n);for(i in 1:n){o[n-i+1]=l[[2]];l=l[[1]]}
.
목록에 추가할 항목 수를 미리 알 수 없지만 최대 항목 수를 알고 있는 경우 최대 길이로 목록을 초기화하고 나중에 NULL 값을 제거할 수 있습니다.또는 목록이 가득 찰 때마다 목록의 크기를 두 배로 늘리는 방법도 있습니다(목록 길이에 대한 변수와 목록에 추가한 항목 수에 대한 변수가 하나씩 있으면 루프를 반복할 때마다 목록 개체의 길이를 확인할 필요가 없습니다).
ns=10^c(2:7)
m=sapply(ns,function(n)bench(n=5,
`list at index`={l=list();for(i in 1:n)l[[i]]=i},
`list at length + 1`={l=list();for(i in 1:n)l[[length(l)+1]]=i},
`list at index, initialize with length`={l=vector("list",n);for(i in 1:n)l[[i]]=i},
`list at index, initialize with double length, remove null`={l=vector("list",2*n);for(i in 1:n)l[[i]]=i;l=head(l,i)},
`list at index, initialize with length 1e7, remove null`={l=vector("list",1e7);for(i in 1:n)l[[i]]=i;l=head(l,i)},
`list at index, initialize with length 1e8, remove null`={l=vector("list",1e8);for(i in 1:n)l[[i]]=i;l=head(l,i)},
`list at index, double when full, get length from variable`={len=1;l=list();for(i in 1:n){l[[i]]=i;if(i==len){len=len*2;length(l)=len}};l=head(l,i)},
`list at index, double when full, check length inside loop`={len=1;l=list();for(i in 1:n){l[[i]]=i;if(i==length(l)){length(l)=i*2}};l=head(l,i)}
)[,1])
m2=apply(m,2,function(x)formatC(x,max(0,2-ceiling(log10(min(x)))),format="f"))
m3=apply(rbind(paste0("1e",log10(ns)),m2),2,function(x)formatC(x,max(nchar(x)),format="s"))
writeLines(apply(cbind(m3,c("",rownames(m))),1,paste,collapse=" "))
출력:
1e4 1e5 1e6 1e7
9.3 102 1225 13250 list at index
27.4 315 3820 45920 list at length + 1
5.7 58 726 7548 list at index, initialize with length
5.8 60 748 8057 list at index, initialize with double length, remove null
33.4 88 902 7684 list at index, initialize with length 1e7, remove null
333.2 393 2691 12245 list at index, initialize with length 1e8, remove null
8.6 83 1032 10611 list at index, double when full, get length from variable
9.3 96 1280 14319 list at index, double when full, check length inside loop
언급URL : https://stackoverflow.com/questions/2436688/append-an-object-to-a-list-in-r-in-amortized-constant-time-o1
'itsource' 카테고리의 다른 글
PowerShell에서 명령 실행 타이밍 설정 (0) | 2023.04.17 |
---|---|
명령줄 인수를 Bash에서 배열로 변환 (0) | 2023.04.17 |
Bash 이력에 명령어가 표시되지 않도록 하려면 어떻게 해야 합니까? (0) | 2023.04.17 |
로컬 파일을 생성하지 않고 OpenXML을 사용하여 Excel 파일을 생성하는 방법은 무엇입니까? (0) | 2023.04.17 |
T-SQL을 사용하여 외부 키 제약을 일시적으로 해제하려면 어떻게 해야 합니까? (0) | 2023.04.17 |