【问题标题】:Local reference to inner array in swiftswift中对内部数组的本地引用
【发布时间】:2016-04-24 04:30:51
【问题描述】:

我来自 C# 世界,习惯于将数组作为引用类型。据我了解,在 swift 数组中是值类型,但它们试图作为参考类型。

我实际上不知道如何问我需要什么(我认为当我需要知道答案才能提出问题时就是这种情况),但在 C# 中我会说我需要存储对内部的引用将锯齿状数组的数组转换为局部变量。

考虑以下代码:

// a function to change row values in-place
func processRow(inout row : [Int], _ value : Int)
{
    for col in 0..<row.count
    {
        row[col] = value;
    }
}

// a function to change matrix values in-place
func processMatrix(inout matrix : [[Int]])
{
    for rowIdx in 0..<matrix.count
    {
        // (1) Works with array in-place
        processRow(&(matrix[rowIdx]), -1)

        // (2) Creates local copy
        var r = matrix[rowIdx]; // <--- What to write here to not make a copy but still have this local variable?
        processRow(&r, -2);
    }
}

var matrix = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
]

processMatrix(&matrix)

print(matrix) // outputs [[-1, -1, -1], [-1, -1, -1], [-1, -1, -1]]

这里是 Swift 沙盒 http://swiftlang.ng.bluemix.net/#/repl/8608824e18317e19b32113e1aa08deeb4ec5ab96ed8cdbe1dbfb4e753e87d528

在这里我想就地处理多维数组,这样我就不会创建数组的副本或数组的一部分。

在选项 (1) 中,我将所有内容更改为“-1”,它可以工作,但为此使用了附加功能。

在选项(2)中,我尝试使用局部变量来存储matrix[rowIdx],但它实际上创建了内部数组的副本——这不是我想要的;使用此变量会更改数组的副本,而不是原始的。

我怎样才能获得类似于选项 (1) 的结果,但使用局部变量而不是函数?即如何获取内部数组的引用并将其放入局部变量中?

我会理解回答“这是不可能的”,但我希望这样的答案伴随着一些 Apple 参考。

