代码之家  ›  专栏  ›  技术社区  ›  Nick

在r中以摊销常数时间o(1)将对象追加到列表中?

  •  227
  • Nick  · 技术社区  · 15 年前

    如果我有一些R列表 mylist ,您可以附加一个项目 obj 就像这样:

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

    但肯定还有更紧凑的方法。当我刚到R的时候,我试着写 lappend() 像这样:

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

    但这当然不起作用,因为r的按名称调用语义( lst 在调用时有效复制,因此更改为 LST 在范围之外不可见 拉本() . 我知道您可以在r函数中进行环境黑客攻击,以超出您的函数范围并改变调用环境,但这似乎是编写简单的append函数的一个大锤子。

    有谁能推荐一种更漂亮的方法来做这个吗?如果对向量和列表都有效,则可以获得加分。

    17 回复  |  直到 7 年前
        1
  •  247
  •   smci    9 年前

    如果是字符串列表,只需使用 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日): 这篇文章将在五岁生日那天发表。一些好心的读者不断地用它重复任何缺点,所以也一定会看到下面的一些评论。一个建议 list 类型:

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

    一般来说,r类型会使所有类型和用途都很难有一个且只有一个习语。

        2
  •  91
  •   phonetagger    9 年前

    OP(在2012年4月更新的问题的修订版)感兴趣的是知道是否有办法在摊销的固定时间中添加到列表中,例如可以用C++来完成。 vector<> 容器。最佳答案?到目前为止,这里只显示给定一个固定大小问题的各种解决方案的相对执行时间,但不涉及任何不同的解决方案。 algorithmic efficiency 直接。许多答案下面的注释讨论了一些解决方案的算法效率,但是 到目前为止 (截至2015年4月) 他们得出了错误的结论。

    算法效率捕获增长特征,无论是在时间(执行时间)还是空间(消耗的内存量) 随着问题规模的扩大 . 给定一个固定大小的问题,对各种解决方案运行性能测试并不能解决各种解决方案的增长率问题。op想知道是否有一种方法可以在“摊销常数时间”中将对象追加到r列表中。那是什么意思?为了解释,首先让我描述一下“恒定时间”:

    • 常数 O(1) 生长:

      如果执行给定任务所需的时间 保持不变 根据问题的大小 双打 ,然后我们说算法展示了 恒定时间 增长,或者用“大O”表示法,表示O(1)时间增长。当操作人员说“摊销”恒定时间时,他只是说“从长远来看”…也就是说,如果执行单个操作的时间有时比正常时间长得多(例如,如果预先分配的缓冲区耗尽,并且有时需要调整到更大的缓冲区大小),只要长期平均性能是恒定时间,我们仍将其称为O(1)。

      为了便于比较,我还将描述“线性时间”和“二次时间”:

    • 线性的 o(n) 生长:

      如果执行给定任务所需的时间 双打 根据问题的大小 双打 ,然后我们说算法展示了 线性时间 o(n) 生长。

    • 二次 O(n) ) 生长:

      如果执行给定任务所需的时间 按问题大小的平方增加 ,我们说算法展示了 二次时间 O(n) ) 生长。

    还有许多其他效率类的算法;我遵从 Wikipedia article 以便进一步讨论。

    我感谢@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次运行中的每一次执行完全相同的任务,在理想情况下,我们可以预期每次运行所需的时间将完全相同。但是第一次运行通常偏向于更长的时间,因为我们正在测试的代码还没有加载到CPU的缓存中。在第一次运行之后,我们希望时间是相当一致的,但有时我们的代码可能会由于计时器滴答声中断或其他与我们正在测试的代码无关的硬件中断而从缓存中被逐出。通过测试代码片段5次,我们允许在第一次运行期间将代码加载到缓存中,然后给每个代码片段4次运行完成的机会,而不受外部事件的干扰。出于这个原因,并且由于我们每次都在完全相同的输入条件下运行完全相同的代码,因此我们将只考虑“min”时间,以便在各种代码选项之间进行最佳比较。

    请注意,我选择先运行2000个问题大小,然后运行20000个问题大小,因此从第一次运行到第二次运行,我的问题大小增加了10倍。

    执行 list 溶液:O(1)(恒定时间)

    让我们先看看 列表 解决方案,因为我们可以马上知道这是两次分析运行中最快的解决方案:在第一次运行中,它花费了854 微型的 秒(0.854 毫利 秒)执行2000个“附加”任务。在第二次运行中,执行20000个“append”任务需要8.746毫秒。一个中堂观察员会说, “啊, 列表 解决方案呈现O(N)增长,因为随着问题规模增长10倍,执行测试所需的时间也随之增长。” 这个分析的问题是,OP想要的是 单个对象插入 ,而不是整个问题的增长速度。知道了这一点,很明显 列表 解决方案正好满足了op的需求:一种在o(1)时间内将对象追加到列表的方法。

    其他解决方案的性能

    其他的解决方案都没有达到 列表 解决方案,但无论如何检查它们都会提供信息:

    其他大多数解决方案的性能似乎都是o(n)。例如, by_index solution是一个非常流行的解决方案,基于我在其他so文章中找到它的频率,它花费了11.6毫秒来附加2000个对象,953毫秒来附加10倍多的对象。整个问题的时间增长了100倍,所以一个天真的观察者可能会说 “啊, 拜尔指数 溶液显示O(N )增长,因为问题规模增长了10倍,执行测试所需的时间增长了100倍。” 和以前一样,这种分析是有缺陷的,因为op对单个对象插入的增长感兴趣。如果我们将整个时间增长除以问题的大小增长,我们会发现附加对象的时间增长仅增加了10倍,而不是100倍,这与问题大小的增长相匹配,因此 拜尔指数 解是o(n)。表O(n)中没有列出任何解决方案 )附加单个对象的增长。

        3
  •  40
  •   JanKanis    10 年前

    在其他答案中,只有 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_ 一点。原文 环境容器_ 已经坏了,实际上没有储存所有的号码。

    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_ ,但它也将嵌套结构转换回普通列表。除此之外,内联和非内联版本之间的差异是由于函数调用的开销造成的。

    所有变体 展开列表 链接列表 显示o(1)追加性能,基准时间与追加的项目数成线性比例。 链接列表 展开列表 ,函数调用开销也可见。因此,如果您真的需要尽可能快的速度(并且希望坚持使用r代码),请使用 展开列表 .

    我还研究了r的c实现,这两种方法对于任何大小都应该是o(1)append,直到内存耗尽为止。

    我也变了 环境容器_ ,原始版本将在索引“i”下存储每个项,覆盖先前附加的项。这个 更好的集装箱 我已经补充说非常类似于 环境容器_ 但是没有 deparse 东西。两者都表现出o(1)性能,但是它们的开销比链表/扩展表要大得多。

    内存开销

    在C R实现中,每个分配的对象有4个字和2个整数的开销。这个 链接列表 方法为64位计算机上的每个附加项分配一个长度为2的列表,总共为(4*8+4+4+2*8=)56字节(不包括内存分配开销,因此可能接近64字节)。这个 展开列表 方法使用每个附加项一个单词,当向量长度加倍时再加上一个副本,因此每个项的总内存使用量高达16字节。由于内存都在一个或两个对象中,因此每个对象的开销微不足道。我还没有深入调查 env 内存使用,但我认为它将更接近 链接列表 .

        4
  •  17
  •   Arseny    13 年前

    在口齿不清的情况下,我们是这样做的:

    > l <- c(1)
    > l <- c(2, l)
    > l <- c(3, l)
    > l <- rev(l)
    > l
    [1] 1 2 3
    

    虽然是“cons”,而不仅仅是“c”。如果需要从empy列表开始,请使用l<-null。

        5
  •  6
  •   Ken Williams Dirk is no longer here    15 年前

    你也许想要这样的东西?

    > 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() 有点粗鲁)但我知道这是你想要的。

        6
  •  5
  •   ayman    15 年前

    如果将列表变量作为带引号的字符串传入,则可以从函数中访问它,如:

    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
  •  5
  •   Cron Merdek    10 年前

    我对这里提到的方法做了一个小小的比较。

    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 
    
        8
  •  4
  •   Paul    15 年前

    不知道为什么你认为你的第一种方法行不通。lappend函数中有一个错误:length(list)应该是length(lst)。这样可以很好地工作,并返回一个带有附加obj的列表。

        9
  •  3
  •   Community Mohan Dere    8 年前

    尝试lappend函数

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

    以及本页的其他建议 Add named vector to a list

    再见。

        10
  •  2
  •   DavidM    14 年前

    我想你想做的是 事实上 按引用传递(指针)到函数--创建一个新环境(通过引用传递给函数),并将列表添加到其中:

    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
  •  2
  •   doug    13 年前

    这是向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
    
        12
  •  2
  •   David Bellot    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)
    
        13
  •  1
  •   5th    7 年前

    还有 list.append rlist ( link to the documentation )

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

    它非常简单有效。

        14
  •  1
  •   WestCoastProjects    7 年前

    为了验证,我运行了@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
            }
    )
    
        15
  •  0
  •   Marek    14 年前
    > 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
    
        16
  •  0
  •   xappppp    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
    
        17
  •  -1
  •   saravanan saminathan    7 年前

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

    所以我们可以使用上面的代码轻松地附加元素/对象