【问题标题】:Can't create default closure parameter in Array extension method in Swift无法在 Swift 的数组扩展方法中创建默认闭包参数
【发布时间】:2014-10-09 04:26:35
【问题描述】:

作为学习 Swift 的练习,我尝试使用名为 comparest 的共享方法在 Array 上创建最小和最大扩展方法。一切都经过测试并且运行良好,除了一件事:我不能在没有参数的情况下使用 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 &lt; $1 }) 而不是comparest(comparator!, &lt;),请查看我的related question。)

如果我说

var array = [3, 4, 1, 9]
var min = array.minimum({ $0 })

一切正常。但是,我设计了这种方法,以便可以省略传递给最小值的参数,在这种情况下,数组元素本身用作比较值,例如

var array = [3, 4, 1, 9]
var min = array.minimum()

编译器讨厌这个。它说“无法将表达式的类型 () 转换为 Int?”。我在这里一无所知。有什么想法吗?

(顺便说一句,参数的原因是如果您想通过属性值比较对象,例如,routes.map( { $0 as MKRoute }).minimum({ $0.distance }) 将为您提供最短距离的路线。)

【问题讨论】:

    标签: swift


    【解决方案1】:

    数组中的元素不必是Comparable,这意味着您不能为所有可能的数组编写该函数。

    不幸的是,Swift 不允许您仅扩展数组的一个子集。为了使您的函数正常工作,编译器需要额外的类型信息来推断类型 C

    其他人也遇到过同样的问题:

    How can we create a generic Array Extension that sums Number types in Swift?

    您应该能够将minimum 用作全局函数,而不是使用扩展。

    【讨论】:

    • 好的,在阅读了链接的问题并考虑了类型系统之后,我明白发生了什么,但我不得不承认我不太喜欢它。 Swift 的泛型和扩展对我来说有点崩溃。也许“不完整”是最好的词。甚至 C# 在这方面做得更好,允许扩展方法专门用于受约束的泛型类型。哦,好吧,也许随着语言的成熟,写extension Array&lt;T: Comparable&gt; 之类的东西的能力就会出现。
    • @GregoryHigley 我完全同意。我真的希望能够扩展Array&lt;T&gt;,而不是“所有”数组。
    • 顺便说一下,minimum 作为一个全局函数,我觉得很难看。我看到 Apple 提供了一个 contains 函数,它也是全局的,但当然,它存在的唯一原因是因为当前扩展系统的工作方式。如果可以扩展协议而不仅仅是具体类型,contains 将是一种扩展方法。斯威夫特似乎有一些减速带。 :)
    【解决方案2】:

    我认为编译器无法推断 C 是什么类型,因为调用者表达式没有说 C 是什么类型。因此,作为其当前行为的 Swift 编译器将 Void (= ()) 替换为 C。 (为了减少错误,应该报告为错误,我会说)

    【讨论】:

      【解决方案3】:

      虽然我不会更改已接受的答案,但我想我会为后代发布我的工作解决方案。虽然我没有像suggested by Gabrielle Petronella 那样引入新的协议或幺半群,但它确实让我想到了给编译器提供更多“证据”来帮助它。

      这通过重载方法解决了默认闭包参数问题,尤其是因为事实证明每个方法都需要不同的返回类型。

      这是我想出的:

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-01-28
        • 2016-02-13
        • 2012-09-18
        • 2014-03-25
        相关资源
        最近更新 更多