在处理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
}
在下面的谜题中,我也需要转置一个矩阵,但它不是Int
s的矩阵,所以我试着推广。在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<Array<Int>> but Array<Array<Any>> was expected
。问题是,我真的不明白这个错误消息:我以为Any
是其他任何东西的超类型?
在谷歌上搜索了一下,我想我应该使用某种类型约束语法(对不起,不确定它在Kotlin中的调用方式),因此将类型更改为fun <T: Any> transpose(xs: Array<Array<T>>): Array<Array<T>>
,但在返回行中我得到了Type mismatch: inferred type is Array<Array<Any>> but Array<Array<T>> was expected
所以我的问题是,我如何写一个transpose
矩阵,它适用于任何二维数组?
2条答案
按热度按时间bkhjykvo1#
正如你自己指出的,
Array(cols) { Array(rows) { Any() } }
行创建了一个Array<Array<Any>>
,所以如果你在泛型函数中使用它,当Array<Array<T>>
被期望时,你将无法返回它。相反,你应该使用这个lambda直接为正确的索引提供正确的值(而不是初始化为任意值并替换所有值):
我真的不明白这个错误消息:我以为Any是其他任何东西的超类型?
这是因为Kotlin中的数组在其元素类型上是不变的。如果你不知道泛型变异,那么它是关于描述泛型类型的层次结构与它们的类型参数的层次结构的比较。
例如,假设您有一个类型
Foo<T>
。现在,Int
是Any
的子类型这一事实并不一定意味着Foo<Int>
是Foo<Any>
的子类型。您可以查找术语,但基本上您有3种可能性:Foo<Int>
是Foo<Any>
的 * 子类型 *(Foo
的类型与T
的“变化相同”),我们说Foo
在其类型参数T
中是 * 协变 *。Foo<Int>
是Foo<Any>
的 * 超类型 *,我们说Foo
在其类型参数T
中是 * 逆变的 *(与T
相比,Foo
类型“以相反的方式变化”)Foo
在其类型参数T
中是 * 不变的 *Kotlin中的数组是不变的。然而,Kotlin的只读
List
在其元素的类型上是协变的。这就是为什么在Kotlin中可以将List<Int>
分配给List<Any>
类型的变量。mtb9vblg2#
另一种方法是向List或Array添加一个通用扩展方法,以便可以在示例上调用
.transpose()
方向。你可以这样调用
希望这有帮助!