string - ¿Por qué no puedo almacenar claves de cadena en una matriz asociativa?
associative-array (2)
Soy nuevo en el lenguaje de programación D, recién comencé a leer el libro El lenguaje de programación D.
Me encuentro con un error al intentar un código de ejemplo de matriz asociativa
#!/usr/bin/rdmd
import std.stdio, std.string;
void main() {
uint[string] dict;
foreach (line; stdin.byLine()) {
foreach (word; splitter(strip(line))) {
if (word in dict) continue;
auto newId = dict.length;
dict[word] = newId;
writeln(newId, ''/t'', word);
}
}
}
DMD muestra este mensaje de error:
./vocab.d(11): Error: a las matrices asociativas solo se les pueden asignar valores con claves inmutables, no char []
Estoy usando compilación DMD 2.051
Estaba adivinando que las reglas para los arrays asociativos han cambiado desde el libro de TDPL.
¿Cómo debo usar matrices asociativas con claves de cadena?
Gracias.
Actualizar:
Encontré la solución en partes posteriores del libro.
use string.idup para hacer un valor inmutable duplicado antes de colocarlo en la matriz.
asi que
dict[word.idup] = newId;
haría el trabajo
¿Pero es eso eficiente?
Las matrices asociativas requieren que sus claves sean inmutables. Tiene sentido cuando piensas en el hecho de que si no es inmutable, entonces puede cambiar, lo que significa que su hash cambia, lo que significa que cuando vas a obtener el valor nuevamente, la computadora no lo encontrará. Y si va a reemplazarlo, terminará con otro valor agregado a la matriz asociativa (por lo tanto, tendrá uno con el hash correcto y otro con un hash incorrecto). Sin embargo, si la clave es inmutable, no puede cambiar, por lo que no existe tal problema.
Antes de dmd 2.051, el ejemplo funcionaba (que era un bug ). Sin embargo, ahora se ha corregido, por lo que el ejemplo en TDPL ya no es correcto. Sin embargo, no es tanto el caso que las reglas para matrices asociativas hayan cambiado, sino que hubo un error en ellas que no se detectó. El ejemplo se compiló cuando no debería, y Andrei se lo perdió. Se encuentra en la lista de erratas oficiales para TDPL y se debe corregir en futuras impresiones.
El código corregido debe usar el dictionary[word.idup]
o el dictionary[to!string(word)]
. word.idup
crea un duplicado de word
que es inmutable. to!string(word)
, por otro lado convierte la word
en una string
de la manera más apropiada. Como word
es un char[]
en este caso, sería usar idup
. Sin embargo, si la word
ya fuera una string
, simplemente devolverá el valor que se pasó y no la copiará innecesariamente. Por lo tanto, en el caso general, to!string(word)
es la mejor opción (particularmente en funciones de plantilla), pero en este caso, cualquiera de los dos funciona bien ( to!()
Está en std.conv
).
Es técnicamente posible lanzar un char[]
a una string
, pero generalmente es una mala idea. Si sabe que el char[]
nunca cambiará, entonces puede salirse con la suya, pero en el caso general, está arriesgando problemas, ya que el compilador asumirá que la string
resultante nunca puede cambiar, y podría generar Código que es incorrecto. Incluso puede segfault. Por lo tanto, no lo haga a menos que el perfil muestre que realmente necesita la eficiencia adicional de evitar la copia, de lo contrario no puede evitar la copia haciendo algo como usar una string
en primer lugar (por lo que no sería necesaria una conversión) , y sabes que la string
nunca será cambiada.
En general, no me preocuparía demasiado la eficiencia de copiar cadenas. En general, debería usar string
lugar de char[]
, de modo que pueda copiarlos (es decir, copiar su referencia) (por ejemplo, str1 = str2;
) en lugar de copiar todo su contenido como dup
y idup
do) sin preocuparse de que sea Particularmente ineficiente. El problema con el ejemplo es que stdin.byLine()
devuelve un char[]
lugar de una string
(presumiblemente para evitar copiar los datos si no es necesario). Entonces, splitter()
devuelve un char[]
, y entonces la word
es un char[]
lugar de una string
. Ahora, puede hacer splitter(strip(line.idup))
o splitter(strip(line).idup)
lugar de hacer idup
en la tecla. De esa manera, splitter()
devolvería una string
lugar de char[]
, pero probablemente sea tan eficiente como la word
idup
ing. En cualquier caso, debido a la procedencia original del texto, es un char[]
lugar de una string
, lo que obliga a que lo idup
en algún lugar de la línea si pretendes usarlo como una clave en una matriz asociativa. En el caso general, sin embargo, es mejor usar solo la string
y no char[]
. Entonces no necesitas hacer nada.
EDITAR:
En realidad, incluso si encuentra una situación en la que la conversión de char[]
a string
parece segura y necesaria, considere usar std.exception.assumeUnique()
( documentation ). Esencialmente, es la forma preferida de convertir una matriz mutable en una inmutable cuando lo necesite y sepa que puede hacerlo. Por lo general, se haría en los casos en los que se construyó una matriz que no se podía hacer inmutable porque tenía que hacerlo en partes pero que no tiene otras referencias, y no desea crear una copia profunda de la misma. Sin embargo, no sería útil en situaciones como el ejemplo que está preguntando, ya que realmente necesita copiar la matriz.
No, no es eficiente, ya que obviamente duplica la cadena. Si puede garantizar que la cadena que crea nunca se modificará en la memoria, no dude en utilizar explícitamente una cast(immutable)str
de cast(immutable)str
, en lugar de duplicarla.
(Aunque, me he dado cuenta de que el recolector de basura funciona bien, así que sugiero que no lo intentes a menos que veas un cuello de botella, ya que podrías decidir cambiar la cadena más tarde. Simplemente coloca un comentario en tu código para ayudarte. encuentra el cuello de botella más tarde, si existe.)