作为学习Swift的练习,我尝试使用名为Array
的共享方法在comparest
上创建最小和最大扩展方法。一切都经过测试并且运行良好,除了一件事:我不能在没有参数的情况下使用minimum
函数。我已经指定了一个默认参数值nil
,但是当我在没有此参数的情况下调用minimum
时,Apple的Swift编译器不喜欢它。
以下是我的方法定义。他们编译得很好。
extension Array {
func comparest<C: Comparable>(comparator: T -> C, _ op: (C, C) -> Bool) -> T? {
var min = self.first
for elem in self {
if op(comparator(elem), comparator(min!)) {
min = elem
}
}
return min
}
func minimum<C: Comparable>(var _ comparator: (T -> C)? = nil) -> T? {
if comparator == nil {
comparator = { elem -> C in elem as C }
}
return self.comparest(comparator!, { $0 < $1 })
}
}
(如果您想知道为什么我说comparest(comparator!, { $0 < $1 })
而不是comparest(comparator!, <)
,请参阅我的related question。
如果我说
var array = [3, 4, 1, 9]
var min = array.minimum({ $0 })
一切顺利。但是,我设计了这个方法,以便可以省略传递给minimum的参数,在这种情况下,数组元素本身用作比较值,例如
var array = [3, 4, 1, 9]
var min = array.minimum()
编译器讨厌这个。它说“无法将表达式的类型()转换为Int?”。我在这里很无能为力。有什么想法吗?
(快速地说,参数的原因是,如果你想通过比较一个属性的值来比较对象,例如,routes.map( { $0 as MKRoute }).minimum({ $0.distance })
将为你提供最小距离的路线。)< / p>
答案 0 :(得分:1)
数组中的元素不必是Comparable
,这意味着您无法为所有可能的数组编写该函数。
不幸的是,Swift并不允许你扩展数组的子集。在其他功能中,编译器需要额外的类型信息来推断类型C
。
其他人也在努力解决同样的问题:
How can we create a generic Array Extension that sums Number types in Swift?
您应该能够将minimum
用作全局函数,而不是使用扩展名。
答案 1 :(得分:1)
我认为编译器无法推断出C
是什么类型,因为调用者表达式并不是说C
是什么。因此,Swift编译器作为其当前行为将Void
(= ()
)替换为C
。 (应该报告为减少错误的错误,我会说)
答案 2 :(得分:0)
虽然我没有改变接受的答案,但我认为我会为后人发布我的工作解决方案。虽然我没有引入新的协议或monoid作为suggested by Gabrielle Petronella,但它确实给了我提供更多&#34;证据的想法&#34;到编译器来帮助它。
这通过重载方法解决了默认的闭包参数问题,特别是因为事实证明每个方法都需要不同的返回类型。
这是我想出的:
extension Array
{
func comparest<C: Comparable>(comparable: T -> C, _ op: (C, C) -> Bool) -> T? {
var est = self.first
if count > 1 {
for elem in self[1..<count] {
if op(comparable(elem), comparable(est!)) {
est = elem
}
}
}
return est
}
func comparest<C: Comparable>(op: (C, C) -> Bool) -> C? {
var array = map({ $0 as C })
var est = array.first
if count > 1 {
for elem in array[1..<count] {
if op(elem, est!) {
est = elem
}
}
}
return est
}
func minimum<C: Comparable>(comparable: T -> C) -> T? {
return comparest(comparable) { $0 < $1 }
}
func minimum<C: Comparable>() -> C? {
return comparest() { $0 < $1 }
}
func maximum<C: Comparable>(comparable: T -> C) -> T? {
return comparest(comparable) { $0 > $1 }
}
func maximum<C: Comparable>() -> C? {
return comparest() { $0 > $1 }
}
}
可悲的是,在所有情况下,都无法进行类型推断。例如,
var x = [5, 9, 1, 3].minimum()
这会失败,因为类型系统不知道T(有效)= C.要修复它,你必须提供适当的类型:
var x: Int? = [5, 9, 1, 3].minimum()
这个有效!我的下一个任务是收紧一些东西,可能是一个元组,这样每个元素只调用一次内部comparable
闭包。最后,单个参数comparest
可能会使用reduce
重新实现。