【问题讨论】:

    标签: c# arrays swift jagged-arrays mutability


    【解决方案1】:

    在处理可变数据时,Swift 数组非常痛苦。有两种方法可以解决这个问题:

    1) 使用 NSMutableArray

    import Foundation
    
    func processRow(_ row : NSMutableArray, _ value : Int) {
        for col in 0..<row.count {
            row[col] = value;
        }
    }
    
    func processMatrix(_ matrix : inout [NSMutableArray]) {
        for rowIdx in 0..<matrix.count {
            let r = matrix[rowIdx]
            processRow(r, -2);
        }
    }
    
    var matrix = [
        NSMutableArray(array: [1, 2, 3]),
        NSMutableArray(array: [4, 5, 6]),
        NSMutableArray(array: [7, 8, 9])
    ]
    
    processMatrix(&matrix)
    
    print(matrix) // outputs , <__NSArrayM 0x6000027a1560>(-2,-2,-2)]
    

    2) 使用类包装器

    class Wrapper<T: CustomDebugStringConvertible>: CustomDebugStringConvertible {
        var value: T
    
        init(_ value: T) {
            self.value = value
        }
    
        var debugDescription: String {
            return value.debugDescription
        }
    }
    
    func processRow(_ row : Wrapper<[Int]>, _ value : Int) {
        for col in 0..<row.value.count {
            row.value[col] = value;
        }
    }
    
    func processMatrix(_ matrix : inout [Wrapper<[Int]>]) {
        for rowIdx in 0..<matrix.count {
            let r = matrix[rowIdx]
            processRow(r, -2);
        }
    }
    
    var matrix = [
        Wrapper([1, 2, 3]),
        Wrapper([4, 5, 6]),
        Wrapper([7, 8, 9])
    ]
    
    processMatrix(&matrix)
    
    print(matrix) // outputs [[-2, -2, -2], [-2, -2, -2], [-2, -2, -2]]
    

    不过看起来不太好看。

    【讨论】:

    • 感谢您的回答;我仍然认为这是一种解决方法,并且谈到原始问题 - 看起来(现在)没有办法在局部变量中获取对多维数组内部数组的引用,它总是会创建一个副本。
    • @Lanorkin 是的,没有办法使用快速默认数组。如果您需要它,只需考虑使用好的旧 NSMutableArray。 Swift 数组和 dicts 实现似乎是一个糟糕的决定。
    【解决方案2】:

    我不相信有一种方法可以将数组复制到局部变量中而不进行复制。这正是值类型在 Swift 中的工作方式。 Says Apple:

    值类型最基本的区别特征是复制——赋值、初始化和参数传递的效果——创建一个独立的实例,它有自己唯一的数据副本[。]

    And here:

    值类型是一种类型,其值在分配给变量或常量时被复制,或者在传递给函数时...

    [...]

    所有结构和枚举在 Swift 中都是值类型。这意味着您创建的任何结构和枚举实例(以及它们作为属性具有的任何值类型)在代码中传递时始终会被复制。

    这只是值类型定义的一部分——每个数组都是一个值类型。

    获得所需结果的最简单方法是将r 的值重新分配给matrix 中要更改的行:

    // (2) Creates local copy
    var r = matrix[rowIdx]   
    processRow(&r, -2)
    matrix[rowIdx] = r
    

    与您的建议最接近的是使用指针:UnsafeMutablePointerUnsafePointer 等。但这确实与 Swift 的设计使用方式背道而驰。但是,如果您愿意,它看起来像这样:

    func processRow(ptr: UnsafeMutablePointer<Int>, _ value : Int, count: Int) {
        for col in 0..<count {
            ptr.advancedBy(col).memory = value
        }
    }
    
    func processMatrix(inout matrix : [[Int]]) {
        for rowIdx in 0..<matrix.count {
            
            let r = UnsafeMutablePointer<Int>(matrix[rowIdx])
            processRow(r, -2, count: matrix[rowIdx].count)
        }
    }
    
    var matrix = [
        [1, 2, 3],
        [4, 5, 6],
        [7, 8, 9]
    ]
    
    processMatrix(&matrix)
    print(matrix)   // [[2, 2, 2], [2, 2, 2], [2, 2, 2]]
    

    【讨论】:

    • 感谢您的回答 1) 数组是“特殊”值类型,因此您引用的提取对于数组来说基本上是错误的。例如,这里演示的“写时复制”功能swiftlang.ng.bluemix.net/#/repl/… 显示数组不是always copied when they are passed around 2) 复制matrix[rowIdx] = r 是我要避免的 - 我希望始终就地使用数组3) UnsafeMutablePointer 很接近,但由于它是 unsafepointer 它引发了旧的好 C 指针地狱,但我会和他们一起玩更多
    • 公平积分。 1) 数组不是 特殊的。写时复制行为完全是“在幕后”。除了作为值类型(除了指针)之外,没有办法与它们交互。 2) 为什么不matrix[rowIdx] = [2, 2, 2]?也许你让它变得比它需要的更复杂。 3) 是的,UnsafeMutablePointer 是不安全的并且是一个指针。我认为苹果通过给他们一个可怕的名字来阻止他们的使用。但它们是做你想做的唯一工具(AFAIK)。大局观,让值类型表现为引用类型会破坏拥有值类型的全部意义。
    • 我认为指针最接近本地数组引用,但我看到的唯一 usable 解决方案是为我需要的每个局部变量使用单独的 func(inout ...) 方法为数组。我感到自己不确定、失望和沮丧,但可以接受。
    • 值类型有限制,但也有好处:)
    • 我确实了解值类型。我只是不接受数组作为值类型。我看不出有理由将数组作为一个整体而不是对它的引用传递; Apple 似乎也是,因为他们实际上在内部这样做,但似乎他们必须玩这个游戏来满足他们的 let / var 容器..
    猜你喜欢
    • 1970-01-01
    • 2016-06-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-10
    • 2017-10-08
    • 1970-01-01
    相关资源
    最近更新 更多