有趣的非平凡问题!
重要更新 针对发生的所有事情,我已经重新编写了答案并删除了一些死胡同。我还计时了不同情况下的各种解决方案。
这是第一个相当简单但速度较慢的解决方案:
flatten1 <- function(x) {
y <- list()
rapply(x, function(x) y <<- c(y,x))
y
}
rapply
函数可以遍历列表并对每个子元素应用一个函数。不幸的是,它与返回值的
unlist
函数完全相同。因此我忽略了
rapply
的结果,而是通过执行
<<-
将值追加到变量
y
中。
这种方式增长
y
的方式不太高效(时间复杂度为二次方)。因此,如果有成千上万的元素,这样做会非常慢。
以下是一种更有效的方法,包括@JoshuaUlrich的简化:
flatten2 <- function(x) {
len <- sum(rapply(x, function(x) 1L))
y <- vector('list', len)
i <- 0L
rapply(x, function(x) { i <<- i+1L; y[[i]] <<- x })
y
}
首先,我找出结果的长度并预先分配向量。然后,我填写值。
正如您所看到的,这个解决方案要快得多。
这是@JoshO'Brien伟大解决方案的一个版本,基于Reduce
,但扩展以处理任意深度:
flatten3 <- function(x) {
repeat {
if(!any(vapply(x, is.list, logical(1)))) return(x)
x <- Reduce(c, x)
}
}
现在让战斗开始吧!
x <- list(NA, list("TRUE", list(FALSE), 0L))
dput( flatten1(x) )
dput( flatten2(x) )
dput( flatten3(x) )
x <- as.list(1:1e5)
system.time( flatten2(x) )
system.time( flatten3(x) )
x <-'leaf'; for(i in 1:11) { x <- list(left=x, right=x, value=i) }
system.time( flatten2(x) )
system.time( flatten3(x) )
因此,我们观察到当深度较浅时,Reduce
解决方案更快,而当深度较大时,rapply
解决方案更快!
就正确性而言,这里是一些测试:
> dput(flatten1( list(1:3, list(1:3, 'foo')) ))
list(1L, 2L, 3L, 1L, 2L, 3L, "foo")
> dput(flatten2( list(1:3, list(1:3, 'foo')) ))
list(1:3, 1:3, "foo")
> dput(flatten3( list(1:3, list(1:3, 'foo')) ))
list(1L, 2L, 3L, 1:3, "foo")
不清楚想要的结果是什么,但我倾向于使用flatten2
的结果...
flatten(list(1:3, list(1:3, 'foo')))
应该返回什么? - Tommylist(c(1, 2, 3), c(1, 2, 3), 'foo')
。解释:1:3
不是一个列表,因此不应该被展平。 - eoldpurrr::flatten
看起来是当前最佳实践(根据 @Aurèle 的回答) - geotheory