¿Es mala práctica de matrices dinámicas alter que tienen referencias a ellos?

StackOverflow https://stackoverflow.com/questions/3416657

  •  26-09-2019
  •  | 
  •  

Pregunta

Me parecía un poco en matrices dinámicas en D2, y me parecieron muy difícil de entender. También parece que estoy interpretando erróneamente la especificación .. Trabajando en una referencia o una rebanada de una matriz dinámica parece muy propenso a errores al cambiar las matrices ... o estoy simplemente no comprender los fundamentos?

Haciendo referencia a la misma matriz sólo comparte los elementos reales:

auto a = [1];
auto b = a;
assert(&a != &b); // different instance; Doesn't share length
assert(a.ptr == b.ptr); // same items
assert(a == [1]);
assert(a == b);

A medida que se hacen referencia a la misma matriz, el cambio de uno cambia el otro:

auto a = [1,2];
auto b = a;
a[1] = 20;
assert(a == [1,20]);
assert(a == b);

A partir de la especificación en la matriz

  

Para maximizar la eficiencia, el tiempo de ejecución siempre trata de cambiar el tamaño de la   array en lugar para evitar la copia adicional. Se hará siempre una copia   Si el nuevo tamaño es más grande y la matriz no se asignó a través de la   nuevo operador o una anterior   cambiar el tamaño de la operación.

Así cambiando la longitud no se rompe neccesarily la referencia:

auto a = [1];
auto b = a;
b.length = 2;
assert(b == [1,0]);
assert(a == [1]); // a unchanged even if it refers to the same instance
assert(a.ptr == b.ptr);  // but still the same instance

// So updates to one works on the other
a[0]  = 10;
assert(a == [10]);
assert(b == [10,0]);

A partir de la especificación en la matriz

  

La concatenación siempre crea una copia de sus operandos, incluso si uno de los operandos es un 0 longitud de la matriz

auto a = [1];
auto b = a;
b ~= 2; // Should make a copy, right..?
assert(a == [1]);
assert(b == [1,2]);
assert(a != b);
assert(a4.ptr == b.ptr); // But it's still the same instance
a[0] = 10;
assert(b == [10,2]); // So changes to a changes b

Sin embargo, cuando las matrices se paso el uno del otro, los valores se copian a una nueva ubicación y la referencia roto:

auto a = [1];
auto b = a;
b ~= 2;
assert(a == [1]);
assert(b == [1,2]);

a.length = 2; // Copies values to new memory location to not overwrite b's changes
assert(a.ptr != b.ptr);

Cambio de la longitud de ambas matrices antes de hacer un cambio da el mismo resultado que el anterior (I esperaría que esta dada la anterior):

auto a = [1];
auto b = a;
a.length = 2;
b.length = 2;
a[1] = 2;
assert(a == [1,2]);
assert(b == [1,0]);
assert(a.ptr != b.ptr);

Y lo mismo cuando se cambia la longitud o cancatenating (Yo esperaría que esta dada la anterior):

auto a = [1];
auto b = a;
b.length = 2;
a ~= 2;
assert(a == [1,2]);
assert(b == [1,0]);
assert(a.ptr != b.ptr);

Pero entonces rebanadas también entran en escena, y de repente se complica aún más! Las rebanadas podrían quedar huérfanos ...

auto a = [1,2,3];
auto b = a;
auto slice = a[1..$]; // [2,3];
slice[0] = 20;
assert(a == [1,20,3]);
assert(a == b);

a.length = 4;
assert(a == [1,20,3,0]);
slice[0] = 200;
assert(b == [1,200,3]); // the reference to b is still valid.
assert(a == [1, 20, 3, 0]); // but the reference to a is now invalid..

b ~= 4;
// Now both references is invalid and the slice is orphan...
// What does the slice modify?
assert(a.ptr != b.ptr);
slice[0] = 2000;
assert(slice == [2000,3]);
assert(a == [1,20,3,0]); 
assert(b == [1,200,3,4]);

Así que ... ¿Es una mala práctica de tener múltiples referencias a la misma matriz dinámica? Y que pasa alrededor de las rebanadas etc.? O solo estoy hasta aquí, perdiendo el punto de matrices dinámicas en toda D?

¿Fue útil?

Solución

En general, parece que entender las cosas bastante bien, pero parece ser la mala interpretación del propósito de la propiedad ptr. Lo hace no indicar si dos matrices se refieren a la misma instancia. Lo que hace es que podemos encontrar en el puntero a lo que es efectivamente la parte de abajo C matriz. Una matriz en D tiene su length como parte de ella, por lo que es más como que es una estructura con una longitud y un puntero a una matriz C de lo que es como una matriz C. ptr le permite obtener en la matriz C y pasarlo al código C o C ++. Es probable que no debe utilizar para cualquier cosa en el código puro D. Si desea probar si dos variables de matriz se refieren a la misma instancia, a continuación, se utiliza el operador is (o !is para comprobar que son diferentes instancias):

