string - 为什么不能将字符串键存储在关联数组中?

标签 string d associative-array

我是D编程语言的新手,刚刚开始阅读D编程语言一书。

尝试一个关联数组示例代码时遇到错误

#!/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显示此错误消息:


./vocab.d(11):错误:关联数组只能使用不可变键分配值,而不是char []


我正在使用DMD编译2.051

我猜想自从TDPL书以来,关联数组的规则已经改变。

如何将关联数组与字符串键一起使用?

谢谢。

更新:

我在本书的后面部分找到了解决方案。

在放入数组之前,请使用string.idup使其重复不可变。

所以

dict[word.idup] = newId;


会做的工作。

但这有效吗?

最佳答案

关联数组要求其键是不可变的。当考虑以下事实时,这是有道理的:如果它不是不可变的,那么它可能会发生变化,这意味着它的哈希值会发生变化,这意味着当您再次获取价值时,计算机将找不到它。而且,如果要替换它,最终将在关联数组中添加另一个值(因此,您将拥有一个具有正确哈希值的值和一个具有不正确哈希值的值)。但是,如果密钥是不可变的,则无法更改,因此不会出现此类问题。

在dmd 2.051之前,该示例有效(它是bug)。但是,现在它已修复,因此TDPL中的示例不再正确。但是,改变关联数组的规则的情况并不是那么多,因为其中存在未被发现的错误。该示例在不需要的时候进行了编译,而Andrei则错过了。它在official errata for TDPL中列出,并应在以后的印刷版本中修复。

更正后的代码应使用dictionary[word.idup]dictionary[to!string(word)]word.idup创建一个不变的word副本。另一方面,to!string(word)以最合适的方式将word转换为string。由于在这种情况下wordchar[],因此将使用idup。但是,如果word已经是string,则它将仅返回传入的值,而不必复制它。因此,通常情况下,to!string(word)是更好的选择(尤其是在模板函数中),但是在这种情况下,两者都可以正常工作(to!()std.conv中)。

从技术上讲可以将char[]强制转换为string,但这通常是一个坏主意。如果您知道char[]永远不会改变,那么您可以摆脱它,但是在一般情况下,您冒着问题的风险,因为编译器将假定结果string永远不会改变,并且它可能会生成不正确的代码。甚至可能出现段错误。因此,除非配置文件表明您确实需要避免复制的额外效率,否则不要这样做,否则,您就无法通过做诸如仅使用string之类的操作来避免复制(因此不会进行任何转换) ),并且您知道string永远不会更改。

总的来说,我不会太担心复制字符串的效率。通常,应该使用string而不是char[],这样就可以复制它们(即复制它们的引用(例如str1 = str2;),而不是像dupidup那样复制它们的全部内容)不用担心它效率特别低。该示例的问题是stdin.byLine()返回的是char[]而不是string(大概是避免不必要时复制数据)。因此,splitter()返回一个char[],因此wordchar[]而不是string。现在,您可以执行splitter(strip(line.idup))splitter(strip(line).idup)而不是idup键。这样,splitter()将返回string而不是char[],但这实际上与idup ing word一样有效。无论如何,由于文本的原始来源,它是一个char[]而不是string,如果您打算将其用作关联数组中的键,则会迫使您沿行的某个地方idup放置它。但是,在一般情况下,最好只使用string而不是char[]。这样,您就不需要idup了。

编辑:
实际上,即使您发现从char[]转换为string看起来既安全又必要的情况,也可以考虑使用std.exception.assumeUnique()documentation)。本质上,这是在需要且知道可以的时候将可变数组转换为不可变数组的首选方法。通常,在以下情况下才可以完成此操作:您构建了一个不能固定不变的数组,因为您必须将其分段,但没有其他引用,并且不想创建它的深层副本。但是,在您所要询问的示例之类的情况下,它将毫无用处,因为您确实确实需要复制该数组。

关于string - 为什么不能将字符串键存储在关联数组中?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4611477/

相关文章:

java - 显示注册某个字符串的所有可能组合的递归方法

c - 分割长字符串

java - 在JDK 1.6中,String的equals操作可以用==代替吗?

D中的多线程与for循环

arrays - 在不精确的浮点运算中,乘法是否总是可交换的?

php - 关联数组 - 重复键值

C++ - 替代基类函数指针调用

php - 如何使用 "tail"将字符串转换为 float ?

d - D 中是否有 EXIT_SUCCESS 和 EXIT_FAILURE 的类似物

perl - 来自数组的散列分配中的后面的键是否总是覆盖前面的键?