Unweave 序列,Kotlin functional/streaming 习语

Unweave sequence, Kotlin functional/streaming idiom

我有一系列交错数据(具有固定步幅),我想将其减少为每个 "structure" 的单个值(n*步幅值到 n 值)。

我可以只使用循环写入可变列表,并选择 reader 索引的步骤,但我正在寻找更实用和可读的方法。有什么想法吗?

例如: 输入序列由 RGB 三元组(步长 3)组成,输出为灰度。

命令式是这样的:

fun greyscale(stream:List<Byte>):List<Byte>{
    val out = ArrayList(stream.size / 3)
    var i = 0; var o = 0
    while(i < stream.size)
        out[o++]=(stream[i++] + stream[i++] + stream[i++])/3
    return out
}

如何在不显式实现函数和可变容器的情况下做出类似的东西,而是纯粹依靠 .map 等功能扩展?

排除剩余元素:

const val N = 3
fun greyscale(stream: List<Byte>) = (0 until stream.size / N)
        .map { it * N }
        .map { stream.subList(it, it + N).sum() / N }
        .map(Int::toByte)

输出

[1, 2, 3, 4, 5, 6] => [2, 5]

[1, 2, 3, 4, 5] => [2]

包括剩余元素:

const val N = 3
fun greyscale(stream: List<Byte>) = (0 until (stream.size + N - 1) / N)
        .map { it * N }
        .map { stream.subList(it, minOf(stream.size, it + N)).sum() / N }
        .map(Int::toByte)

输出

[1, 2, 3, 4, 5, 6] => [2, 5]

[1, 2, 3, 4, 5] => [2, 3]

我能做到的最好的是:

fun grayscale(rgb:List<Byte>):List<Byte>
    = rgb.foldIndexed(
        IntArray(rgb.size / 3),
        { idx, acc, i -> 
            acc[idx / 3] = acc[idx / 3] + i; acc
        }).map{ (it / 3).toByte() }

输出

in: [1, 2, 3, 4, 5, 6]

out: [2, 5]

以及 ArrayListaddlast

的变体

一种可能的方法是按元素的索引进行分组(在本例中为 /3)并将这些组映射到它们的总和。

stream.withIndex()
      .groupBy { it.index / 3 }
      .toSortedMap()
      .values
      .map { (it.sumBy { it.value } / 3).toByte() }

严格来说也是功能性的,但是使用 Rx,可以通过使用 window(long)

Observable.from(stream)
          .window(3)
          .concatMap { it.reduce(Int::plus).toObservable() }
          .map { (it / 3).toByte() }

Kotlin 1.2(里程碑 1 昨天发布)带来了集合的 chunked 方法。它将集合分成给定大小的块。您可以使用它来实现您的功能:

fun greyscale(stream: List<Byte>): List<Byte> = 
      stream.chunked(3)
            .map { (it.sum() / 3).toByte() }

与@marstran 的回答类似,在 Kotlin 1.2 中您可以使用 chunked 函数,但向其提供变换 lambda:

fun greyscale(stream: List<Byte>): List<Byte> = 
      stream.chunked(3) { it.average().toByte() }

这个变体的优点是它不会为每个三元组实例化一个新的列表,而是创建一个列表并在整个操作期间重复使用它。