2

问题

我正在使用 NetBSD 6.1、Perl v5.18.1 和 DB_File v1.818。each如果我使用并从哈希中删除每个项目来遍历与 DB_File 相关的哈希,则并非所有项目都被删除。这是一个演示问题的脚本:

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);

当我运行它时,10 条记录中有 4 条(有时是 5 条)没有被删除:

$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说:

    任何插入到散列中的操作都可能会更改顺序,任何删除操作也会更改顺序,但最近的键返回eachkeys可能会在不更改顺序的情况下被删除。

    对我来说,这意味着delete在迭代时对当前条目是安全的。

  • 文档DB_File在迭代时没有提到删除。

问题

是不是这个问题:

  • NetBSD 的 Berkeley DB 实现中的错误引起的?
  • 由 DB_File 中的错误引起的?
  • 的已知限制each
  • 绑定哈希的已知限制?
  • DB_File 绑定哈希的已知限制?

为什么foreach在按键不起作用时起作用each

4

1 回答 1

1

我的预感是这种行为是绑定哈希的限制。似乎无法保证 DB_File 在删除最近获取的密钥时不会重新散列。

您还询问了两者之间的区别

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

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

.

在第一种情况下,您在迭代绑定哈希时干预了绑定哈希。因此,迭代器有可能无法到达所有键。

在第二种情况下,您首先迭代绑定的哈希以获得完整的键列表。循环遍历完整列表时,您可以保证删除所有条目。

于 2014-05-03T16:42:22.420 回答