Eri*_*rik 5 idioms idiomatic set cartesian-product kotlin
要创建两组参数的所有可能组合并对其执行操作,您可以执行以下操作:
setOf(foo, bar, baz).forEach { a ->
setOf(0, 1).forEach { b ->
/* use a and b */
}
}
Run Code Online (Sandbox Code Playgroud)
但是,如果您有更多(可能更多)参数,这很快就会变成毁灭金字塔:
setOf(foo, bar, baz).forEach { a ->
setOf(0, 1).forEach { b ->
setOf(true, false, null).forEach { c ->
setOf("Hello,", "World!").forEach { d ->
/* use a, b, c and d */
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
您可以使用for循环类似地编写此代码,也可以这样编写:
val dAction = { d: String -> /* use a, b, c and d */ }
val cAction = { c: Boolean? -> setOf("Hello,", "World!").forEach(dAction) }
val bAction = { b: Int -> setOf(true, false, null).forEach(cAction) }
val aAction = { a: Any? -> setOf(0, 1).forEach(bAction) }
setOf(foo, bar, baz).forEach(aAction)
Run Code Online (Sandbox Code Playgroud)
但是我认为这再好不过了,因为这里存在一些可读性问题:d,c,b和a的动作是相反编写的。无法推断出它们的类型规格,因此必须指定它们。与厄运金字塔相比,它被顺序颠倒了。提供可能值的集合的顺序应该无关紧要,但是,它会:您只想从一堆集合中创建任何组合,但是,在此代码中,每一行都取决于前一行。
可以采用一种惯用的方式来完成类似Python或Haskell的理解之类的工作,在这种情况下,您(几乎像数学符号一样)可以执行以下操作:
{ /* use a, b, c and d */
for a in setOf(foo, bar, baz),
for b in setOf(0, 1),
for c in setOf(true, false, null),
for d in setOf("Hello,", "World!")
}
Run Code Online (Sandbox Code Playgroud)
这很容易理解:没有过多的缩进,首先要执行您感兴趣的操作,非常清楚地定义了数据源,等等。
附注:与发生类似的问题flatMap- flatMap-...- flatMap- map。
关于如何在Kotlin中巧妙地创建n元笛卡尔积的任何想法?
Eri*_*rik 15
我自己创建了一个解决方案,所以我不必按照Omar's answer 的建议添加依赖项。
我创建了一个需要两组或更多组任意大小的函数:
fun cartesianProduct(a: Set<*>, b: Set<*>, vararg sets: Set<*>): Set<List<*>> =
(setOf(a, b).plus(sets))
.fold(listOf(listOf<Any?>())) { acc, set ->
acc.flatMap { list -> set.map { element -> list + element } }
}
.toSet()
Run Code Online (Sandbox Code Playgroud)
例子:
val a = setOf(1, 2)
val b = setOf(3, 4)
val c = setOf(5)
val d = setOf(6, 7, 8)
val abcd: Set<List<*>> = cartesianProduct(a, b, c, d)
println(abcd)
Run Code Online (Sandbox Code Playgroud)
输出:
[[1, 3, 5, 6], [1, 3, 5, 7], [1, 3, 5, 8], [1, 4, 5, 6], [1, 4, 5, 7], [1, 4, 5, 8], [2, 3, 5, 6], [2, 3, 5, 7], [2, 3, 5, 8], [2, 4, 5, 6], [2, 4, 5, 7], [2, 4, 5, 8]]
Run Code Online (Sandbox Code Playgroud)
该函数cartesianProduct返回一组列表。这些列表存在许多问题:
Any?。该函数返回 a Set<List<*>>,即Set<List<Any?>>。Pair或Triple,根据定义,大小是常数。但是,这些列表/元组的大小应该等于输入集的数量,即上例中的 4。但是,使用反射,我们可以解决这些问题。我们想要对每个列表执行的操作可以写成一个函数(例如某个类的构造函数,它也只是一个函数):
data class Parameters(val number: Int, val maybe: Boolean?) {
override fun toString() = "number = $number, maybe = $maybe"
}
val e: Set<Int> = setOf(1, 2)
val f: Set<Boolean?> = setOf(true, false, null)
val parametersList: List<Parameters> = cartesianProduct(e, f).map { ::Parameters.call(*it.toTypedArray()) }
println(parametersList.joinToString("\n"))
Run Code Online (Sandbox Code Playgroud)
输出:
number = 1, maybe = true
number = 1, maybe = false
number = 1, maybe = null
number = 2, maybe = true
number = 2, maybe = false
number = 2, maybe = null
Run Code Online (Sandbox Code Playgroud)
转换的签名(::Parameters在示例中)指定了列表内容的契约。
因为map { ::Parameters.call(*it.toTypedArray()) }不是很好,我创建了第二个扩展函数来为我做这件事:
fun <T> Set<List<*>>.map(transform: KFunction<T>) = map { transform.call(*it.toTypedArray()) }
Run Code Online (Sandbox Code Playgroud)
这样,代码就变得非常地道了:
val parametersList: List<Parameters> = cartesianProduct(e, f).map(::Parameters)
Run Code Online (Sandbox Code Playgroud)
该代码可从这个 GitHub Gist 获得,如果我改进它,我会在那里更新它。还有测试:包含任何空集的笛卡尔积返回空集,正如数学上所预期的那样。我既不是说这是一个最佳解决方案,也不是说它在数学上是合理的(并非每个数学属性都被明确实现和测试),但它适用于问题的目的。
val ints = listOf(1, 2, 3, 4)
val strings = listOf("a", "b", "c")
val booleans = listOf(true, false)
val combined = ListK.applicative()
.tupled(ints.k(), strings.k(), booleans.k())
.fix()
// or use the shortcut `arrow.instances.list.applicative.tupled`
// val combined = tupled(ints, strings, booleans)
combined.forEach { (a, b, c) -> println("a=$a, b=$b, c=$c") }
Run Code Online (Sandbox Code Playgroud)
产生笛卡尔积
a = 1,b = a,c = true
a = 1,b = b,c = true
a = 1,b = c,c = true
a = 2,b = a,c = true
a = 2,b = b,c = true
a = 2,b = c,c = true
a = 3,b = a,c = true
a = 3,b = b,c = true
a = 3,b = c,c = true
a = 4,b = a,c = true
a = 4,b = b,c = true
a = 4,b = c,c = true
a = 1,b = a,c = false
a = 1,b = b,c = false
a = 1,b = c,c = false
a = 2,b = a,c = false
a = 2,b = b,c = false
a = 2,b = c,c = false
a = 3,b = a,c = false
a = 3,b = b,c = false
a = 3,b = c,c = false
a = 4,b = a,c = false
a = 4,b = b,c = false
a = 4,b = c,c = false
| 归档时间: |
|
| 查看次数: |
766 次 |
| 最近记录: |