Siempre pensé: "¿Por qué necesitas escribir tu propia copia sobre escritura? Es genial cuando hay estructuras y son tan rápidas que hacen todo por ti".
Pero todo esto no es necesario hasta que empiece a escribir sus propios tipos y esté enganchado a LinkedLists.
¿Qué es esta lista enlazada y cuáles son sus beneficios?
Una lista vinculada tiene varias ventajas teóricas sobre las opciones de almacenamiento contiguas como Array:
Las listas enlazadas tienen O (1) complejidad de tiempo para insertar los primeros elementos. Las matrices tienen una complejidad de tiempo de O (n).
El cumplimiento de los protocolos de recopilación de Swift, como Sequence y Collection, ofrece muchos métodos útiles para una cantidad bastante pequeña de requisitos.
Una lista vinculada es una secuencia de elementos de datos en la que cada elemento se denomina nodo .
Hay dos tipos principales de listas vinculadas:
Las listas enlazadas individualmente son listas enlazadas en las que cada nodo solo enlaza con el siguiente.
Las listas doblemente enlazadas son listas enlazadas en las que cada nodo tiene un enlace al nodo anterior y al siguiente.
, . , head tail.
:
public class Node<Value> {
public var value: Value
public var next: Node?
public init(value: Value, next: Node? = nil) {
self.value = value
self.next = next
}
}
public struct LinkedList<Value> {
public var head: Node<Value>?
public var tail: Node<Value>?
public init() {}
public var isEmpty: Bool { head == nil }
}
, ! , . . .
Node'a , reference type. , . .
? LinkedList . COW .
value type COW . , ( ) .
private mutating func copyNodes() {
guard var oldNode = head else {
return
}
head = Node(value: oldNode.value)
var newNode = head
while let nextOldNode = oldNode.next {
newNode!.next = Node(value: nextOldNode.value)
newNode = newNode!.next
oldNode = nextOldNode
}
tail = newNode
}
. , O (n) …
COW
O (n) .
, . - , .
Swift isKnownUniquelyReferenced. , , .
Y si agrega código al comienzo de la función copyNodes (), entonces no necesita copiar más:
private mutating func copyNodes(returningCopyOf node:
Node<Value>?) -> Node<Value>? {
guard !isKnownUniquelyReferenced(&head) else {
return nil
}
guard var oldNode = head else {
return nil
}
head = Node(value: oldNode.value)
var newNode = head
var nodeCopy: Node<Value>?
while let nextOldNode = oldNode.next {
if oldNode === node {
nodeCopy = newNode
}
newNode!.next = Node(value: nextOldNode.value)
newNode = newNode!.next
oldNode = nextOldNode
}
return nodeCopy
}
Este método tiene mucho en común con la implementación anterior. La principal diferencia es que devolverá el nodo recién copiado según el parámetro pasado.
Por lo tanto, Copy-on-write no es algo distante y oculto. Y bastante manejable y comprensible.