2011-01-06 3 views
16

Ich bin neu in der Programmiersprache D, habe gerade mit dem Lesen der Programmiersprache D begonnen.Warum kann ich keine String-Schlüssel in einem assoziativen Array speichern?

ich in die Irre führen, wenn ein assoziatives Array Beispielcode

#!/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 zeigt diese Fehlermeldung versuchen:

./vocab.d(11): Error: associative arrays can only be assigned values with immutable keys, not char[]

Ich bin mit DMD 2,051

kompilieren Ich vermute, die Regeln für assoziative Arrays haben sich seit dem TDPL-Buch geändert.

Wie soll ich assoziative Arrays mit Stringschlüsseln verwenden?

Danke.

Update:

fand ich die Lösung in späteren Teilen des Buches.

Verwenden Sie string.idup, um vor dem Einfügen in das Array einen doppelten unveränderlichen Wert zu erstellen.

so

dict[word.idup] = newId; 

würde die Arbeit machen.

Aber ist das effizient?

+4

FYI für jeden, der Monate oder Jahre später zu dieser Frage kommt - es gibt andere Dinge, die mit dem gedruckten Beispiel nicht stimmen. Verwenden Sie für den Typ des assoziativen Arrays ulong not uint, und Sie müssen std.array importieren, um den Splitter zu erhalten. Siehe http://www.digitalmars.com/d/archives/digitalmars/D/learn/problems_with_DPL_example._30009.html – DarenW

Antwort

25

Assoziative Arrays erfordern, dass ihre Schlüssel unveränderlich sind. Es macht Sinn, wenn Sie daran denken, dass es sich ändern kann, wenn es nicht unveränderlich ist, was bedeutet, dass sich sein Hashwert ändert, was bedeutet, dass der Computer den Wert nicht mehr finden wird, wenn Sie ihn wieder abrufen. Und wenn Sie es ersetzen, erhalten Sie einen anderen Wert, der dem assoziativen Array hinzugefügt wird (Sie haben also einen mit dem richtigen Hash und einen mit einem falschen Hash). Wenn der Schlüssel jedoch unveränderlich ist, kann er sich nicht ändern, und daher gibt es kein solches Problem.

Vor dmd 2.051 arbeitete das Beispiel (das war ein bug). Es wurde nun behoben, das Beispiel in TDPL ist nicht mehr korrekt. Es ist jedoch nicht so sehr der Fall, dass sich die Regeln für assoziative Arrays geändert haben, da es einen Fehler in ihnen gab, der nicht abgefangen wurde. Das Beispiel kompiliert, wenn es nicht hätte sein sollen, und Andrei hat es verpasst. Es ist in der official errata for TDPL aufgeführt und sollte in zukünftigen Drucken behoben werden. Der korrigierte Code sollte dictionary[word.idup] oder dictionary[to!string(word)] verwenden. word.idup erstellt ein Duplikat von word, das unveränderlich ist. to!string(word), andererseits konvertiert word zu einem string in der am besten geeigneten Weise.Da word in diesem Fall ein char[] ist, wäre das idup zu verwenden. Wenn jedoch word bereits ein string wäre, würde es einfach den übergebenen Wert zurückgeben und nicht unnötigerweise kopieren. Im allgemeinen Fall ist also to!string(word) die bessere Wahl (besonders bei Vorlagenfunktionen), aber in diesem Fall funktioniert es entweder gut (to!() ist in std.conv).

Es ist technisch möglich, eine char[] zu einer string zu werfen, aber es ist im Allgemeinen eine schlechte Idee. Wenn Sie wissen, dass die char[] wird nie ändern, dann können Sie damit durchkommen, aber im allgemeinen Fall riskieren Sie Probleme, da der Compiler wird dann davon ausgehen, dass die resultierende string kann nie ändern, und es könnte generieren Code, der falsch ist. Es kann sogar segfault sein. Also, tun Sie es nicht, wenn Profiling zeigt, dass Sie wirklich die zusätzliche Effizienz der Vermeidung der Kopie benötigen, können Sie die Kopie nicht anders vermeiden, indem Sie etwas tun, nur mit einem string an erster Stelle (so dass keine Konvertierung erforderlich wäre) und Sie wissen, dass die string wird nie geändert werden.

