String 为什么可以';我是否将字符串键存储在关联数组中?
我是D编程语言的新手,刚开始读D编程语言的书 我在尝试一个关联数组示例代码时出错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;
#!/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
ingword
一样有效。不管怎样,由于文本最初来自何处,它是一个char[]
而不是string
,如果要将其用作关联数组中的键,这会迫使您将其设置为idup
。但是,在一般情况下,最好只使用string
而不是char[]
。那么你就不需要idup
任何东西了
编辑:
实际上,即使您发现一个情况,从 CAR[]/COD> > <代码> String < /C> >似乎都是安全和必要的,请考虑使用<代码> STD.Extry.AssiMeUnQuess()/<代码>()。当您需要并且知道可以将可变数组转换为不可变数组时,它本质上是首选的方法。通常情况下,如果您构建了一个数组,而该数组不能使其不可变,因为您必须分块执行,但它没有其他引用,并且您不想创建它的深度副本,那么就需要执行此操作。但是,在像您询问的示例这样的情况下,它不会有用,因为您确实需要复制数组
dict[word.idup] = newId;