2016-08-27 4 views
2

다음 함수를 2D 배열의 일반 확장으로 변환하려고합니다.스위프트 2D 배열 일반 확장 - 2 차원 액세스 문제

나는 두 번째 차원에 액세스 할 수 있도록 제약 조건을 지정하는 방법을 자세히 설명하지 않았습니다.

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

답변

3

문제는 컴파일러가 확장 차원 배열에 대한 것을 알고하지 않습니다 - 그것은 그냥 컬렉션의 배열에 대한 있다고 알고 : 여기에 시도가 실패한입니다. 따라서 관련 유형 IndexDistanceIndex은 반드시 Int 일 필요는 없습니다.

솔루션 따라서 Element의이 IndexDistanceIndex유형 Int되도록 확장을 제한하는 것입니다. 이 count 지금 형 Int (IndexDistance)이 될 것입니다 당신이, 범위 0..<count을 형성하게됩니다 - 당신은 (첨자가 Index 기대대로) map(_:)Int와의 내의 요소를 첨자 할 수있을 것입니다.

(당신이 단순히 Array가되도록 Element을 제한 할 수 이것은 그러나이 아직 가능하지 않다, 지원 concrete same-type requirements 한 번 할 사소한 것입니다.) 또한주의해야한다

당신의 제약 Element.Iterator.Element: Collection을 이것이 확장이 컬렉션의 3D 배열 (요소가 컬렉션 인 요소, 컬렉션의 요소가 컬렉션 인 배열)로 제한됩니다.

마지막으로 스위프트가 현재 some limitations when working with nested types directly 인 경우 (예 : 해당 유형의 빈 2D 배열을 만들 때) typealias을 정의하여 2D 배열의 '내부 요소'유형을 나타내야 할 수 있습니다.

따라서, 현재 방법의 작업 버전이 다음과 같이합니다 :

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으로, 더 우리와 같은 typealias의 필요성을 제거, 중첩 map(_:)를 사용하지하여 상당히 단순화 할 수있다 더 이상 '결과'배열을 만들 필요가 : 그

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

비록 메모를 Y의 구속 Int 인덱스로만 작업 할 수있는 확장 기능은 반드시 필요한 것은 아닙니다 (단, 2D 배열과 함께 사용하려는 경우 실제적인 차이는 없습니다). 또 다른 대안은 내부 컬렉션의 indices을 직접 반복하는 것입니다.

이 작업을 수행하기 위해, 당신은 단순히 내부 콜렉션의 Indices 컬렉션의 Index와 동일한 유형의 Element의 가질 수 있도록 확장을 제한해야한다 (AN Array의 경우를 IndicesCountableRange<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

눈부신 + 귀중한 설명 부탁드립니다. – AfricanSwift

+1

먼저 빈 결과 배열을 생성하지 않고'return (0 ..

+0

고마워요 마틴 ... – AfricanSwift