我想在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/
类似但不是答案:
答案 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 }
}
当然这是一种不同的类型,但通过实施
SequenceType
和CollectionType
可以非常相似地使用它。
例如:
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}}变量,则可以构造一个全新的不可变数组并将变量更改为指向新数组,但是从共享引用中看不到任何特定数组对象的“更改”。