递归 purrr::modify_depth() 不适用于参差不齐的列表

max*_*eld 5 recursion r list purrr tidyverse

说,这是我有点复杂和参差不齐的清单:

res <- list(
  a = TRUE,
  b = "error msg 1",
  c = list(
    TRUE,
    "error msg 2"
  ),
  d = list(
    e = "error msg 3",
    "error msg 4",  # no name for this list item just to make things interesting
    f = list(
      g = list(
        h = "error msg 5",
        i = TRUE
      )
    )
  )
)
Run Code Online (Sandbox Code Playgroud)

我现在想说,在2 深度(从顶部)应用一些函数。我的清单可以任意深入和参差不齐。

我想变得很酷和整洁,所以我认为这会奏效:

purrr::modify_depth(.x = res, .depth = 2, .f = str, .ragged = TRUE)
Run Code Online (Sandbox Code Playgroud)

但是,出乎意料的是,失败了

Error in .x[] <- .f(.x, ...) : replacement has length zero
Run Code Online (Sandbox Code Playgroud)

不能对此做出正面或反面,因为当我str()手动浏览所有列表元素时,它工作得很好;str() 始终给予一定的结果。

我猜我用.ragged =错了。

我还注意到相同的设置在is.null()用作函数时有效,而不是str(),但随后应用于实际存在的叶子(扩展列表)。

purrr::modify_depth(.x = res, .depth = 4, .f = is.null, .ragged = TRUE)
Run Code Online (Sandbox Code Playgroud)

这将创建一个统一为4 深的列表,尽管原始实际上相当参差不齐,并且只有 4 深在1 个分支。

什么我喜欢做的,是修改仅适用于那些一个列表元素n的深度确实存在,并给所有的其他修改。

我怎样才能purrr::modify_depth()做到这一点?

Jor*_*hau 1

这是一个老问题,但由于这里缺少令人满意的答案,所以就这样吧。

首先,str将输出显示到终端,但实际上不返回任何内容:

is.null(str("anything"))
#>  chr "anything"
#> [1] TRUE
Run Code Online (Sandbox Code Playgroud)

-调用purrr失败,因为.f = str尝试将NULL值分配给列表元素,这是不允许的。当前 purrr 版本(3.4.0)中的错误消息也更清楚地说明了这一点:

purrr::modify_depth(.x = res, .depth = 2, .f = str, .ragged = TRUE)
#>  logi TRUE
#> Error: Result 1 must be a single logical, not NULL of length 0
Run Code Online (Sandbox Code Playgroud)

替换str为不同的函数,purrr不再抱怨:

purrr::modify_depth(.x = res, .depth = 2, .f = is.character, .ragged = TRUE)
#> $a
#> [1] FALSE
#> 
#> $b
#> [1] "TRUE"
#> 
#> $c
#> $c[[1]]
#> [1] FALSE
#> 
#> $c[[2]]
#> [1] TRUE
#> 
#> 
#> $d
#> $d$e
#> [1] TRUE
#> 
#> $d[[2]]
#> [1] TRUE
#> 
#> $d$f
#> [1] FALSE
Run Code Online (Sandbox Code Playgroud)

然而,问题仍然存在,完全modify_depth适用于因此任何更深层次的子列表都将被折叠成一个逻辑值.f.depth = 2.f = is.character


另一个(非purrr)选项可以位于-package(base- 的扩展版本)rrapply中,以通过嵌套列表进行递归。设置我们只能修改特定深度的元素,同时保持所有其他列表元素不变:rrapplyrrapplyhow = "replace"

library(rrapply)

rrapply(res, condition = function(x, .xpos) length(.xpos) == 2, f = function(x) paste(x, "<- modified"), how = "replace")
#> $a
#> [1] TRUE
#> 
#> $b
#> [1] "error msg 1"
#> 
#> $c
#> $c[[1]]
#> [1] "TRUE <- modified"
#> 
#> $c[[2]]
#> [1] "error msg 2 <- modified"
#> 
#> 
#> $d
#> $d$e
#> [1] "error msg 3 <- modified"
#> 
#> $d[[2]]
#> [1] "error msg 4 <- modified"
#> 
#> $d$f
#> $d$f$g
#> $d$f$g$h
#> [1] "error msg 5"
#> 
#> $d$f$g$i
#> [1] TRUE
Run Code Online (Sandbox Code Playgroud)

此处,决定将函数应用condition到哪些列表元素,并将参数计算为嵌套列表中元素的位置(作为整数向量)。然后可用于评估嵌套列表中任何元素的深度。f.xposlength(.xpos)