不可变`var`数组

时间:2015-05-31 06:05:35

标签: swift immutability

我想在Swift中创建一个不可变的数组,但可以用函数式编程的方式完全替换。

我知道我可以用这种方式创建它们:

myArr.append(42) // fails. immutable array

我想要不变性的属性,但仍然能够改变我的变量所指向的不可变数组。例如,我希望这会失败:

// Desired behavior:
// Multiply each element in the immutable array by 2
// Return a new immutable array, assigned to the old var
immutable = immutable.map { $0 * 2 } // ERROR: can't replace immutable ref

但我希望这会成功:

svn co http://svn.kuali.org/repos/student/enrollment/ks-deployments/tags/ks-deployments-2.0.3-cm/ks-cfg-dbs/

类似但不是答案:

3 个答案:

答案 0 :(得分:2)

我不知道使用内置Array类型实现这一目标的方法。 您可以做的是定义自定义ConstantArray类型 其中包含一个数组,并且只有不可变的访问器方法 (都转发到包含的数组):

struct ConstantArray<T> {
    private let elements : [T]

    // Create with an empty array.
    init() {
        elements = []
    }

    // Create with some sequence, e.g.
    //     let constArray = ConstantArray([1, 2, 3])
    init<S : SequenceType where S.Generator.Element == T>(_ s: S) {
        elements = Array(s)
    }

    // Read-only computed property to get the elements.
    var array : [T]  {
        return elements
    }

    // Some properties you might want to implement:
    var count: Int { return elements.count }
    var isEmpty: Bool { return elements.isEmpty }
    var first: T? { return elements.first }
    var last: T? { return elements.last }

}

extension ConstantArray : ArrayLiteralConvertible {
    // Create with an array literal, e.g.
    //     let constArray : ConstantArray = [1, 2, 3]
    init(arrayLiteral array: T...) {
        elements = array
    }
}

// Implement sequence prototol, e.g. for enumeration:
//     for elem in myConstantArray { ... }
extension ConstantArray : SequenceType {
    func generate() -> IndexingGenerator<[T]> {
        return elements.generate()
    }
}

// Implement read-only subscripting, e.g.
//     let elem = myConstantArray[1]
extension ConstantArray: CollectionType {
    var startIndex : Int { return elements.startIndex }
    var endIndex : Int { return elements.endIndex }

    subscript(index: Int) -> T {
        return elements[index]
    }
}

extension ConstantArray : Printable {
    var description: String { return elements.description }
}

当然这是一种不同的类型,但通过实施 SequenceTypeCollectionType可以非常相似地使用它。 例如:

var myArr : ConstantArray = [1, 2, 3]

myArr.append(42)   // error: 'ConstantArray<Int>' does not have a member named 'append'
myArr[2] = 3       // error: cannot assign to the result of this expression
myArr.array[2] = 3 // error: cannot assign to the result of this expression

myArr = [4, 5, 6] // succeeds
println(myArr) // [4, 5, 6]
for elem in myArr {
    println(elem)
}

对于映射,您可以使用全局map()函数 (或map()的{​​{1}}方法)并将结果转换回Array

ConstantArray

或者为myArr = ConstantArray(map(myArr) { $0 * 2 }) myArr = ConstantArray(myArr.array.map { $0 * 2 }) 实施map()方法:

ConstantArray

并将其用作

extension ConstantArray {
    func map<U>(transform: (T) -> U) -> ConstantArray<U> {
        return ConstantArray<U>(elements.map(transform))
    }
}

以同样的方式,其他方法,如myArr = myArr.map { 2 * $0 } filter() 可以为sorted()实施。

答案 1 :(得分:1)

在这种情况下,let无法使用,因为它在整个运行时都不会更改。

我个人更喜欢使用var并声明NSArray。您可以重新分配变量以指向另一个不可变NSArray,但不能修改NSArray的内容。

如果您设法将其转换为NSMutableArray,则会导致运行时崩溃。

答案 2 :(得分:1)

你想要什么对Array没有意义,因为它是一个带有值语义的值类型。变量的值是数组 - 因此更改变量的能力和更改数组的能力是一回事。

语义上没有区别:

myArr.append(42)

等等(编造):

myArr = myArr.newArrayWithAppending(42)

在某种程度上,您可以想象对值类型变量的任何“修改”可以实现为为变量分配一个全新的值。 (这可能不是那样做的,因为它效率低下,但从语义的角度来看,没有理由不能以这种方式实现它。)

“可变性”的概念仅对引用类型有意义,其中变量是指向实际对象的引用,实际对象可以在多个引用之间共享(因此通过一个引用的“突变”可以是通过另一个看到。)

使用引用类型,您可以执行所需的操作,例如使用不可变的ImmutableArray类,然后如果您拥有类型为var的{​​{1}}变量,则可以构造一个全新的不可变数组并将变量更改为指向新数组,但是从共享引用中看不到任何特定数组对象的“更改”。