Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/tfs/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Kotlin 创建n元笛卡尔积的惯用方法(多组参数的组合)_Kotlin_Set_Cartesian Product_Idioms - Fatal编程技术网

Kotlin 创建n元笛卡尔积的惯用方法(多组参数的组合)

Kotlin 创建n元笛卡尔积的惯用方法(多组参数的组合),kotlin,set,cartesian-product,idioms,Kotlin,Set,Cartesian Product,Idioms,要创建两组参数的所有可能组合并对其执行操作,可以执行以下操作: setOf(foo, bar, baz).forEach { a -> setOf(0, 1).forEach { b -> /* use a and b */ } } 但是,如果您有(可能有许多)更多的参数,这将很快变成: 您可以使用为循环编写类似的代码,或者以不同的方式编写: val dAction = { d: String -> /* use a, b, c and d *

要创建两组参数的所有可能组合并对其执行操作,可以执行以下操作:

setOf(foo, bar, baz).forEach { a ->
    setOf(0, 1).forEach { b ->
        /* use a and b */
    }
}
但是,如果您有(可能有许多)更多的参数,这将很快变成:

您可以使用
循环编写类似的代码,或者以不同的方式编写:

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)
但我不认为这更好,因为这里有一些可读性问题:d、c、b和a的操作是反写的。无法推断它们的类型规范,因此必须指定它们。与厄运金字塔相比,它是颠倒的。提供可能值的集合的顺序应该无关紧要,但确实如此:您只需要从一组集合中创建任意组合,然而,在这段代码中,每一行都取决于前一行

如果有一种惯用的方式来做类似或理解的事情,那将非常好,在这种方式中,您()可以做如下事情:

