2011-01-06 3 views
16

저는 D 프로그래밍 언어를 처음 사용하기 시작했습니다. D 프로그래밍 언어 책을 읽기 시작했습니다.연관 배열에 문자열 키를 저장할 수없는 이유는 무엇입니까?

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

내가 DMD는 2.051

나는 추측했다 컴파일 사용하고 있습니다 : 하나의 연관 배열 예제 코드를 시도 할 때

내가 오류로 실행

#!/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이 오류 메시지가 표시 연관 배열에 대한 규칙은 TDPL 북 이후 변경되었습니다.

문자열 배열 키를 사용하여 연관 배열을 어떻게 사용해야합니까?

감사합니다.

업데이트 :

나는이 책의 뒷부분에서 해결책을 찾았습니다.

string.idup을 사용하여 배열에 넣기 전에 중복 불변 값을 만듭니다.

그렇게

dict[word.idup] = newId; 

일을 할 것입니다.

하지만 효율적인가요?

+4

이 질문에 몇 달 또는 몇 년 후 오는 사람을위한 참고 사항 - 인쇄 된 예제에는 다른 잘못된 것이 있습니다. 연관 배열 유형에 대해 ulong이 아닌 uint를 사용하고 std.array를 가져 와서 스플리터를 가져와야합니다. http://www.digitalmars.com/d/archives/digitalmars/D/learn/problems_with_DPL_example._30009.html – DarenW

답변

25

연관 배열에서는 키가 변경되지 않아야합니다. 변경할 수없는 경우 변경 될 수 있다는 사실에 대해 생각할 때 의미가 있습니다. 즉 해시가 변경된다는 것을 의미합니다. 즉, 값을 다시 가져올 때 컴퓨터에서 찾지 못한다는 의미입니다. 그리고 당신이 그것을 대체하려고한다면, 연관 배열에 다른 값을 추가하게 될 것입니다. 그래서 올바른 해시와 잘못된 해시를 가진 값을 갖게 될 것입니다. 그러나 키가 변경되지 않으면 변경할 수 없으므로 이러한 문제가 없습니다.

dmd 2.051 이전에는 예제가 작동했습니다 (bug). 이제는 수정되었으므로 TDPL의 예가 더 이상 올바르지 않습니다. 그러나, 연관 배열에 대한 규칙이 변경되지 않았기 때문에 잡히지 않은 버그가 있음을 알 수 있습니다. 그것이 없어야 할 때 컴파일 된 예가 Andrei가 놓쳤습니다. official errata for TDPL에 나열되어 있으며 향후 인쇄물에서 수정해야합니다.

수정 된 코드는 dictionary[word.idup] 또는 dictionary[to!string(word)] 중 하나를 사용해야합니다. word.idup은 변경 불가능한 word의 사본을 작성합니다. 한편, to!string(word)wordstring으로 가장 적절하게 변환합니다.이 경우 wordchar[]이므로 idup을 사용합니다. 그러나 word이 이미 string 인 경우 전달 된 값을 반환하기 때문에 불필요하게 복사 할 필요가 없습니다. 따라서 일반적으로 to!string(word)이 더 나은 선택입니다 (특히 템플릿 기능에서).이 경우 어느 쪽이든 올바르게 작동합니다 (to!()std.conv에 있음).

기술적으로는 char[]string으로 전송할 수 있지만 일반적으로 좋지 않습니다. char[]을 결코 바꿀 수 없다는 것을 알고있는을 알게된다면, 컴파일러는 그 결과로 string이 결코 바뀔 수 없다고 생각할 것이므로 일반적으로 문제를 일으킬 수 있습니다. 잘못된 코드입니다. 그것은 심지어 단절 될 수도 있습니다. 따라서 프로파일 링을 통해 복사를 피하는 추가 효율성을 필요로하지 않는다면, 처음에는 string을 사용하여 사본을 피할 수 없습니다 (변환이 필요하지 않음) , string이 절대로 변경되지 않는다는 것을 알고있는을 알고 있습니다.

일반적으로 문자열 복사의 효율성에 대해서는 크게 걱정하지 않아도됩니다. 일반적으로 char[] 대신 string을 사용해야하므로 특히 비효율적 인 걱정없이 dupidup처럼 전체 내용을 복사하는 대신 참조 (예 : str1 = str2;)를 복사 할 수 있습니다. 이 예에서의 문제점은 string이 아닌 char[]을 반환한다는 것입니다 (필요하지 않은 경우 데이터 복사를 방지하는 것으로 추정 됨). 따라서 splitter()char[]을 반환하므로 대신 wordchar[]입니다. 이제 idup 키 대신 splitter(strip(line.idup)) 또는 splitter(strip(line).idup)을 사용할 수 있습니다. 그런 식으로 splitter()char[]이 아니라 string을 반환 할 것입니다.하지만 이것은 실질적으로 idup ing word처럼 효율적일 것입니다. 그럼에도 불구하고 원래 텍스트가 어디서 왔는지에 따라 string 대신 char[]이되어 연관 배열에서 키로 사용하려는 경우 라인을 따라 어딘가에 idup이됩니다. 그러나 일반적으로 string이 아닌 char[]을 사용하는 것이 좋습니다. 그렇다면 idup은 필요하지 않습니다.

편집 : 사실
, 당신은 stringchar[]에서 캐스팅 상황이 안전하고 필요한 모두 같다 찾을 경우에도 std.exception.assumeUnique() (documentation)를 사용하는 것이 좋습니다. 기본적으로 변경할 수있는 배열을 필요에 따라 변경할 수있는 배열로 변환하는 기본 방법입니다. 그것은 일반적으로 당신이 부분적으로해야만하기 때문에 변경할 수없는 배열을 만들었지 만, 다른 참조는 없기 때문에 배열의 전체 복사본을 만들고 싶지 않을 때 수행됩니다. 배열을 복사해야하므로 예제와 같은 상황에서는 유용하지 않습니다.

+1

상세하고 통찰력있는 답변 주셔서 감사합니다! 이제 문자열과 char []의 포인트를 얻었습니다. 매우 감사합니다! –

+1

+1, 좋은 답변입니다. 오랜 틈새에서 D로 돌아와서 2.047 컴파일러가 조금 움직 였다는 것을 알았습니다! – shambulator

1

아니요, 분명히 문자열을 복제하기 때문에 효율적이지 않습니다. 문자열을 만들면 이 메모리에 수정되지 않으므로 복제하지 않고 cast(immutable)str 캐스트를 명시 적으로 사용하십시오.

(비록 가비지 컬렉터가 잘 작동하는 것으로 나타 났지만 병목 현상이 보이지 않으면 실제로 시도하지 말 것을 제안합니다. 나중에 병목 현상을 확인하기 위해 문자열을 변경해야하기 때문입니다. 나중에 병목 현상을 발견하는 데 도움이되는 코드입니다.

+2

을 참조하십시오.이 예에서는 여분의 idup이 성능 문제는 아니지만 파일 입력 코드.결과적으로 당신이 거기에있는 idup을 피하려고한다면 당신은 다른 곳에서와 같은 비용을 지불하고있는 것입니다. – BCS

관련 문제