【问题标题】:2D Array extension Swift 3.1.1二维数组扩展 Swift 3.1.1
【发布时间】:2017-05-26 11:49:04
【问题描述】:

我正在尝试在 Swift 3.1.1 中创建一个 Array 扩展,以支持将对象添加到二维数组中的某个索引即使该数组尚未填充 .该扩展还应该提供在特定indexPath 处获取对象的能力。我在 Swift 2 中有这方面的代码,但我似乎无法将其迁移到 Swift 3。这是 Swift 2 代码:

extension Array where Element: _ArrayProtocol, Element.Iterator.Element: Any {

    mutating func addObject(_ anObject : Element.Iterator.Element, toSubarrayAtIndex idx : Int) {
        while self.count <= idx {
            let newSubArray = Element()
            self.append(newSubArray) 
        }

        var subArray = self[idx]
        subArray.append(anObject)
    }

    func objectAtIndexPath(_ indexPath: IndexPath) -> Any {
        let subArray = self[indexPath.section]
        return subArray[indexPath.row] as Element.Iterator.Element
    }
}

代码取自answer

【问题讨论】:

  • 首先在 Swift3 中没有 _ArrayProtocol 了。我想我不再需要where 子句了?不确定..
  • 相关:swift.org/migration-guide(也许你已经知道了,如果不知道,那会有很大帮助)
  • 我真的不确定如何将代码迁移到 Swift 3。我尝试不使用 where 子句而不是 Element.Iterator.Element 我只是使用 Element 但是当我尝试执行let newSubArray = Element() 我得到Element cannot be constructed because it has no accessible initiliaziers 我还尝试扩展Collection 而不是Array 并使用Iterator.Element 而不是Element 但再次没有成功。
  • 我只是无法以某种方式获得语法和一切正确..

标签: arrays swift multidimensional-array swift3


【解决方案1】:

正如 Martin 在 Swift 3.1 中所说的 in his answer here_ArrayProtocol is no longer public,因此意味着您不能将其用作扩展中的约束。

在您的情况下,一个简单的替代方法是将ArrayElement 限制为RangeReplaceableCollection - 这两者都定义了一个init() 要求,意思是“空集合”,以及一个append(_:) 方法为了向集合中添加元素。

extension Array where Element : RangeReplaceableCollection {

    typealias InnerCollection = Element
    typealias InnerElement = InnerCollection.Iterator.Element

    mutating func fillingAppend(
        _ newElement: InnerElement,
        toSubCollectionAtIndex index: Index) {

        if index >= count {
            append(contentsOf: repeatElement(InnerCollection(), count: index + 1 - count))
        }

        self[index].append(newElement)
    }
}

另请注意,我们将追加作为单个调用进行(使用append(contentsOf:),确保我们最多只需要调整外部数组的大小。

对于从给定 IndexPath 获取元素的方法,您可以将内部元素类型限制为 CollectionInt Index

// could also make this an extension on Collection where the outer Index is also an Int.
extension Array where Element : Collection, Element.Index == Int {

    subscript(indexPath indexPath: IndexPath) -> Element.Iterator.Element {
        return self[indexPath.section][indexPath.row]
    }
}

请注意,我将其设为 subscript 而不是方法,因为我觉得它更适合 Array 的 API。

您现在可以像这样简单地使用这些扩展:

var arr = [[Int]]()

arr.fillingAppend(6, toSubCollectionAtIndex: 3)
print(arr) // [[], [], [], [6]]

let indexPath = IndexPath(row: 0, section: 3)
print(arr[indexPath: indexPath]) // 6

当然,如果您事先知道外部数组的大小,fillingAppend(_:toSubCollectionAtIndex:) 方法是多余的,因为您可以通过以下方式创建嵌套数组:

var arr = [[Int]](repeating: [], count: 5)

这将创建一个包含 5 个空 [Int] 元素的 [[Int]] 数组。

【讨论】:

  • 感谢您的精彩回答!该代码具有魅力,正是我想要的!写得很好,有据可查的答案,从中学到了很多!
【解决方案2】:

没有必要将所有这些想法局限于具体的Array 类型。

这是我的解决方案。这次讨论很棒,因为我刚刚了解了RangeReplaceableCollection。合并(我认为是)两全其美,我将所有操作向下(向上?)向下(向上?)类型层次结构。

正如@Hamish 所说,下标的作用远不止Array。而且,没有必要限制索引类型,所以我们必须摆脱IndexPath。我们总是可以用typealias Index2d = ... 来加糖

extension Collection where Self.Element: Collection {
    subscript(_ indexTuple: (row: Self.Index, column: Self.Element.Index)) -> Self.Element.Element {
        get {
            return self[indexTuple.row][indexTuple.column]
        }
    }
}

为什么不在尽可能通用的级别(在CollectionRangeReplaceableCollection 之间)有一个可变版本(不幸的是,当我们重新定义subscript 时,我认为getter 不能被继承):

extension MutableCollection where Self.Element: MutableCollection {
    subscript(_ indexTuple: (row: Self.Index, column: Self.Element.Index)) -> Self.Element.Element {
        get {
            return self[indexTuple.row][indexTuple.column]
        }
        set {
            self[indexTuple.row][indexTuple.column] = newValue
        }
    }
}

然后,如果您想延迟初始化,请避免使用init:repeatedValue 并修改set 以具有自动初始化语义。您可以通过整合已接受答案的fillingAppend 想法来捕获边界溢出并在两个维度中添加缺失的空元素。

在创建 2D 初始化器时,为什么不以自然的方式扩展 repeating 的想法:

extension RangeReplaceableCollection where Element: RangeReplaceableCollection {
    init(repeating repeatedVal: Element.Element, extents: (row: Int, column: Int)) {
        let repeatingColumn = Element(repeating: repeatedVal, count: extents.column)
        self.init(repeating: repeatingColumn, count: extents.row)
    }
}

示例用法:

enum Player {
    case first
    case second
}

class Model {
    let playerGrid: Array<Array<Player>> = {
        var p = [[Player]](repeating: .first, extents: (row: 10, column: 10))
        p[(3, 4)] = .second
        print("Player at 3, 4 is: \(p[(row: 3, column: 4)])")
        return p
    }()
}

【讨论】:

    猜你喜欢
    • 2018-07-24
    • 1970-01-01
    • 2012-02-17
    • 2020-11-19
    • 1970-01-01
    • 1970-01-01
    • 2019-10-09
    • 1970-01-01
    • 2015-08-25
    相关资源
    最近更新 更多