无法在Swift中的Array扩展方法中创建默认闭包参数

时间:2014-10-07 04:21:56

标签: swift

作为学习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>

3 个答案:

答案 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重新实现。