【问题标题】:How to recursively implement a deep flatten on Iterable?如何递归地在 Iterable 上实现深度扁平化?
【发布时间】:2020-11-16 07:04:51
【问题描述】:

看到flatten,我一直在寻找deepFlatten,也就是说,它不仅适用于Iterable<Iterable<T>>Arrays 几乎相同,但让我们关注@ 987654326@ 现在为简洁起见),还有Iterable<Iterable<Iterable<T>>>Iterable<Iterable<Iterable<Iterable<T>>>> 等等...

当然,结果必须是List<T>,这是标准flatten() 不提供的——它会返回List<Iterable<T>(或带有更多嵌套Iterables 的List)。

我正在尝试使用 reified 泛型:

inline fun <reified E, T> Iterable<E>.deepFlatten(): List<T> = when(E::class) {
    Iterable<*>::class -> (this as Iterable<Iterable<*>>).flatten().deepFlatten()
    else -> flatten()
}

但这显然充满了错误:

  • T 似乎无法推断
  • 你不能拥有一个接口::class
  • 您不能递归 inline 函数

是否有针对上述问题的解决方法?或者,更好的是,有没有更清洁的方法来解决这个问题?


为了展示一个完整的例子,我希望能够做到:

fun main() {
    val data: List<List<List<Int>>> = listOf(
            listOf(listOf(1, 2, 3), listOf(5, 6), listOf(7)),
            listOf(listOf(8, 9), listOf(10, 11, 12, 13))
    )

    print(data.deepFlatten()) // 1 2 3 4 5 6 7 8 9 10 11 12 13
}

嵌套 Iterables 的深度(它们不必是同一类型 - 重要的是它们通常是 Iterable)可以变化。

【问题讨论】:

    标签: kotlin generics recursion flatten kotlin-reified-type-parameters


    【解决方案1】:

    在 Java 中,您可以使用 实现完全相同的行为:

    使用Collection&lt;?&gt;

    public static Stream<?> deepFlatMap(Object o) {
       if (o instanceof Collection<?>) {
           return ((Collection<?>) o).stream().flatMap(i -> deepFlatMap(i));
       }
       return Stream.of(o);
    }
    

    使用Iterable&lt;?&gt;

    public static Stream<?> deepFlatMap(Object o) {
       if (o instanceof Iterable<?>) {
           Spliterator<?> spliterator = ((Iterable<?>) o).spliterator();
           return StreamSupport.stream(spliterator, false).flatMap(i -> deepFlatMap(i));
       }
       return Stream.of(o);
    }
    

    用法很简单:deepFlatMap(list).forEach(System.out::println);

    只要我不了解 Kotlin,我希望这至少可以帮助你重写这个想法。


    编辑:只要你想指定返回目标泛型类型,你应该使用另一个包装方法(不要忘记在递归方法中更改名称):

    public static <T> Stream<T> deepFlatMap(Collection<?> collection) {
        return (Stream<T>) internalDeepFlatMap(collection);
    }
    
    public static Stream<?> internalDeepFlatMap(Object o) {
       if (o instanceof Collection<?>) {
           return ((Collection<?>) o).stream().flatMap(i -> internalDeepFlatMap(i));
       }
       return Stream.of(o);
    }
    

    明确指定泛型类型的用法:

    MyClass.<Integer>deepFlatMap(list).map(i -> i + 1).forEach(System.out::println);
    

    【讨论】:

    • 有没有机会在返回类型中保留最里面的类型?就我而言,Int?当我们有泛型时,到处乱扔Object 似乎有点不对劲。
    • 谢谢,这似乎是一个不错的解决方法,尽管我会继续尝试将其改编为 Kotlin 的 Sequence,而不是普通 Java 的 Stream
    【解决方案2】:
    fun <T> Iterable<*>.deepFlatten(): List<T> {
        val result = ArrayList<T>()
        for (element in this) {
            when (element) {
                is Iterable<*> -> result.addAll(element.deepFlatten())
                else -> result.add(element as T)
            }
        }
        return result
    }
    ...
    
    println(data.deepFlatten<Int>())
    

    你必须明确指定类型,你会失去编译时的安全性。但它可以展平任何嵌套和不同类型元素的列表 ([1, "foo", [3, "bar"]] -> [ 1, "foo", 3, "bar"])

    我更喜欢不同的解决方案。像这样的:

    typealias It2<T> = Iterable<Iterable<T>>
    typealias It3<T> = Iterable<It2<T>>
    typealias It4<T> = Iterable<It3<T>>
    typealias It5<T> = Iterable<It4<T>>
    //etc...
    
    fun <T> It3<T>.flatten2(): List<T> = flatten().flatten()
    fun <T> It4<T>.flatten3(): List<T> = flatten2().flatten()
    fun <T> It5<T>.flatten4(): List<T> = flatten3().flatten()
    //etc...
    
    ...
    println(data.flatten2())
    

    【讨论】:

      猜你喜欢
      • 2016-08-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-11-11
      • 1970-01-01
      • 2019-03-16
      • 2018-09-01
      • 2021-03-11
      相关资源
      最近更新 更多