String 为什么可以';我是否将字符串键存储在关联数组中?

String 为什么可以';我是否将字符串键存储在关联数组中?,string,d,associative-array,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;

我是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生成重复的不可变值

所以

我会做的


但是这是有效的吗?

不,这不是有效的,因为它显然重复了字符串。如果您可以保证您创建的字符串永远不会在内存中被修改,那么可以在其上显式使用cast
cast(immutable)str
,而不是复制它


(虽然我注意到垃圾收集器工作得很好,但我建议您不要尝试,除非您看到瓶颈,因为您可能会决定稍后更改字符串。只要在代码中添加注释,以帮助您稍后找到瓶颈(如果存在)。

关联数组要求它们的键是不可变的。如果它不是不可变的,那么它可能会改变,这意味着它的哈希值会改变,这意味着当你再次获取值时,计算机将找不到它。如果您要替换它,您将得到另一个添加到关联数组的值(因此,您将得到一个具有正确哈希的值,另一个具有错误哈希的值)。但是,如果密钥是不可变的,它就不能更改,因此不存在这样的问题

在DMD2.051之前,该示例有效(这是一个示例)。但现在它已被修复,因此TDPL中的示例不再正确。然而,与其说关联数组的规则发生了变化,不如说它们中有一个未被捕获的bug。该示例在不应该编译的时候编译,Andrei错过了它。它列在中,应该在以后的打印中修复

更正后的代码应使用
字典[word.idup]
字典[to!string(word)]
word.idup
创建不可变的
word
副本<代码>到!另一方面,字符串(word),以最合适的方式将
word
转换为
string
。因为在本例中,
word
是一个
char[]
,所以应该使用
idup
。但是,如果
word
已经是
字符串
,那么它只会返回传入的值,而不会不必要地复制它。所以,在一般情况下,
to!字符串(word)
是更好的选择(特别是在模板函数中),但在这种情况下,两者都可以正常工作(
to!()
位于
std.conv

从技术上讲,可以将
char[]
强制转换为
string
,但这通常不是一个好主意。如果您知道
char[]
永远不会更改,那么您可以侥幸逃脱,但在一般情况下,您会面临问题的风险,因为编译器会假定生成的
string
永远不会更改,并且它可能生成错误的代码。这甚至可能是一个错误。因此,除非分析表明您确实需要避免复制的额外效率,否则不要这样做。否则,您无法通过首先使用
字符串
来避免复制(因此不需要转换),并且您知道
字符串
永远不会更改

一般来说,我不会太担心复制字符串的效率。通常,您应该使用
string
而不是
char[]
,这样您就可以复制它们(也就是说,复制它们的引用(例如
str1=str2;
),而不是复制它们的全部内容,比如
dup
idup
do),而不必担心效率特别低。该示例的问题是
stdin.byLine()
返回的是
char[]
,而不是
字符串(可能是为了避免在不必要时复制数据)。因此,
splitter()
返回一个
char[]
,因此
word
char[]
而不是
字符串。现在,您可以使用
splitter(strip(line.idup))
splitter(strip(line.idup)
代替
idup
键。这样,
splitter()
将返回一个
string
而不是
char[]
,但这基本上与
idup
ing
word
一样有效。不管怎样,由于文本最初来自何处,它是一个
char[]
而不是
string
,如果要将其用作关联数组中的键,这会迫使您将其设置为
idup
。但是,在一般情况下,最好只使用
string
而不是
char[]
。那么你就不需要
idup
任何东西了

编辑:
实际上,即使您发现一个情况,从 CAR[]/COD> > <代码> String < /C> >似乎都是安全和必要的,请考虑使用<代码> STD.Extry.AssiMeUnQuess()/<代码>()。当您需要并且知道可以将可变数组转换为不可变数组时,它本质上是首选的方法。通常情况下,如果您构建了一个数组,而该数组不能使其不可变,因为您必须分块执行,但它没有其他引用,并且您不想创建它的深度副本,那么就需要执行此操作。但是,在像您询问的示例这样的情况下,它不会有用,因为您确实需要复制数组dict[word.idup] = newId;