这是一个接近您正在寻找的解决方案,然后进行讨论。
var a:Int[] = [5,4,3,2,1]
extension Array {
func ricSort(fn: (lhs: T, rhs: T) -> Bool) -> T[] {
let tempCopy = self.copy()
tempCopy.sort(fn)
return tempCopy
}
}
var b = a.ricSort(<) // [1, 2, 3, 4, 5]
原始代码有两个问题。第一个相当简单的错误是Array.sort 不返回任何值(表示为(),在其他一些语言中称为void 或Unit)。因此,以return self.sort({$0 < $1}) 结尾的函数实际上并没有返回任何内容,我认为这与您的意图背道而驰。所以这就是为什么它需要return tempCopy 而不是return self.sort(...)。
与您的版本不同,此版本复制了数组以进行变异,然后将其返回。您可以轻松地对其进行更改以使其自身发生变异(如果您检查编辑历史记录,该帖子的第一个版本就是这样做的)。有些人认为 sort 的行为(改变数组,而不是返回一个新数组)是不可取的。这种行为已经在一些 Apple 开发者名单上进行了辩论。见http://blog.human-friendly.com/swift-arrays-the-bugs-the-bad-and-the-ugly-incomplete
另一个问题是编译器没有足够的信息来生成将实现ricSort 的代码,这就是您收到类型错误的原因。听起来您想知道为什么当您使用 myArray.sort 时它能够工作,但当您尝试在 Array 上的函数内执行相同的代码时却不能。
原因是因为你告诉编译器为什么 myArray 包含:
var myArray:Array = [5,4,3,2,1]
这是的简写
var myArray: Array<Int> = [5,4,3,2,1]
换句话说,编译器推断 myArray 由 Int 组成,并且恰好 Int 符合提供< 运算符的Comparable 协议(请参阅:https://developer.apple.com/library/prerelease/ios/documentation/General/Reference/SwiftStandardLibraryReference/Comparable.html#//apple_ref/swift/intf/Comparable)[1]。从文档中,您可以看到< 有以下签名:
@infix func < (lhs: Self, rhs: Self) -> Bool
根据您的语言背景,< 是根据语言定义的,而不仅仅是内置运算符,您可能会感到惊讶。但如果你仔细想想,< 只是一个接受两个参数并返回 true 或 false 的函数。 @infix 表示它可以出现在它的两个函数之间,所以不用写< 1 2。
(此处的类型“Self”表示“无论该协议实现的类型是什么”,请参阅https://developer.apple.com/library/prerelease/ios/documentation/swift/conceptual/swift_programming_language/Declarations.html#//apple_ref/doc/uid/TP40014097-CH34-XID_597 中的协议相关类型声明)
将此与 Array.sort 的签名进行比较:isOrderedBefore: (T, T) -> Bool
这是通用签名。当编译器处理这行代码时,它知道真正的签名是isOrderedBefore: (Int, Int) -> Bool
编译器的工作现在很简单,它只需要弄清楚,是否有一个名为< 的函数与预期的签名相匹配,即接受两个 Int 类型的值并返回一个 Bool 的函数。显然<确实匹配了这里的签名,所以编译器允许在这里使用该函数。它有足够的信息来保证< 将适用于数组中的所有值。这与动态语言形成对比,动态语言无法预料到这一点。您必须实际尝试执行排序才能了解类型是否可以实际排序。一些动态语言,如 JavaScript,将尽一切可能的尝试继续而不会失败,因此 0 < "1" 等表达式的计算正确,而其他语言,如 Python 和 Ruby,将抛出异常。 Swift 两者都不做:它会阻止您运行程序,直到您修复代码中的错误。
那么,为什么 ricSort 不起作用?因为在您创建特定类型的实例之前,没有类型信息可供它使用。它无法推断ricSort 是否正确。
例如,假设我没有myArray,而是这样:
enum Color {
case Red, Orange, Yellow, Green, Blue, Indigo, Violet
}
var myColors = [Color.Red, Color.Blue, Color.Green]
var sortedColors = myColors.ricSort() // Kaboom!
在这种情况下,myColors.ricSort 将基于类型错误而失败,因为尚未为 Color 枚举定义 <。这可能发生在动态语言中,但绝不应该发生在具有复杂类型系统的语言中。
我还能使用myColors.sort吗?当然。我只需要定义一个函数,它采用两种颜色,然后以对我的域有意义的顺序返回(EM 波长?字母顺序?最喜欢的颜色?):
func colorComesBefore(lhs: Color, rhs: Color) -> Bool { ... }
然后,我可以将其传递给:myColors.sort(colorComesBefore)
希望这表明,为了使ricSort 工作,我们需要以这样一种方式构造它,即它的定义保证当它被编译时,它可以被证明是正确的,而不必运行它或编写单元测试。
希望这能解释解决方案。对 Swift 语言的一些提议修改可能会在未来减轻这种痛苦。特别是创建参数化扩展应该会有所帮助。