【问题标题】:Generic transpose (or anything else really!) in KotlinKotlin 中的通用转置(或其他任何东西!)
【发布时间】:2021-12-05 00:53:17
【问题描述】:

在处理Advent of Code 难题时,我发现自己定义了一个函数来转置整数矩阵:

fun transpose(xs: Array<Array<Int>>): Array<Array<Int>> {
    val cols = xs[0].size // 3
    val rows = xs.size // 2
    var ys = Array(cols) { Array(rows) { 0 } }
    for (i in 0..rows - 1) {
        for (j in 0..cols - 1)
            ys[j][i] = xs[i][j]
    }
    return ys
}

原来在下面的谜题中我还需要转置一个矩阵,但它不是Ints 的矩阵,所以我试图概括。在 Haskell 中我会有一些类型的东西

transpose :: [[a]] -> [[a]]

为了在 Kotlin 中复制它,我尝试了以下方法:

fun transpose(xs: Array<Array<Any>>): Array<Array<Any>> {
    val cols = xs[0].size
    val rows = xs.size
    var ys = Array(cols) { Array(rows) { Any() } } // maybe this is the problem?
    for (i in 0..rows - 1) {
        for (j in 0..cols - 1)
            ys[j][i] = xs[i][j]
    }
    return ys
}

这看起来不错,但事实并非如此。事实上,当我尝试在原始整数矩阵上调用它时,我得到Type mismatch: inferred type is Array&lt;Array&lt;Int&gt;&gt; but Array&lt;Array&lt;Any&gt;&gt; was expected。 问题是,我不太明白这条错误信息:我认为 Any 是其他任何东西的超类型?

谷歌搜索我想我明白我应该使用某种类型约束语法(抱歉,不确定它在 Kotlin 中的调用方式),因此将类型更改为 fun &lt;T: Any&gt; transpose(xs: Array&lt;Array&lt;T&gt;&gt;): Array&lt;Array&lt;T&gt;&gt;,但随后在返回行我得到 @ 987654331@

所以我的问题是,如何编写适用于任何二维数组的transpose 矩阵?

【问题讨论】:

  • 如果有兴趣,您可以查看 my solution 了解 AdventOfCode 拼图。在那里,我从行列表中创建了一个列列表。
  • 很高兴在 Kotlin 中看到另一个 AoC 存储库! :) 我的是here。我这几天时间不多,所以我想我以后会回到第 2 部分,所以我不会看你的解决方案,不要有任何剧透

标签: kotlin generics polymorphism


【解决方案1】:

正如您自己指出的那样,Array(cols) { Array(rows) { Any() } } 行创建了一个 Array&lt;Array&lt;Any&gt;&gt;,因此如果您在泛型函数中使用它,您将无法在需要 Array&lt;Array&lt;T&gt;&gt; 时返回它。

相反,您应该使用此 lambda 直接为正确的索引提供正确的值(而不是初始化为任意值并替换所有值):

inline fun <reified T> transpose(xs: Array<Array<T>>): Array<Array<T>> {
    val cols = xs[0].size
    val rows = xs.size
    return Array(cols) { j ->
        Array(rows) { i -> 
            xs[i][j]
        }
    }
}

我不太明白这个错误信息:我认为 Any 是其他任何东西的超类型?

这是因为arrays in Kotlin are invariant in their element type。如果您不了解泛型差异,那就是描述泛型类型的层次结构与其类型参数的层次结构的比较。

例如,假设您有一个类型Foo&lt;T&gt;。现在,IntAny 的子类型这一事实并不一定意味着Foo&lt;Int&gt;Foo&lt;Any&gt; 的子类型。您可以查找行话,但基本上您在这里有 3 种可能性:

  • 如果Foo&lt;Int&gt;Foo&lt;Any&gt;子类型Foo 类型为“vary和T一样”
  • 如果Foo&lt;Int&gt;Foo&lt;Any&gt;超类型,我们说Foo 在其类型参数T逆变Foo 类型“变化相反的方式”与T相比)
  • 如果以上都不能说,我们说Foo 在其类型参数T 中是不变的

Kotlin 中的数组是不变的。然而,Kotlin 的只读 List 在其元素的类型上是协变的。这就是为什么在 Kotlin 中可以将 List&lt;Int&gt; 分配给 List&lt;Any&gt; 类型的变量。

【讨论】:

  • 谢谢你,这真是一个清晰而全面的解释!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-09-12
  • 2017-03-12
  • 2015-06-17
  • 2012-06-16
  • 2011-06-11
相关资源
最近更新 更多