【问题标题】:Swift 2D Array Generic Extension - issue accessing 2nd dimensionSwift 2D Array Generic Extension - 访问二维的问题
【发布时间】:2017-01-03 21:57:54
【问题描述】:

我正在尝试将以下函数转换为 2D 数组的通用扩展。

func rotate(_ input: [[Int]]) -> [[Int]]
{
  let length = input[0].count
  var value = Array(repeating: [Int](), count: length)
  for index in 0 ..< length
  {
    value[index] = input.map { $0[index] }.reversed()
  }
  return value
}

我特别困惑如何指定约束以允许我访问第二个维度。这是一次失败的尝试:

extension Array where Element: Collection, Element.Iterator.Element: Collection
{
  private func rotate()
  {
    let count = self[0].count // Element.IndexDistance instead of Int

    // Expression type 'Array<Element>' is ambiguous without more context
    var returnValue = Array(repeating: Element, count: 11)
    for index in 0 ..< count // Element.IndexDistance instead of Int
    {
      returnValue[index] = self.map { $0[index] }.reversed()
    }
    return returnValue
  }
}

【问题讨论】:

    标签: arrays swift generics


    【解决方案1】:

    问题是编译器不知道你的扩展是针对二维数组的——它只知道它是针对集合数组的。因此关联类型IndexDistanceIndex 不一定是Int

    因此,解决方案是限制您的扩展,使ElementIndexDistanceIndex 类型为Int。这将让您形成0..&lt;count 范围,因为count 现在的类型为Int (IndexDistance) – 您将能够使用Ints 为map(_:) 中的元素下标(如下标需要 Index)。

    (一旦支持concrete same-type requirements,这将是微不足道的,因为您可以简单地将Element 限制为Array,但这还不可能。)

    您还应该注意,您的约束 Element.Iterator.Element: Collection 不正确,因为这会将扩展限制为 3D 集合数组(元素是集合的数组,其中该集合的元素是集合)。

    最后,您可能必须定义一个 typealias 来表示二维数组的“内部元素”类型,因为 Swift 目前有 some limitations when working with nested types directly,例如在创建该类型的空二维数组时。

    因此,您当前方法的工作版本应如下所示:

    extension Array where Element: Collection, Element.Index == Int, Element.IndexDistance == Int {
    
        private func rotate() -> [[Element.Iterator.Element]] {
    
            typealias InnerElement = Element.Iterator.Element
    
            // in the case of an empty array, simply return an empty array
            if self.isEmpty { return [] } 
            let length = self[0].count
    
            var returnValue = [[InnerElement]](repeating: [InnerElement](), count: length)
            for index in 0..<length {
                returnValue[index] = self.map{ $0[index] }.reversed()
            }
            return returnValue
        }
    }
    

    正如@MartinR points out below,可以通过使用嵌套的map(_:) 大大简化,消除了typealias 的需要,因为我们不再需要创建“结果”数组:

    private func rotate() -> [[Element.Iterator.Element]] {
    
        if self.isEmpty { return [] }
        let length = self[0].count
    
        return (0..<length).map { index in
            self.map { $0[index] }.reversed()
        }
    }
    

    尽管请注意,将扩展限制为仅与 Int 索引一起使用并不是绝对必要的(但是没有实际区别,因为您只打算将其与 2D 数组一起使用)。另一种选择是直接遍历内部集合的indices

    为此,您只需约束您的扩展,使内部集合的Indices 具有与集合的Index 相同类型的Elements(在Array 的情况下) , IndicesCountableRange&lt;Int&gt;):

    extension Array where Element: Collection, Element.Indices.Iterator.Element == Element.Index {
        private func rotate() -> [[Element.Iterator.Element]] {
    
            if self.isEmpty { return [] }
    
            return self[0].indices.map { index in
                self.map { $0[index] }.reversed()
            }
        }
    }
    

    【讨论】:

    • 您可以将其简化为return (0..&lt;length).map { index in self.map{ $0[index] }.reversed() },而无需先创建一个空的结果数组。
    • @MartinR 太棒了,谢谢你提到它——我已经编辑了我的答案以包括这个:)
    • 感谢 Hamish 的修订,完美。直到
    • @AfricanSwift 乐于助人:)
    • @AlexanderMomchliov 总的来说,我同意你的看法。尽管在这种特殊情况下,能够使用 self[0].indices 需要为扩展设置一组单独的约束(我在上一个示例中进行了演示)。
    猜你喜欢
    • 2015-12-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-07
    • 2015-08-10
    • 1970-01-01
    • 2015-09-30
    • 1970-01-01
    相关资源
    最近更新 更多