You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用rrapply获取所有列表元素(含空列表)的索引位置

嵌套列表元素位置提取问题

我有一个嵌套列表结构,叶子节点为空列表,父列表仅包含子列表,需要提取所有列表元素的索引位置用于遍历。使用rrapply处理含元素的列表时有效,但无法获取无嵌套非列表子元素的空列表的位置。以下是示例代码,展示rrapply的输出缺失情况,以及我自行编写的解决方案,希望能用rrapply实现需求,并理解之前无法实现的原因。

# stackoverflow_list-element-position-vector.R

library(rrapply)
library(purrr)

# 示例列表
lst <-
    list(
        list(),
        list(
            list(),
            list(),
            list()
        ),
        list(a=1:3
             , list()
        )
    )

# 仅获取到含名称"a"的子列表,遗漏所有空列表:
rrapply(lst,
        f = \(x, .xpos) .xpos,
        how="melt") |> 
    pluck("value") |> 
    str()
#> List of 1
#>  $ : int [1:2] 3 1

# 社区方案:遗漏了中间节点的位置,比如位置2和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

# 自定义函数:输入列表,返回所有列表元素的位置向量组成的扁平列表
listEltPos <- function(llst) {
    work_lst <-
        list(list(coord_vec = integer(0), comp = llst))
    output_lst <-
        list()
    
    while (TRUE) {
        if (length(work_lst) == 0) break
        
        # 取出work_lst的首个元素
        e <-
            pluck(work_lst, 1)
        work_lst <-
            work_lst[-1]
        
        # 若元素不是列表,跳过
        if (class(e$comp) != "list") next
        
        # 若列表为空,跳过
        if (length(e$comp) == 0) next
        
        # 处理子元素位置
        subElementPositions <-
            seq(length(e$comp))
        
        for (i in subElementPositions) {
            # 拼接坐标向量
            newPosVec <-
                c(e$coord_vec, i)
            sub_e <-
                pluck(e$comp, i)
            
            # 添加到输出列表
            output_lst <-
                append(output_lst, list(newPosVec))
            
            # 将子元素加入工作队列
            work_lst <-
                append(work_lst, list(list(coord_vec=newPosVec, comp=sub_e)))
        }
        rm(i)
    }

    output_lst
}

# 这是期望的结果
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


问题原因分析

  1. 首次rrapply调用缺失结果:
    默认情况下,rrapply仅对非列表类型的元素执行函数逻辑,所有列表节点(包括空列表)会被直接跳过,因此仅捕获到了位置c(3,1)的非列表元素(1:3)。

  2. 二次调用遗漏中间节点:
    先标记空列表再提取位置的逻辑,仅处理了空列表和原有非列表元素,没有覆盖非空父列表的子元素位置(如顶层的位置2、3),导致结果缺失。

正确的rrapply实现方案

只需通过exclude_root = TRUE排除顶层列表,让rrapply遍历所有子节点并返回位置向量,即可得到与自定义函数一致的结果:

# 使用rrapply获取所有非顶层节点的位置向量
rrapply(
  lst,
  f = \(x, .xpos) .xpos,
  how = "flatten",
  exclude_root = TRUE
) |> 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

核心参数说明

  • exclude_root = TRUE:跳过输入的顶层列表,仅处理其所有子节点;
  • f = \(x, .xpos) .xpos:对每个子节点返回其在嵌套结构中的完整位置向量;
  • how = "flatten":将递归处理的结果压平为扁平列表,与自定义函数输出格式匹配。

内容的提问来源于stack exchange,提问作者mpettis

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 10:52:02