Почему я не могу хранить строковые ключи в ассоциативном массиве?

Как насчет этого пути?

user.Methods.ShowGreetings.call(user, args);

Таким образом, вы можете получить доступ к user.Name в ShowGreetings

var user = {
    Name: "Some user",
    Methods: {
        ShowGreetings: function(arg) {
            console.log(arg, this.Name);
        },
        GetUserName: function() { }
    },
    Init: function() {
        this.Methods.ShowGreetings.call(this, 1);
    }
};

user.Init(); // => 1 "Some user"
16
задан Jonathan M Davis 6 January 2011 в 07:08
поделиться

1 ответ

Ассоциативные массивы требуют, чтобы их ключи были неизменными. Это имеет смысл, когда вы думаете о том, что если он не является неизменяемым, то он может измениться, что означает, что его хеш-код изменяется, а это означает, что когда вы снова получите значение, компьютер его не найдет. И если вы замените его, вы получите еще одно значение, добавленное к ассоциативному массиву (поэтому у вас будет одно с правильным хешем и одно с неправильным хешем). Однако, если ключ является неизменным, он не может измениться, и поэтому такой проблемы нет.

До dmd 2.051, пример работал (который был ошибкой ). Теперь это было исправлено, поэтому пример в TDPL больше не верен. Тем не менее, дело не столько в том, что правила для ассоциативных массивов изменились, сколько в том, что в них была ошибка, которая не была обнаружена. Пример скомпилирован, когда его не должно было быть, и Андрей пропустил его. Он указан в официальных исправлениях для TDPL и должен быть исправлен в будущих печатных изданиях.

Исправленный код должен использовать либо dictionary[word.idup], либо dictionary[to!string(word)]. word.idup создает дубликат word, который является неизменным. to!string(word), с другой стороны, преобразует word в string наиболее подходящим способом. Поскольку word является char[] в этом случае, это будет использовать idup. Однако, если бы word уже было string, он просто вернул бы переданное значение и не стал бы его копировать без необходимости. Так, в общем случае, to!string(word) является лучшим выбором (особенно в шаблонных функциях), но в этом случае любой из них работает просто отлично (to!() в std.conv).

Технически возможно привести char[] к string, но обычно это плохая идея. Если вы знаете , что char[] никогда не изменится, то вы можете сойти с рук, но в общем случае вы рискуете проблемами, так как компилятор тогда предположит, что результирующий string никогда не может измениться, и это может генерировать код, который является неправильным. Это может даже сегфо. Так что не делайте этого, пока профилирование не покажет, что вам действительно нужна дополнительная эффективность предотвращения копирования, иначе вы не сможете избежать копирования, выполнив что-то вроде простого использования string во-первых (так что преобразование не будет необходимо), и вы знаете , что string никогда не изменится.

В общем, я бы не слишком беспокоился об эффективности копирования строк. Как правило, вы должны использовать string вместо char[], поэтому вы можете копировать их (то есть копировать их ссылки (например, str1 = str2;), а не копировать все их содержимое, как это делают dup и idup) не беспокоясь о том, что это особенно неэффективно. Проблема с примером заключается в том, что stdin.byLine() возвращает char[], а не string (предположительно, чтобы избежать копирования данных, если в этом нет необходимости). Так, splitter() возвращает char[], и поэтому word является char[] вместо 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() ( документация ). По сути, это предпочтительный способ преобразования изменяемого массива в неизменяемый, когда вам нужно знать об этом. Обычно это делается в тех случаях, когда вы создали массив, который нельзя было сделать неизменным, потому что вам приходилось делать это по частям, но у которого нет других ссылок, и вы не хотите создавать его глубокую копию. Это не будет полезно в ситуациях, подобных примеру, о котором вы спрашиваете, поскольку вам действительно нужно скопировать массив.

25
ответ дан Jonathan M Davis 6 January 2011 в 07:08
поделиться
Другие вопросы по тегам:

Похожие вопросы: