var x = jokes[arc4random_uniform(UInt32(jokes.count))]
为什么这行代码会产生这样的错误?
编写此代码时
var x = jokes[Int(arc4random()%jokes.count)]
出现此错误
Int is not convertible to UInt32
答案 0 :(得分:3)
数组下标需要Int
,而不是UInt32
。你需要转换回来:
let x = jokes[Int(arc4random_uniform(UInt32(jokes.count)))]
如果这对你有点吵,你可能想要创建一个处理它的函数:
func randomValueLessThan(x: Int) -> Int {
return Int(arc4random_uniform(UInt32(x)))
}
或者您可以扩展Array以帮助:
extension Array {
func uniformSelection() -> T {
return self[Int(arc4random_uniform(UInt32(self.count)))]
}
}
编辑:值得深入研究一下Int(arc4random()%jokes.count)
案例,因为你可能会错误地修复它,并且它证明了Swift为什么会以它的方式工作。
让我们从你的版本开始
let n = Int(arc4random() % jokes.count)
// => Could not find an overload for 'init' that accepts the supplied arguments
这有点令人困惑。让我们简化以查看问题
let n = arc4random() % jokes.count
// => Cannot invoke '%' with an argument list of type '(UInt32, Int)'
那应该更清楚。 arc4random
返回UInt32
,jokes.count()
返回Int
。你不能模数不同的类型。你需要让他们到同一个地方。好吧,我们想要一个Int
,对吗?看似简单:
let n = Int(arc4random()) % jokes.count // WARNING!!!! Never ever do this!!!!
为什么Apple如此迂腐并强迫我们手工完成?编译器无法自动投射吗?好吧,上面的代码在64位处理器上运行良好,在32位处理器上崩溃了大约一半。这是因为通过调用Int()
,您承诺该值始终位于Int
范围内。在32位处理器上,这是32位有符号范围。但arc4random
会返回整个32位无符号范围内的值,其中包含许多不适合Int
的数字。布拉姆! (或者如果你关闭边界检查,那么它就像在C中一样破坏你的数字,这不是更好。)
这就是为什么Swift对整数转换很挑剔的原因。当你转换它时,你需要绝对确定它是一个安全的转换。你不应该只是将它们洒在它周围,直到它编译。</ p>
那说,当然你永远不应该在arc4random
上使用模数。 But that's a different question.
再补充一点,您会注意到randomValueLessThan()
中的数字转换会产生许多可能的无效情况。如果你传递一个小于0的数字,你就会崩溃(但这不应该是令人惊讶的)。如果你传递的数字大于UInt32.Max
,你也会崩溃,这会更令人惊讶,但在大多数代码中都不太可能。这一点是通过添加这些演员表,我们已经randomValueLessThan
一个部分函数。它没有在其所有输入范围内定义(它是“域”)。在“现实生活”编程中,我们一直这样做,我们只希望它永远不会咬我们。但是当你打破类型安全时,斯威夫特试图通过让它变得更加明显来帮助我们减少伤害。
uniformSelection
有类似的问题。它仅为具有少于UInt32.Max
个元素的数组定义。这些有时会感觉像无意义的角落情况,并且它们是,直到突然他们不是,你的程序崩溃。 (Array.subscript
也是一个部分函数,因为它未定义数组范围之外的值。我实际上向Apple建议Array.subscript
返回一个可选项来解释它。他们可能明智地忽略我。 )