{ /* 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!")
}
这很容易理解:没有过度的缩进,你感兴趣的动作先开始,数据源定义得非常清楚,等等

旁注:
flatMap
-
flatMap
-…-
flatMap
-
-
map
也会出现类似问题

关于如何在Kotlin中巧妙地创建n元笛卡尔产品,有什么想法吗?

我建议使用
列表中的。请参见示例:

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") }
产生笛卡尔积的

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


我已经自己创建了一个解决方案,所以我不必像所建议的那样添加依赖项

我创建了一个函数,可以使用两个或多个任意大小的集合:

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()
函数
cartesianProduct
返回一组列表。这些列表存在许多问题:


  • 任何类型信息都将丢失,因为返回的集包含包含输入集类型并集的列表。这些列表元素的返回类型是
    Any?
    。该函数返回一个
    Set
    ,即
    Set,下面是使用任意组合器函数执行此操作的另一种方法:

    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"))
    
    fun <T, U, V> product(xs: Collection<T>, ys: Collection<U>, combiner: (T, U) -> V): Collection<V> =
        xs.flatMap { x ->
            ys.map { y ->
                combiner(x, y)
            }
        }
    
    operator fun <T, U> Set<T>.times(ys: Set<U>): Set<Set<*>> =
        product(this, ys) { x, y ->
            if (x is Set<*>) x + y // keeps the result flat
            else setOf(x, y)
        }.toSet()
    
    operator fun <T, U> List<T>.times(ys: List<U>): List<List<*>> =
        product(this, ys) { x, y ->
            if (x is List<*>) x + y // keeps the result flat
            else listOf(x, y)
        }.toList()
    
    // then you can do
    
    listOf(1, 2, 3) * listOf(true, false)
    
    // or
    
    setOf(1, 2, 3) * setOf(true, false)
    
    // you can also use product's combiner to create arbitrary result objects, e.g. data classes
    
    
    fun产品(xs:Collection,ys:Collection,combiner:(T,U)->V):Collection=
    xs.flatMap{x->
    ys.map{y->
    组合器(x,y)
    }
    }
    操作员乐趣设置。时间(ys:Set):设置=
    乘积(this,ys){x,y->
    如果设置了(x),则x+y//将保持结果平坦
    (x,y)的else集
    }.toSet()
    操作员乐趣列表。时间(ys:List):列表=
    乘积(this,ys){x,y->
    如果(x是列表),则x+y//保持结果平坦
    else列表(x,y)
    }托利斯先生()
    //那你就可以了
    listOf(1,2,3)*listOf(真、假)
    //或
    集合(1,2,3)*集合(真,假)
    //您还可以使用产品的组合器创建任意结果对象,例如数据类
    
    延迟生成一系列结果的解决方案。它接受列表,但不接受集合

    fun产品(vararg iterables:List):Sequence=Sequence{
    require(iterables.map{it.size.toLong()}.reduce(Long::times)
    val result=ArrayList()
    (0到numberOfIterables)。forEach{iterableIndex->
    val elementIndex=产品/L剩余[iterableIndex]%L长度[iterableIndex]
    结果.添加(iterables[iterableIndex][elementIndex])
    }
    收益率(result.toList())
    }
    }
    
    算法的所有学分都归他和他的答案
    通过使用char生成5x5 2-d阵列进行测试,在我的2核机器上生成33554432个产品需要约4.4秒。

    以下是@Erik答案的一个改编版本,它维护了类型安全,并可用于功能链:

    fun <T> Collection<Iterable<T>>.getCartesianProduct(): Set<List<T>> =
        if (isEmpty()) emptySet()
        else drop(1).fold(first().map(::listOf)) { acc, iterable ->
            acc.flatMap { list -> iterable.map(list::plus) }
        }.toSet()
    
    fun Collection.getCartesianProduct():Set=
    如果(isEmpty())清空()
    else drop(1).fold(first().map(::listOf)){acc,iterable->
    acc.flatMap{list->iterable.map(list::plus)}
    }.toSet()
    
    如果我们想保持输入大小至少为两个的要求,我将如何重写该类型安全解决方案:

    fun <T> cartesianProduct(a: Set<T>, b: Set<T>, vararg sets: Set<T>): Set<List<T>> =
        (setOf(a, b).plus(sets))
            .fold(listOf(listOf<T>())) { acc, set ->
                acc.flatMap { list -> set.map { element -> list + element } }
            }
            .toSet()
    
    fun cartesianProduct(a:Set,b:Set,vararg Set:Set):Set=
    (一组(a、b)加上(组))
    .fold(listOf(listOf()){acc,set->
    acc.flatMap{list->set.map{element->list+element}
    }
    .toSet()
    
    很高兴看到Arrow具有此功能。您可以从函数式编程库中看到这一点。我不希望仅仅为了能够做到这一点而添加额外的依赖项。这个答案对于Arrow的用户来说非常好,或者正在寻找使用Arrow的原因。我将等待一个不同的答案,它可能会揭示一些漂亮的Kotlin的问题构造,或者只是一个普通的:不,没有难看的代码就无法完成。;)从Arrow 0.12.0开始,此方法已被弃用,并且它不再适用于Arrow 0.13.0。非常不幸的是,我发现它优雅而简洁。对于这个答案,仍然存在类型差异的问题,因为星形投影不知道或不关心传递给这个函数的集合中的类型。这以后在使用类型时可能会导致问题,因为它们现在有类型
    Any?
    ,必须向下转换。^我刚刚要提到这一点,您不仅会丢失类型推断,还会犯类似
    cartesianProduct(setOf(1,2),setOf(“a”,“b”))的错误。forEach{(a,b,c)->println(“a=$a,b=$b,c=$c”)}
    it将很好地编译,但在运行时崩溃。此外,返回的值是
    number = 1, maybe = true
    number = 1, maybe = false
    number = 1, maybe = null
    number = 2, maybe = true
    number = 2, maybe = false
    number = 2, maybe = null
    
    fun <T> Set<List<*>>.map(transform: KFunction<T>) = map { transform.call(*it.toTypedArray()) }
    
    val parametersList: List<Parameters> = cartesianProduct(e, f).map(::Parameters)
    
    fun <T, U, V> product(xs: Collection<T>, ys: Collection<U>, combiner: (T, U) -> V): Collection<V> =
        xs.flatMap { x ->
            ys.map { y ->
                combiner(x, y)
            }
        }
    
    operator fun <T, U> Set<T>.times(ys: Set<U>): Set<Set<*>> =
        product(this, ys) { x, y ->
            if (x is Set<*>) x + y // keeps the result flat
            else setOf(x, y)
        }.toSet()
    
    operator fun <T, U> List<T>.times(ys: List<U>): List<List<*>> =
        product(this, ys) { x, y ->
            if (x is List<*>) x + y // keeps the result flat
            else listOf(x, y)
        }.toList()
    
    // then you can do
    
    listOf(1, 2, 3) * listOf(true, false)
    
    // or
    
    setOf(1, 2, 3) * setOf(true, false)
    
    // you can also use product's combiner to create arbitrary result objects, e.g. data classes
    
    
    fun <T> Collection<Iterable<T>>.getCartesianProduct(): Set<List<T>> =
        if (isEmpty()) emptySet()
        else drop(1).fold(first().map(::listOf)) { acc, iterable ->
            acc.flatMap { list -> iterable.map(list::plus) }
        }.toSet()
    
    fun <T> cartesianProduct(a: Set<T>, b: Set<T>, vararg sets: Set<T>): Set<List<T>> =
        (setOf(a, b).plus(sets))
            .fold(listOf(listOf<T>())) { acc, set ->
                acc.flatMap { list -> set.map { element -> list + element } }
            }
            .toSet()