Интересная нетривиальная задача!
ГЛАВНОЕ ОБНОВЛЕНИЕ Со всем, что произошло, я переписал ответ и удалил несколько тупиков. Я также приурочил различные решения к разным случаям.
Вот первое, довольно простое, но медленное решение:
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)
}
}
А теперь пусть битва начнется!
# Check correctness on original problem
x <- list(NA, list("TRUE", list(FALSE), 0L))
dput( flatten1(x) )
#list(NA, "TRUE", FALSE, 0L)
dput( flatten2(x) )
#list(NA, "TRUE", FALSE, 0L)
dput( flatten3(x) )
#list(NA_character_, "TRUE", FALSE, 0L)
# Time on a huge flat list
x <- as.list(1:1e5)
#system.time( flatten1(x) ) # Long time
system.time( flatten2(x) ) # 0.39 secs
system.time( flatten3(x) ) # 0.04 secs
# Time on a huge deep list
x <-'leaf'; for(i in 1:11) { x <- list(left=x, right=x, value=i) }
#system.time( flatten1(x) ) # Long time
system.time( flatten2(x) ) # 0.05 secs
system.time( flatten3(x) ) # 1.28 secs
... Итак, мы наблюдаем, что решение 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")
Непонятно, какой результат желаем, но я склоняюсь к результату от _15 _...
person
Tommy
schedule
15.11.2011
flatten( list(1:3, list(1:3, 'foo')) )
вернуть? - person Tommy   schedule 16.11.2011list(c(1, 2, 3), c(1, 2, 3), 'foo')
. Объяснение:1:3
не является списком, поэтому его не следует сводить. - person eold   schedule 16.11.2011purrr::flatten
выглядит как текущая передовая практика (согласно ответу @Aurèle) - person geotheory   schedule 13.09.2017