2011-01-06 11 views
16

Soy nuevo en el lenguaje de programación D, acabo de empezar a leer el libro The D Programming Language.¿Por qué no puedo almacenar claves de cadena en una matriz asociativa?

me encuentro con error al intentar código de ejemplo, una 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: associative arrays can only be assigned values with immutable keys, not char[]

estoy usando DMD compilar 2.051

Yo estaba adivinando el las reglas para matrices asociativas han cambiado desde el libro TDPL.

¿Cómo debo usar matrices asociativas con claves de cadena?

Gracias.

Actualización:

he encontrado la solución en partes posteriores del libro.

use string.idup para hacer un valor duplicado inmutable antes de poner en la matriz.

por lo

dict[word.idup] = newId; 

haría el trabajo.

¿Pero es eso eficiente?

+4

FYI para cualquiera que llegue a esta pregunta meses o años después - hay otras cosas incorrectas con el ejemplo tal como está impreso. Use ulong not uint para el tipo de matriz asociativa, y necesita importar std.array para obtener el divisor. Consulte http://www.digitalmars.com/d/archives/digitalmars/D/learn/problems_with_DPL_example._30009.html – DarenW

Respuesta

25

Las matrices asociativas requieren que sus claves sean inmutables. Tiene sentido cuando piensas en el hecho de que si no es inmutable, puede cambiar, lo que significa que su hash cambia, lo que significa que cuando vayas 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 llave es inmutable, no puede cambiar, por lo que no hay tal problema.

Antes de dmd 2.051, el ejemplo funcionó (que era bug). Ahora se ha corregido, por lo que el ejemplo en TDPL ya no es correcto. Sin embargo, no es tanto el caso de que las reglas para las matrices asociativas hayan cambiado ya que hubo un error en ellas que no fue detectado. El ejemplo compilado cuando no debería y Andrei lo perdió. Se enumera en el official errata for TDPL y se debe corregir en futuras impresiones.

El código corregido debe usar dictionary[word.idup] o dictionary[to!string(word)]. word.idup crea un duplicado de word que es inmutable. to!string(word), por otro lado, convierte word a string de la manera más adecuada.Como word es char[] en este caso, eso sería usar idup. Sin embargo, si word ya fuera un string, simplemente devolverá el valor que se transfirió y no lo copiará innecesariamente. Por lo tanto, en el caso general, to!string(word) es la mejor opción (especialmente en funciones con plantilla), pero en este caso, cualquiera de los dos funciona bien (to!() está en std.conv).

Es técnicamente posible emitir un char[] a un string, pero en general es una mala idea. Si sabe que char[] nunca cambiará, entonces puede salirse con la suya, pero en el caso general, está arriesgando problemas, ya que el compilador asumirá que el 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 demuestre que realmente necesita la eficiencia extra de evitar la copia; de lo contrario, no puede evitar la copia haciendo algo así como simplemente usar un string (por lo que no sería necesaria ninguna conversión) , y usted sabe que el string nunca cambiará.

En general, no me preocuparía demasiado la eficacia de la copia de cadenas. Generalmente, debe utilizar string en lugar de char[], de modo que puede copiarlos (es decir, copiar sus referencias (por ejemplo, str1 = str2;) en lugar de copiar todo su contenido como dup y idup) sin preocuparse de que sea particularmente ineficiente. El problema con el ejemplo es que stdin.byLine() devuelve char[] en lugar de string (presumiblemente para evitar copiar los datos si no es necesario). Por lo tanto, splitter() devuelve char[], por lo que word es char[] en lugar de string. Ahora, puede hacer splitter(strip(line.idup)) o splitter(strip(line).idup) en lugar de idup ing la clave. De esta forma, splitter() devolvería string en lugar de char[], pero probablemente sea tan eficiente como idup ing word. De todos modos, debido a la procedencia original del texto, es char[] en lugar de string, lo que obliga a idup en algún lugar a lo largo de la línea si tiene la intención de utilizarlo como clave en una matriz asociativa. En el caso general, sin embargo, es mejor usar string y no char[]. Entonces no necesita idup nada.

EDIT:
En realidad, incluso si se encuentra una situación en la fundición char[]-string parece a la vez seguro y necesario, considere el uso de std.exception.assumeUnique() (documentation). Es esencialmente la forma preferida de convertir una matriz mutable a una matriz inmutable cuando lo necesita y sabe que puede hacerlo. Normalmente se realizaría en casos en los que hayas construido una matriz que no podrías hacer inmutable porque tuviste que hacerlo en trozos pero que no tenga otras referencias, y no quieras crear una copia profunda de la misma. Sin embargo, no sería útil en situaciones como el ejemplo sobre el que está preguntando, ya que realmente necesita copiar la matriz.

+1

¡Gracias por la respuesta detallada y perspicaz! Ahora tengo el punto de string y char []. ¡Muchas gracias! –

+1

+1, gran respuesta. ¡Volviendo a D después de un largo paréntesis, solo para descubrir que el compilador 2.047 se ha movido un poco! – shambulator

1

No, no es eficiente, ya que obviamente duplica la cadena. Si puede garantizar que la cadena que cree será nunca modificarse en la memoria, siéntase libre de utilizar explícitamente un molde cast(immutable)str en él, en lugar de duplicarlo.

(Aunque, me he dado cuenta de que el recolector de basura funciona bien, por lo que sugieren que en realidad no intenta que a menos que vea un cuello de botella, ya que es posible que decida cambiar la cadena posterior. Sólo tiene que colocar un comentario en su código para ayudarlo a encontrar el cuello de botella más adelante, si existe.)

+2

En este ejemplo, el id adicional no es un problema de rendimiento porque es el resultado de evitar asignaciones adicionales en el código de entrada de archivo.Como resultado, solo está pagando el mismo costo que pagaría en otro lugar si tratara de evitar la idoneidad allí. – BCS

Cuestiones relacionadas