【问题标题】:Deleted but still defined?已删除但仍定义?
【发布时间】:2014-06-04 07:38:46
【问题描述】:

我正在处理 Perl 中的哈希值,但遇到了一些问题。在我的代码运行一段时间后,它会生成一个类似于以下内容的哈希值:

my %book = (
    +1 => {
        27 => 100,
        24 => 1000,
    },
    -1 => {
        30 => 200,
        31 => 500,
    }
);

在处理更多数据后,代码最终通过使用以下代码行删除了哈希的两个条目:

delete $book{-1}{30};
delete $book{-1}{31};

因此,哈希应该是半空的。但是,稍后在我的代码中运行以下 if 语句:

if ((defined $book{+1}) && (defined $book{-1})){
    do A
}else{
    do B
}

我的代码最终会执行“事情 A”而不是“事情 B”,因为哈希的“-1”侧已被删除,所以他应该这样做。这怎么可能?而且,最重要的是,如何解决这个问题?

【问题讨论】:

  • 惊讶的答案不只是建议改变你的 if 来测试&& %{ $book{-1} }

标签: perl hash undefined


【解决方案1】:

删除%{$books{-1}} 中的两个条目后,哈希引用$books{-1} 仍然存在,即使它为空。

use strict;
use warnings;

my %book = (
    +1 => {
        27 => 100,
        24 => 1000,
    },
    -1 => {
        30 => 200,
        31 => 500,
    }
);

delete $book{-1}{30};
delete $book{-1}{31};

use Data::Dump;
dd \%book;

输出:

{ -1 => {}, 1 => { 24 => 1000, 27 => 100 } }

如果您想修剪该条目,则必须为此编写显式逻辑

# Trim Hash
my @empty_keys = grep {!%{$book{$_}}} keys %book;
delete $book{$_} for @empty_keys;

更新

正如 ysth 所建议的,另一种解决方案是测试是否有任何哈希键为空:

if (! grep {!$book{$_} || !%{$book{$_}}} qw(1 -1)) {
    print "A\n";
} else {
    print "B\n";
}

【讨论】:

  • +1:这是从HoHkeys 获取没有值的条目的非常好的方法。谢谢分享。
  • 或者只是:delete @book{ grep !%{$book{$_}}, keys(%book) };
  • 请注意,在旧版本的 Perl 上,!%{$book{$_}} 会比 !keys(%{$book{$_}}) 慢。
  • 感谢您的回复!我上网想弄清楚 grep 函数是如何工作的,但我仍然对你的代码做了什么感到有些困惑。代码的哪一部分告诉 PERL 查看密钥是否为空?是感叹号吗?另外,如果我理解正确,我的代码的问题是我从未删除 %book 哈希中的“-1”键,而只删除了“sub”哈希的“30”和“31”键,对吗?
  • 你的结论是正确的。在你完成delete $book{-1}{31}; 之后,你也可以完成delete $book{-1} if !%{$book{-1}};。我演示的代码只是做了一般情况,寻找任何空的哈希引用。
【解决方案2】:

与其删除多余的哈希元素,我更愿意让使用%book 的代码对空哈希具有弹性,这通常并不难做到

我也会非常谨慎地使用带有数字键的哈希,因为有许多字符串可以表示相同的数字。例如,虽然'1' == '1.0'true'1' eq '1.0'false,如果这些值被用作哈希键,它们将引用不同的元素

有了这些附加条件,我会写以下内容

很抱歉,我目前只有一台平板电脑可以使用,IDE 不允许我将输出文本复制到此处粘贴。但它已经过测试并且可以正常工作

use strict;
use warnings;

my %book = (
  +1 => {
    27 => 100,
    24 => 1000,
  },
  -1 => {
    30 => 200,
    31 => 500,
  }
);

# $book{-1} becomes an empty hash
delete $book{-1}{30};
delete $book{-1}{31};

# remove elements that have no content
for (keys %book) {
  delete $book{$_} unless keys %{ $book{$_} };
}

use Data::Dumper;
$Data::Dumper::Useqq = 1;
print Dumper \%book;

【讨论】:

  • 感谢鲍罗丁的回复。你能解释一下$Data::Dumper::Useqq = 1;是做什么的吗?
  • 它改变了输出格式,使字符串显示在双引号中,空白和控制字符使用它们的转义序列等价物来表示。它使发现尾随换行符和嵌入的制表符变得非常容易
  • 也感谢您的回答。我不明白如果我删除了 for 语句中的“键”,为什么您的代码不再起作用;如果我将您的 for 循环重写为 for (keys %book){ if (!%{$book{$_}}){delete $book{$_}} 我不需要指定这些是键??
  • @g_puffo:像%book 这样的简单散列扩展为列表key1value1key2value2key3value3、...所以其他每一项都是元素。使用 keys %books 会得到 key1, key2, key3, ... 而这正是我们想要的
  • 我明白了;我认为既然你的for 循环中有keys,这意味着PERL 将只通过散列的键来查看。谢谢。
猜你喜欢
  • 1970-01-01
  • 2014-10-14
  • 2012-08-19
  • 1970-01-01
  • 2019-12-05
  • 1970-01-01
  • 1970-01-01
  • 2021-08-06
  • 2018-11-18
相关资源
最近更新 更多