我正在尝试向Array类添加功能。
所以我试图添加一个类似于Ruby的词典的sort()
为此,如果尊重Swift的sort(),我选择名称'ricSort()'。
但编译器表示它不能为'<'找到重载,尽管'sort({$ 0,$ 1}'by
本身工作正常。
为什么呢?
var myArray:Array = [5,4,3,2,1]
myArray.sort({$0 < $1}) <-- [1, 2, 3, 4, 5]
myArray.ricSort() <-- this doesn't work.
答案 0 :(得分:1)
这是一个接近您所寻找的解决方案,然后进行讨论。
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语言的修改建议可能会使其在未来减少痛苦。特别是创建参数化扩展应该有所帮助。
答案 1 :(得分:0)
您收到错误的原因是编译器无法保证存储在Array中的类型可以与<
运算符进行比较。
您可以在数组上看到相同的排序闭包,其类型可以使用<
进行比较,如Int
:
var list = [3,1,2]
list.sort {$0 < $1}
但如果您尝试使用无法与<
进行比较的类型,则会出现错误:
var URL1 = NSURL()
var URL2 = NSURL()
var list = [URL1, URL2]
list.sort {$0 < $1} // error
特别是你可以在Swift中省略所有语法,我没有理由为此定义一个方法。以下内容有效且按预期工作:
list.sort(<)
您可以这样做,因为<
实际上定义了一个函数,该函数需要两个Ints
并返回一个Bool
,就像sort
方法所期望的那样。