assert(a is b);   //checks that they're the same instance
assert(a !is b);  //checks that they're *not* the same instance

Todo lo que ptr en igualdad de condiciones para los dos conjuntos indicarían es que su primer elemento es en el mismo lugar en la memoria. En particular, sus lengths podrían diferir. Sin embargo, sí significa que todos los elementos superpuestos se consiguen alterado en ambas matrices si se les altera en uno de ellos.

Al cambiar el length de una matriz, D trata de evitar la reasignación, pero podría decidir la reasignación, por lo que no necesariamente puede depender de si sería reasignar o no. Por ejemplo, se va a reasignar si no el hacerlo pisar fuerte en la memoria de otra matriz (incluyendo aquellas que tienen el mismo valor para ptr). También podría reasignar si no hay suficiente memoria para cambiar el tamaño sí mismo en su lugar. Básicamente, se reasignará si no el hacerlo pisar fuerte en la memoria de otra matriz, y puede o no puede reasignar lo contrario. Por lo tanto, no es generalmente una buena idea que depender de si un array reasignar o no cuando ajuste su length.

Me habría esperado añadiendo que siempre copiar por los documentos, pero por sus pruebas, no parecen actuar como length no (no sé si eso significa que los documentos deben ser actualizados o si se trata de un error - mi conjetura sería que los documentos deben ser actualizados). En cualquier caso, ciertamente no se puede confiar en otras referencias a esa matriz para todavía se refieren a la misma matriz después de anexar.

En cuanto a rodajas, que funciona como se espera y están altamente utilizado en D - sobre todo en la biblioteca estándar, Fobos. Una rebanada es un rango para una matriz y rangos son un concepto básico en Fobos. Sin embargo, al igual que muchos otros rangos, alterando el recipiente que la gama / rebanada es para ese rango podría invalidar / rebanada. Es por eso que cuando se está utilizando funciones que podrían cambiar el tamaño de los contenedores en Fobos, es necesario utilizar las funciones precedidas estable (por ejemplo stableRemove() o stableInsert()) si no se quiere correr el riesgo de invalidar los rangos que tienes que dicho contenedor.

Además, una rebanada es una matriz como la matriz que se apunta. Así que, naturalmente, alterando su length o añadiendo a ella va a seguir todos las mismas reglas que las de la alteración de la length del o de agregar a cualquier otro arreglo, y por lo tanto podría ser reasignado y ya no sea una rebanada en otra matriz.

Más o menos, sólo tiene que ser consciente de que la alteración de la length de una matriz de ninguna manera podría dar lugar a una reasignación, por lo que necesita hacer para evitar que si quieres referencias a siguen haciendo referencia a la misma instancia de matriz. Y si es absolutamente necesario para asegurarse de que lo hacen no punto a la misma referencia, entonces usted necesidad de utilizar dup para obtener una nueva copia de la matriz. Si no te metas con el length de una matriz en absoluto, entonces las referencias de matriz (ya sean rodajas o referencias a toda la matriz) continuará refiriéndose felizmente a la misma matriz.

EDIT: Resulta que los documentos deben ser actualizados. Cualquier cosa que pueda cambiar el tamaño de la matriz va a tratar de hacerloen su lugar si es posible (lo que no podría reasignar) sino que reasignar si se tiene que con el fin de evitar que pisa fuerte en la memoria de otra matriz o si no tiene suficiente espacio para reasignar en su lugar. Por lo tanto, no debería haber ninguna distinción entre el cambio de tamaño de la matriz estableciendo su propiedad length y cambiar su tamaño añadiendo a la misma.

Adición: Cualquier persona que utilice D realmente debería leer este artículo en matrices y rodajas. Se les explica bastante bien, y debe darle una mejor idea de cómo funcionan las matrices de D.

Otros consejos

No Realmente quiero convertir esto en una respuesta en toda regla, pero aún no pueden dejar comentarios en la respuesta anterior.

creo que la concatenación y anexar dos operaciones ligeramente diferentes. Si utiliza ~ con una matriz y un elemento, es anexar; con dos matrices, es la concatenación.

Se podría probar este lugar:

a = a ~ 2;

Y ver si obtiene los mismos resultados.

Además, si usted quiere tener un comportamiento definido, sólo tiene que utilizar el .dup (o .idup para inmutables) propiedades. Esto también es muy útil si usted tiene una serie de referencias; se puede modificar la matriz principal y rebanadas .dup a trabajar en salir sin tener que preocuparse por las condiciones de carrera.

EDIT: OK, lo tengo un poco mal, pero es así de todos modos. La concatenación! = Anexar.

// Max

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top