Лучше ли проверять ключи хэша Perl на истину или на существование?
Является ли это более предпочтительным при назначении хэша только ключей (где значения действительно не нужны), чтобы сказать:
$hash{$new_key} = "";
Или сказать:
$hash{$new_key} = 1;
Требуется, чтобы вы проверяли ключ с помощью exists
, другой позволяет вам сказать:
if (exists $hash{$some_key})
или
if ($hash{$some_key})
Я думаю, что назначение 1 будет лучше, но есть ли проблемы с этим? Это даже имеет значение?
Ответы
Ответ 1
Это зависит от того, нужен ли вам ключ для существования или иметь истинное значение. Испытайте то, что вам нужно. Если вы используете хэш только для того, чтобы увидеть, есть ли что-то в списке, существует() путь. Если вы делаете что-то еще, проверка значения может быть способом.
Ответ 2
Когда значения не нужны, вы часто увидите эту идиому:
my %exists;
$exists{$_}++ for @list;
Это означает, что он должен быть 1.
Ответ 3
Если вы пытаетесь сохранить память (что обычно имеет значение только при очень большом хэше), вы можете использовать undef как значение и просто проверить его существование. Undef реализуется как singleton, поэтому тысячи undefs - всего лишь указатели на одно и то же значение. Установка каждого значения в пустую строку или 1 будет выделять другое скалярное значение для каждого элемента.
my %exists;
@exists{@list} = ();
В свете вашего более позднего комментария о вашем предполагаемом использовании, это идиома, которую я видел и использовал много раз:
my %seen;
while (<>) {
next if $seen{$_}++; # false the first time, true every successive time
...process line...
}
Ответ 4
Предположим, вам действительно нужно проверить наличие ключей, но вы написали код, который проверяет правду. Он проверяет правду во всей вашей программе в разных местах. Затем внезапно появляется, что вы что-то неправильно поняли, и вы должны фактически хранить сопоставление от ваших ключей до строковых значений; строки должны использоваться в том же потоке данных, который вы уже реализовали.
И строки могут быть пустыми!
Следовательно, вы должны либо реорганизовать свою программу, либо создать другой хеш, потому что проверка правды больше не проверяет существование. Это не произойдет, если вы проверили существование с самого начала.
(отредактированный coz dunno, почему его проголосовали.)
Ответ 5
* Обновление: * Синан указывает, что мой осторожный подход к созданию хэш-элементов датирован, а не проблема для новых Perls. Я отредактировал свой пост ниже и добавил новые мысли по этому вопросу.
Проблема с просто проверкой истины заключается в том, что вы можете изменить хэш с помощью старой версии Perl, о которой я узнал. Этот код безопасен с Perl 5.8:
my %foo = ();
if( $foo{bar} ) {
print "never happens";
}
print keys %foo;
Это плохая часть смешанного благословения авто-вивификации (по сравнению с тем, что мне нравится auto-viv, но это то, где он болит).
Во многих ситуациях это не имеет большого значения. Но это потенциальная проблема, о которой нужно знать. Я обращаюсь к этому в своем коде с помощью блокировки любого хеша, который должен оставаться неизменным.
На практике я либо завершаю, всегда выполняю тест существования перед булевым тестом.
if( exists $foo{bar} and $foo{bar} ) {
# hash is not modified due to short circuit
}
Такое же изменение структуры данных может происходить с массивами. Если вы заходите в $foo[2000]
, тогда массив будет расширен. Поэтому может быть хорошей идеей проверить существование, прежде чем вы случайно расширите массив. На практике это было гораздо меньше проблемы, чем соответствующее поведение хэша. < - Ирония заключается в том, что вы можете
используется только в массиве на perls 5.6 и новее, где, по-видимому, эта проблема исправлена.
Если мне нужно перейти в структуры данных, я использую Data:: Diver. Он автоматически проверяет существование на каждом уровне структуры, чтобы предотвратить случайное изменение структуры данных.
Самое главное - быть последовательным в каждой программе script/. Самый простой способ столкнуться с проблемами - это проверить существование здесь, но правда там. Особенно, если вы получаете доступ к одному и тому же хэшу для обоих наборов тестов.
Заключительные соображения по моему обновлению относительно автовивификации:. Штурм исследований показал несколько вещей. Я должен был проверить свой код перед публикацией - если не сделаю этого, я распространяю дезинформацию, о которой я прошу прощения. Я также обнаружил, что есть еще некоторые скрытые проблемы с автовивизацией, задерживаясь - достаточно, чтобы существовал открыть предмет todo, чтобы все было в порядке. Поэтому, хотя это может быть неправильным, старомодным и немым, я продолжу явно предпринимать шаги по контролю автовивификации и ограничить его появлением только тогда, когда я хочу, чтобы это произошло. FWIW, автовивитация - отличная вещь, когда она работает. Я думаю, что специальная оболочка if
для предотвращения autoviv - это правильная вещь - она избавляется от необходимости в большом количестве дополнительного кода, но мне хотелось бы найти некоторые документы, которые подробно описывали бы это поведение.
Ответ 6
Как говорится в предыдущем ответе, это зависит от того, чего вы пытаетесь достичь; если вы просто пытаетесь получить (например) уникальные значения из некоторого набора (элементы которого затем образуют ключи), вы можете просто использовать exists (также может помочь поймать дубликаты, если вы проверите наличие существует прежде, чем назначить значение).
Не зная приложения, трудно быть более конкретным.
Ответ 7
См. также Автовивитация: что это такое и почему меня волнует?.
Ответ 8
Обычно я проверяю defined
значения. Это средний случай, который ты оставляешь. Не совсем "правда" не совсем "существует". (В основном, но не совсем.)
Теперь теоретически более общий способ exists
, как в
if ( exists $hash{$key} ) return 'strawberry';
Это охватывает случай, когда ключ существует, а значение 0
, или когда ключ был назначен undef
. Ключ просто должен существовать, чтобы пройти этот тест.
Однако я редко обнаружил необходимость проверить наличие ключа.
-
Хеши часто являются частью определенного API, и если вы их обрабатываете, у вас есть представление о диапазоне значений, которые могут быть сохранены. Элемент конфигурации будет искать конкретные вещи; и как неупорядоченные ключи параметров, подпрограммы будут искать конкретные вещи.
-
Я нахожу идею "бесконечной таблицы" очень гибкой концепцией. И exists x
<= > defined x
работает для этого. Каждое мыслимое значение "установлено" в таблице, но определяется только конечное число ключей, остальные считаются undefined.
В результате обычно, если значение не определено в хэше, мне все равно, что это такое. Я считаю это ложным значением. Хранение undef
и не хранение вообще ничего эквивалентны в большинстве вещей, которые я пишу. Это далее мотивируется пунктом ниже.
-
В большинстве случаев, когда мне нужно знать, есть ли ключ в таблице, мне нужно использовать его для чего-то другого. Сначала я храню значение локально, а затем проверяю, если для определенного значения.
my $value = $hash{$key};
if ( defined $value ) {
push @valid_values, $value;
}
Если бы я мог быть уверен, что существует некоторая локальная оптимизация общего подвыражения между поиском для exists
и поиском для использования значения, тогда я бы не был так придирчив к этому. Но я не люблю извлекать из хэша более одного раза. Поэтому я 1) кеширую значение и 2) проверяю его - каждый раз.
Тем не менее, я могу ужесточить критерии, я знаю, что значение не должно быть 0
, например, в таблице поиска или таблице параметров. Поэтому я иногда испытываю истину. Но я все равно могу ужесточить тест.
if ( ( $hash{$key} || '' ) =~ m/^(?:Bears|Lions|Packers|Vikings)$/ ) {
$nfc_north++;
}
- Конечно, принцип работы здесь заключается в том, что
defined
работает для "неограниченных" таблиц. Где все мыслимые значения "установлены" в таблице, но определяется только конечное число ключей.
Существует случай, когда вы можете работать с анонимным хэшем. Но тогда ваш интерес к ключам, которые не могут быть удовлетворены keys
или values
? Даже если вы создаете универсальную хэш-функцию "удобство", лучше не относиться к себе с наличием определенных ключей, чтобы быть абсолютно нейтральным к тому, что там хранит кто-то еще.