Can';t在Swift中的数组扩展方法中创建默认闭包参数

Can';t在Swift中的数组扩展方法中创建默认闭包参数,swift,Swift,作为学习Swift的练习,我正在尝试使用名为comparest的共享方法在Array上创建最小和最大扩展方法。所有功能都经过测试,运行良好,但有一点除外:没有参数,我无法使用minimum函数。我已经指定了一个默认参数值nil,但是当我调用minimum而没有这个参数时,苹果的Swift编译器不喜欢它 下面是我的方法定义。它们编译得很好 extension Array { func comparest<C: Comparable>(comparator: T -> C,

作为学习Swift的练习,我正在尝试使用名为
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()