Небезопасно удалять элементы из связанного хеша DB_File при повторении с `each`?

Эта проблема

Я использую NetBSD 6.1, Perl v5.18.1 и DB_File v1.818. Если я перебираю хеш, связанный с DB_File, используя each и удалить каждый элемент из хэша, не все элементы будут удалены. Вот скрипт, демонстрирующий проблему:

use strict;
use warnings;
use DB_File;

my $dbfile = "/tmp/foo.db";
! -f $dbfile or unlink($dbfile) or die("unable to delete $dbfile");

my %db;
tie(%db, "DB_File", "/tmp/foo.db", O_RDWR|O_CREAT, 0644);

# add some random records
my @chars = ("0".."9", "a".."f");
for (1..10) {
    my ($key, $val);
    $key .= $chars[rand(@chars)] for 1..10;
    $val .= $chars[rand(@chars)] for 1..32;
    $db{$key} = $val;
}

# this doesn't delete everything from the database!
keys(%db); # reset the iterator
while (my ($key, $val) = each(%db)) {
    delete $db{$key};
}

foreach (keys(%db)) {
    print("\$db{$_} = $db{$_}\n");
}

untie(%db);

Когда я запускаю его, 4 (или иногда 5) из 10 записей не удаляются:

$db{4a8e5792e0} = 7a4d078a3f0f3cba750cb395fcc3343d
$db{d28e8cb226} = 17af1122f0b94113416693b1c4165954
$db{a3ae4e2e24} = 3c15270cf16601722bd8106b1727dbc2
$db{886c469eb4} = f1496f83f7866d09c9e28aae8e1b62e6
$db{2c53ebd993} = facfe8228240878aac825de4d97ca22b

Если я запускаю сценарий в системе Linux (Ubuntu 14.04), он всегда работает (все записи удалены).

Если я переключусь на foreach зацикливание ключей, затем оно работает как в NetBSD, так и в Linux:

# this always works
foreach (keys(%db)) {
    delete $db{$_};
}

Что говорит документация

Я не смог найти ничего, что ясно говорит о том, что удаление во время итерации через each не всегда работает

Вот что мне удалось найти:

  • Документация дляforeach говорит:

    foreach вероятно, не будет делать то, что вы ожидаете, если VAR является связанной или другой специальной переменной.

    Я не уверен, что подразумевается под этим, но, как ни странно, foreach Дело в том, где это работает.

  • Документация дляeachговорит:

    Любая вставка в хеш может изменить порядок, как и любое удаление, за исключением того, что последний ключ, возвращенныйeach или жеkeysмогут быть удалены без изменения порядка.

    Для меня это означает, что это безопасноdeleteтекущая запись во время итерации.

  • Документация дляDB_Fileне упоминает удаление во время итерации.

Вопросы

Это проблема:

  • вызвано ошибкой в реализации БД Berkeley в NetBSD?
  • вызвано ошибкой в ​​DB_File?
  • известное ограничение each?
  • известное ограничение связанных хешей?
  • известное ограничение связанных хешей DB_File?

Почему foreach на клавиши работают когда each не делает?

1 ответ

Моя догадка, что поведение является ограничением связанных хэшей. Кажется, нет никакой гарантии, что DB_File не перефразирует при удалении самого последнего извлеченного ключа.

Вы также спросили о разнице между

while (my ($key, $val) = each(%db)) {
    delete $db{$key};
}

а также

foreach (keys(%db)) {
    delete $db{$_};
}

,

В первом случае вы вмешиваетесь в связанный хеш, перебирая связанный хеш. Так что есть вероятность, что итератор не достигнет всех ключей.

Во втором случае вы сначала перебираете связанный хеш, чтобы получить полный список ключей. При циклическом просмотре полного списка вы гарантированно удаляете все записи.

Другие вопросы по тегам