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]
以及 ArrayList
与 add
和 last
的变体
一种可能的方法是按元素的索引进行分组(在本例中为 /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() }
这个变体的优点是它不会为每个三元组实例化一个新的列表,而是创建一个列表并在整个操作期间重复使用它。
我有一系列交错数据(具有固定步幅),我想将其减少为每个 "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]
以及 ArrayList
与 add
和 last
一种可能的方法是按元素的索引进行分组(在本例中为 /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() }
这个变体的优点是它不会为每个三元组实例化一个新的列表,而是创建一个列表并在整个操作期间重复使用它。