Can';t在Swift中的数组扩展方法中创建默认闭包参数
作为学习Swift的练习,我正在尝试使用名为Can';t在Swift中的数组扩展方法中创建默认闭包参数,swift,Swift,作为学习Swift的练习,我正在尝试使用名为comparest的共享方法在Array上创建最小和最大扩展方法。所有功能都经过测试,运行良好,但有一点除外:没有参数,我无法使用minimum函数。我已经指定了一个默认参数值nil,但是当我调用minimum而没有这个参数时,苹果的Swift编译器不喜欢它 下面是我的方法定义。它们编译得很好 extension Array { func comparest<C: Comparable>(comparator: T -> C,
comparest
的共享方法在Array
上创建最小和最大扩展方法。所有功能都经过测试,运行良好,但有一点除外:没有参数,我无法使用minimum
函数。我已经指定了一个默认参数值nil
,但是当我调用minimum
而没有这个参数时,苹果的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 })
}
}
一切正常。然而,我设计了这个方法,这样就可以忽略传递到最小值的参数,在这种情况下,数组元素本身被用作比较值,例如
var array = [3, 4, 1, 9]
var min = array.minimum()
编译器讨厌这个。它表示“无法将表达式的类型()转换为Int?”。我在这里一窍不通。有什么想法吗
(顺便说一句,使用该参数的原因是,如果您想通过属性的值来比较对象,例如,
routes.map({$0 As MKRoute})。minimum({$0.distance})
将为您提供具有最小距离的路由。)数组中的元素不必是可比的,这意味着您不能为所有可能的数组编写该函数
不幸的是,Swift不允许只扩展数组的一个子集。换句话说,为了让函数工作,编译器需要额外的类型信息来推断类型C
其他人也在为同样的问题而挣扎:
您应该能够使用minimum
作为全局函数,而不是使用扩展名。我认为编译器无法推断C
是什么类型,因为调用方表达式没有说C
是什么。因此,Swift编译器作为其当前行为将Void
(=()
)替换为C
。(我会说,这应该被报告为减少错误的错误)虽然我不会改变被接受的答案,但我认为我应该将我的工作解决方案发布给后代。虽然我没有引入一个新的协议或幺半群,但它确实给了我一个想法,给编译器更多的“证据”来帮助它
这通过重载方法解决了默认闭包参数问题,特别是因为每个方法都需要不同的返回类型
以下是我的想法:
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 }
}
}
这将失败,因为类型系统不知道T(有效)=C。要修复它,您必须提供适当的类型:
var x: Int? = [5, 9, 1, 3].minimum()
这管用!我的下一个任务是更紧一些,可能是使用一个元组,以便每个元素只调用一次内部compariable
闭包。最后,单个参数comparest
可能可以通过reduce
重新实现。好的,在阅读链接问题并思考类型系统后,我了解发生了什么,但我不得不承认我不太喜欢它。斯威夫特的泛型和扩展对我来说有点破碎。也许“不完整”是最好的词。甚至C#在这方面也做得更好,允许扩展方法专门处理受约束的泛型类型。哦,好吧,也许随着语言的成熟,编写类似扩展数组
之类的东西的能力将会出现。@GregoryHigley我完全同意。我真的希望能够扩展数组
,而不是“所有”数组。顺便说一句,最小值
作为一个全局函数对我来说很难看。我看到苹果提供了一个contains
函数,它也是全局的,但当然,唯一存在的原因是因为当前的扩展系统是如何工作的。如果协议可以扩展而不仅仅是具体的类型,contains
将是一种扩展方法。斯威夫特似乎有一些减速带。:)
var x = [5, 9, 1, 3].minimum()
var x: Int? = [5, 9, 1, 3].minimum()