我正在尝试将以下函数转换为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 } }
问题在于,编译器不知道您的扩展名是针对2D数组的,而只是知道它针对集合的数组。因此,关联的类型IndexDistance和Index不一定Int。
IndexDistance
Index
Int
因此,解决的办法是限制您的扩展,使Element的IndexDistance和Index 有 型Int。这将使您形成范围0..<count,就像count现在将是Int(IndexDistance)类型–并且将能够map(_:)用Ints 下标元素(如下标所期望的Index)。
Element
0..<count
count
map(_:)
(一旦支持具体的同类型需求,这将是微不足道的,因为您可以简单地将约束Element为an Array,但这还不可能。)
Array
您还应该注意,您的约束Element.Iterator.Element: Collection是不正确的,因为它将限制对3D集合数组的扩展(该数组的元素是一个集合,该集合的元素是一个集合)。
Element.Iterator.Element: Collection
因此,当前方法的工作版本如下所示:
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在下面指出的那样,可以通过使用nested大大简化它map(_:),从而消除了对a的需要,typealias因为我们不再需要创建“结果”数组:
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
为此,您只需要限制扩展名,以使内部集合Indices具有Element与集合相同类型Index的(对于Array,Indices则为CountableRange<Int>):
Indices
CountableRange<Int>
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() } } }