Swift 2D Array Generic Extension - issue accessing 2nd dimension

后端 未结 1 1630
余生分开走
余生分开走 2020-12-06 22:48

I\'m trying to transform the following function into a generic extension for 2D Arrays.

func rotate(_ input: [[Int]]) -> [[Int]]
{
  let length = input[0]         


        
相关标签:
1条回答
  • 2020-12-06 23:09

    The problem is that the compiler doesn't know that your extension is for 2D arrays – it just knows that it's for arrays of collections. Therefore the associated types IndexDistance and Index aren't necessarily Int.

    The solution therefore is to constrain your extension so that the Element's IndexDistance and Index are of type Int. This will let you form the range 0..<count, as count will now be of type Int (IndexDistance) – and you will be able to subscript the elements within the map(_:) with Ints (as the subscript expects an Index).

    (This will be trivial to do once concrete same-type requirements are supported, as you could simply constrain the Element to be an Array, however this is not yet possible.)

    You should also note that your constraint Element.Iterator.Element: Collection is incorrect, as that'll constrain the extension to 3D arrays of collections (An array where the element is a collection, where the elements of that collection are a collection).

    Finally, you may have to define a typealias to represent the 'inner element' type of the 2D array, as Swift currently has some limitations when working with nested types directly, for example when creating an empty 2D array of that type.

    Therefore, a working version of your current method would look something like this:

    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
        }
    }
    

    Which, as @MartinR points out below, could be simplified considerably by using a nested map(_:), eliminating the need of a typealias as we no longer need to create a 'result' array:

    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()
        }
    }
    

    Although note that the constraining of your extension to only work with Int indices isn't strictly necessary (however makes no practical difference as you're only intending to use this with 2D arrays). Another alternative is to just iterate directly over the inner collection's indices.

    In order to do this, you simply have to constrain your extension so that the inner collection's Indices have Elements of the same type as the Index of the collection (in the case of an Array, Indices is a 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()
            }
        }
    }
    
    0 讨论(0)
提交回复
热议问题