索引所有中间列表的列表位置,即使列表为空(包{rrapply})

问题描述 投票:0回答:2

我有一个输入数据结构,最终叶节点为空列表,父列表仅包含子列表。相关信息最终出现在列表属性中,我必须提取它。为此,我需要获取 all 列表元素的索引位置,并使用这些坐标来遍历列表。

{rrapply}
一直在为带有元素的列表工作,但我不知道如何获得没有非列表子元素的列表的位置。

下面是一个代表,显示了我得到

{rrapply}
得到的部分,以及它遗漏了什么,最后是一个非常老式的解决方案,我编写了代码来工作。所以我有一个解决方案,但我想看看我是否能让
{rrapply}
做到这一点,并理解为什么我不能让它做我想做的事。

# stackoverflow_list-element-position-vector.R

library(rrapply)
library(purrr)

# A sample list
lst <-
    list(
        list(),
        list(
            list(),
            list(),
            list()
        ),
        list(a=1:3
             , list()
        )
    )

# Misses all of the empty lists, just the sublist with the name "a":
rrapply(lst,
        f = \(x, .xpos) .xpos,
        how="melt") |>
    pluck("value") |>
    str()
#> List of 1
#>  $ : int [1:2] 3 1

#;; From: https://stackoverflow.com/a/76124073/1022967
#;; Misses the positions of the intermediate nodes, which I need too.
#;; i.e., misses lists at position 2 and 3.
rrapply(lst
        , classes = "list"
        , condition = \(x) length(x) == 0
        , f = \(x) NA
        , how="recurse") |>
    rrapply(f = \(x, .xpos) .xpos
            , how="melt") |>
    pluck("value") |>
    str()
#> List of 6
#>  $ : int 1
#>  $ : int [1:2] 2 1
#>  $ : int [1:2] 2 2
#>  $ : int [1:2] 2 3
#>  $ : int [1:2] 3 1
#>  $ : int [1:2] 3 2

#;; Take a list, return flat list of all of the positions of list
#;; elements.
#;; Real old-school technique...
listEltPos <- function(llst) {
    work_lst <-
        list(list(coord_vec = integer(0), comp = llst))
    output_lst <-
        list()
    
    while (TRUE) {
        if (length(work_lst) == 0) break
        
        # Pop off head of work_lst
        e <-
            pluck(work_lst, 1)
        work_lst <-
            work_lst[-1]
        
        # If element isn't a list, nothing more to process
        if (class(e$comp) != "list") next
        
        # If here, it is a list.  If length is 0, return
        if (length(e$comp) == 0) next
        
        # Otherwise, it has entries.  Find relative coordinate of the entry
        # and add that to the output list and make a list element with that
        # coordinate and the corresponding subelement.
        subElementPositions <-
            seq(length(e$comp))
        
        for (i in subElementPositions) {
            # Append local pos to the overall coordinate vector
            newPosVec <-
                c(e$coord_vec, i)
            sub_e <-
                pluck(e$comp, i)
            
            # Add current new pos to the output queue
            output_lst <-
                append(output_lst, list(newPosVec))
            
            # Append subelements and their updated positions to the work queue
            work_lst <-
                append(work_lst, list(list(coord_vec=newPosVec, comp=sub_e)))
        }
        rm(i)
    }

    output_lst
}

#;; This is what I am hoping for
listEltPos(lst) |>
    str()
#> List of 8
#>  $ : int 1
#>  $ : int 2
#>  $ : int 3
#>  $ : int [1:2] 2 1
#>  $ : int [1:2] 2 2
#>  $ : int [1:2] 2 3
#>  $ : int [1:2] 3 1
#>  $ : int [1:2] 3 2

创建于 2023-05-03 与 reprex v2.0.2

r
2个回答
1
投票

使用

rrapply()
的一种可能方法是使用
how = "recurse"
遍历嵌套列表,并通过将它们分配给全局变量(在函数范围之外)来跟踪所有遇到的
.xpos
值:

library(rrapply)

## initialize empty list
allpos <- list()

## walk nested list and append indices to `allpos`
invisible(
  rrapply(
    lst,
    classes = c("list", "integer"),
    how = "recurse",
    f = \(x, .xpos) {
      allpos <<- append(allpos, list(.xpos))
      x
    }
  )
)

str(allpos)
#> List of 8
#>  $ : int 1
#>  $ : int 2
#>  $ : int [1:2] 2 1
#>  $ : int [1:2] 2 2
#>  $ : int [1:2] 2 3
#>  $ : int 3
#>  $ : int [1:2] 3 1
#>  $ : int [1:2] 3 2

注意:这对于非常大的列表来说效率不高,在这种情况下,最好提前确定

allpos
的大小,而不是迭代地将新值附加到列表中。


1
投票

接受的答案正是我想要的。我在这里添加了我将该解决方案包装在一个函数中的方式,这样我就不会对

.GlobalEnv
进行任何更新。我还将一个参数更改为
classes
以执行
ANY
integer
,因为我可能有更多的整数(我没有在原始示例中列出)。

# A sample list, slightly different content, has an integer and character
# element.
lst <-
    list(
        list(),
        list(
            list(),
            list()
        ),
        list(a=1:3
             , list()
             , c= letters[1:4]
        )
    )


# From Joris solution here -- changes 
getListNodePos <- function(llst) {
    library(rrapply)

    #;; https://stackoverflow.com/questions/8771942/how-can-i-reference-the-local-environment-within-a-function-in-r 
    locEnv <- environment()
    
    ## initialize empty list
    allpos <- list()
    
    ## walk nested list and append indices to `allpos`
    invisible(
        rrapply(
            llst,
            classes = c("list", "ANY"),
            how = "recurse",
            f = \(x, .xpos) {
                assign("allpos", append(allpos, list(.xpos)), envir = locEnv)
                x
            }
        )
    )
    
    allpos
}

# Show the indexes
getListNodePos(lst) |>
    str()
#> List of 8
#>  $ : int 1
#>  $ : int 2
#>  $ : int [1:2] 2 1
#>  $ : int [1:2] 2 2
#>  $ : int 3
#>  $ : int [1:2] 3 1
#>  $ : int [1:2] 3 2
#>  $ : int [1:2] 3 3

创建于 2023-05-04 与 reprex v2.0.2

© www.soinside.com 2019 - 2024. All rights reserved.