Im Allgemeinen würde ich nicht zu viel von der Effizienz des Kopierens von Zeichenfolgen sorgen. Im Allgemeinen sollten Sie string anstelle von char[] verwenden, damit Sie sie kopieren können (das heißt kopieren Sie ihre Referenz herum (z. B. str1 = str2;) anstatt ihre gesamten Inhalte wie dup und idup zu kopieren), ohne sich darüber Gedanken zu machen, dass sie besonders ineffizient ist. Das Problem mit dem Beispiel ist, dass stdin.byLine() eine char[] anstelle einer string zurückgibt (vermutlich, um das Kopieren der Daten zu vermeiden, wenn es nicht notwendig ist). So gibt splitter() eine char[] zurück, und so ist word eine char[] anstelle einer string. Jetzt könnten Sie splitter(strip(line.idup)) oder splitter(strip(line).idup) anstelle von idup in den Schlüssel tun. Auf diese Weise würde splitter() eher eine string als char[] zurückgeben, aber das ist wahrscheinlich im Wesentlichen genauso effizient wie idup ing word. Unabhängig davon, wo der Text ursprünglich herkommt, ist es eine char[] anstelle einer string, die Sie irgendwo entlang der Linie zu zwingt, wenn Sie beabsichtigen, es als Schlüssel in einem assoziativen Array zu verwenden. Im allgemeinen Fall ist es jedoch besser, einfach string und nicht char[] zu verwenden. Dann brauchst du nichts zu idup.

EDIT:
Eigentlich, auch wenn Sie eine Situation, wo char[]-string Gießen sicher und notwendig erscheint, sollten Sie mit std.exception.assumeUnique() (documentation). Es ist im Wesentlichen die bevorzugte Möglichkeit, ein veränderbares Array in ein unveränderliches zu konvertieren, wenn Sie müssen und wissen, dass Sie können. Dies würde normalerweise in Fällen geschehen, in denen Sie ein Array erstellt haben, das Sie nicht unveränderlich machen konnten, weil Sie es in Stücken ausführen mussten, aber keine anderen Referenzen haben, und Sie keine tiefe Kopie davon erstellen möchten. In Situationen wie dem von Ihnen gewünschten Beispiel wäre es jedoch nicht sinnvoll, da Sie das Array wirklich kopieren müssen.

+1

Danke für die ausführliche und aufschlussreiche Antwort! Jetzt habe ich den Punkt von String und Char []. Vielen Dank! –

+1

+1, gute Antwort. Zurück zu D nach einer langen Pause, nur um festzustellen, dass der 2.047-Compiler etwas weitergerückt ist! – shambulator

1

Nein, es ist nicht effizient, da es offensichtlich die Zeichenfolge dupliziert. Wenn Sie können Garantie, dass die Zeichenfolge, die Sie erstellen, wird nie im Speicher geändert werden, fühlen Sie sich frei, eine Besetzung cast(immutable)str darauf explizit zu verwenden, anstatt es zu duplizieren.

(Obwohl ich bemerkt habe, dass der Garbage Collector gut funktioniert, schlage ich vor, dass Sie es nicht versuchen, es sei denn, Sie sehen einen Engpass, da Sie die Zeichenfolge später ändern könnten Code, der Ihnen hilft, den Engpass später zu finden, falls er existiert.)

+2

In diesem Beispiel ist das zusätzliche idup kein Leistungsproblem, da es das Ergebnis der Vermeidung zusätzlicher Zuweisungen in der Dateieingabecode.Infolgedessen zahlen Sie nur die gleichen Kosten, die Sie anderswohin würden, wenn Sie versuchen würden, das idup dort zu vermeiden. – BCS

Verwandte Themen