Skip to content

Perl 哈希

Perl 中的哈希(hash)是键值对(key-value pair)的无序集合。在其他语言中,哈希也称为关联数组(associative arrays)或字典(dictionaries)。哈希变量名前缀是百分号(%),例如 %my_hash。要访问哈希中的单个元素(值),使用美元符号($)作为前缀,后跟哈希名称,然后是花括号中的键,例如 $my_hash{'some_key'},如果键是简单的裸字(bareword),也可以写作 $my_hash{some_key}。

Perl 哈希中的键(key)总是字符串。如果你使用数字作为键,Perl 会将其转换为字符串表示。值(value)可以是任何标量(scalar)(数字、字符串或引用)。

这里有一个简单的例子:

#!/usr/bin/perl
use strict;
use warnings;
my %student_ages = (
'Alice' => 22,
'Bob' => 24,
'Charlie' => 21,
);
print "Alice's age: $student_ages{'Alice'}\n";
print "Bob's age: $student_ages{'Bob'}\n";
# Using bareword for key (if simple enough, quotes are optional with =>)
# 使用裸字作为键(如果足够简单,=> 后可以省略引号)
my %capitals = (
USA => "Washington D.C.",
UK => "London",
Japan => "Tokyo",
);
print "Capital of UK: $capitals{UK}\n";

这将产生如下输出:

Alice's age: 22
Bob's age: 24
Capital of UK: London

可以通过几种方式创建哈希:

  1. 逐个赋值键值对:
my %user_data;
$user_data{'name'} = "David";
$user_data{'id'} = 101;
$user_data{'email'} = 'david@example.com';
  1. 使用键值对列表: 列表被解释为交替的键和值。
my %contact_info = ('name', 'Eve', 'phone', '555-1234', 'city', 'New City');
  1. 使用 =>(胖逗号,fat comma)操作符: 这是逗号的别名,但通过视觉上配对键和值来提高可读性。如果左侧操作数是一个裸字(bareword)(看起来像标识符的简单字符串),它也会自动加上引号。这是最常见和推荐的方式。
my %book_details = (
title => "The Modern Perl Handbook",
author => "Perl Hacker",
year => 2023,
ISBN => "978-0123456789", # Keys are strings
# 键是字符串
);

要检索值,请使用 $ 标记(sigil)和哈希名称,后跟花括号中的键。如果键不存在,访问它将返回 undef(未定义)。

#!/usr/bin/perl
use strict;
use warnings;
my %config = (
host => "localhost",
port => 8080,
timeout => 30,
);
print "Host: $config{'host'}\n";
print "Port: $config{port}\n"; # Bareword key in access
# 访问时使用裸字键
my $non_existent_value = $config{'user'}; # Key 'user' doesn't exist
# 键 'user' 不存在
if (defined $non_existent_value) {
print "User: $non_existent_value\n";
} else {
print "User key is not defined.\n";
}

输出:

Host: localhost
Port: 8080
User key is not defined.

哈希切片(hash slice)允许你通过提供一个键列表,一次性从哈希中检索多个值。使用 @ 标记(sigil)(因为你将获得一个值列表)。

#!/usr/bin/perl
use strict;
use warnings;
my %fruit_colors = (
apple => "red",
banana => "yellow",
grape => "purple",
orange => "orange",
);
my @selected_keys = ('apple', 'grape', 'kiwi'); # 'kiwi' doesn't exist
# 'kiwi' 不存在
my @colors = @fruit_colors{@selected_keys};
# or directly: my @colors = @fruit_colors{'apple', 'grape', 'kiwi'};
# 或者直接:my @colors = @fruit_colors{'apple', 'grape', 'kiwi'};
print "Colors found: " . join(", ", grep { defined } @colors) . "\n";
# grep filters out undef for non-existent keys
# grep 过滤掉不存在键对应的 undef 值
# For 'kiwi', its corresponding value in @colors will be undef.
# 对于 'kiwi',其在 @colors 中对应的值将是 undef。

输出:

Colors found: red, purple

keys %HASH 函数返回哈希中所有键的列表(顺序看似随机)。values %HASH 函数返回所有值的列表(如果在同一个未修改的哈希上调用,其顺序与 keys %HASH 返回的对应键的顺序相同)。

#!/usr/bin/perl
use strict;
use warnings;
my %days_in_month = (
January => 31,
February => 28, # Ignoring leap years for simplicity
# 为简单起见,忽略闰年
March => 31,
);
my @months = keys %days_in_month;
print "Months: " . join(", ", sort @months) . "\n";
my @num_days = values %days_in_month;
print "Number of days (in some order): " . join(", ", @num_days) . "\n";
# 天数(顺序不定):...
# Iterating over key-value pairs
# 迭代键值对
while (my ($month, $days) = each %days_in_month) {
print "$month has $days days.\n";
# $month 有 $days 天。
}
# Note: `each` iterates in an apparently random order. Modifying the hash while iterating with `each` can lead to issues.
# 注意:`each` 以看似随机的顺序迭代。在使用 `each` 迭代时修改哈希可能导致问题。

输出(未排序的键/值和 each 的顺序可能会有所不同):

Months: February, January, March
Number of days (in some order): 28, 31, 31
January has 31 days.
March has 31 days.
February has 28 days.

exists $HASH{key} 函数返回真,如果指定的键存在于哈希中,即使其值为 undef。这与检查 defined $HASH{key} 不同,后者检查与键关联的值是否已定义。

#!/usr/bin/perl
use strict;
use warnings;
my %settings = (
user => "admin",
level => 5,
theme => undef, # Key exists, but value is undef
# 键存在,但值为 undef
);
if (exists $settings{'theme'}) {
print "'theme' key exists.\n";
# 'theme' 键存在。
} else {
print "'theme' key does not exist.\n";
# 'theme' 键不存在。
}
if (defined $settings{'theme'}) {
print "Value for 'theme' is defined: $settings{'theme'}\n";
# 'theme' 的值已定义:...
} else {
print "Value for 'theme' is not defined (it's undef).\n";
# 'theme' 的值未定义(它是 undef)。
}
if (exists $settings{'password'}) {
print "'password' key exists.\n";
# 'password' 键存在。
} else {
print "'password' key does not exist.\n";
# 'password' 键不存在。
}

输出:

'theme' key exists.
Value for 'theme' is not defined (it's undef).
'password' key does not exist.

要查找哈希中键值对的数量,可以在标量上下文(scalar context)中评估哈希,或在标量上下文评估 keys %HASH 或 values %HASH。最直接的方法通常是 scalar %HASH,如果哈希确定包含键值对,但 scalar keys %HASH 非常常见且明确。

#!/usr/bin/perl
use strict;
use warnings;
my %inventory = (
apples => 100,
bananas => 150,
oranges => 75,
);
my $num_items_method1 = scalar(keys %inventory);
print "Number of item types (method 1): $num_items_method1\n";
# 项目类型数量(方法 1):...
# In scalar context, a hash used in a numeric context returns true if it has elements, false otherwise.
# 在标量上下文中,在数字上下文使用的哈希,如果包含元素则返回真,否则返回假。
# To get the count of pairs, you usually use keys or values in scalar context.
# 要获取键值对的数量,通常在标量上下文中使用 keys 或 values。
# A more idiomatic way to get the count of keys:
# 获取键数量更惯用的方式:
my $key_count = keys %inventory; # In scalar context, keys returns number of keys
# 在标量上下文中,keys 返回键的数量
print "Number of item types (method 2): $key_count\n";
# 项目类型数量(方法 2):...
# If a hash is empty, scalar context returns false (empty string or 0 depending on use)
# 如果哈希为空,标量上下文返回假(取决于用法,可能是空字符串或 0)
my %empty_hash;
if (%empty_hash) {
print "Empty hash is true in boolean context (this won't print for truly empty hash after Perl 5.22)\n";
# 空哈希在布尔上下文为真(对于 Perl 5.22 之后的空哈希,这不会打印)
} else {
print "Empty hash is false in boolean context.\n";
# 空哈希在布尔上下文为假。
}
# Prior to Perl 5.22, an empty hash in boolean scalar context was true.
# 在 Perl 5.22 之前,空哈希在布尔标量上下文中为真。
# From 5.22 onwards, it's false. This behavior was somewhat problematic.
# 从 5.22 开始,它为假。此行为之前有些问题。
# The reliable way to check for emptiness is `if (keys %empty_hash)`.
# 检查哈希是否为空的可靠方法是 `if (keys %empty_hash)`。

输出:

Number of item types (method 1): 3
Number of item types (method 2): 3
Empty hash is false in boolean context.

使用简单的赋值添加或更新键值对:$HASH{new_key} = $value;。要移除键值对,使用 delete $HASH{key} 函数。delete 返回与被删除键关联的值(如果键不存在则返回 undef)。

#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper; # For pretty-printing the hash
# 用于美化打印哈希
my %employee_data = (
101 => "Alice",
102 => "Bob",
);
print "Initial: " . Dumper(\%employee_data);
# 初始:...
# Add a new employee
# 添加新员工
$employee_data{103} = "Charlie";
print "After add: " . Dumper(\%employee_data);
# 添加后:...
# Update an existing employee's name
# 更新现有员工姓名
$employee_data{101} = "Alicia";
print "After update: " . Dumper(\%employee_data);
# 更新后:...
# Remove an employee
# 移除员工
my $deleted_value = delete $employee_data{102};
print "Deleted Bob. Value was: $deleted_value\n";
# 已删除 Bob。值曾是:...
print "After delete: " . Dumper(\%employee_data);
# 删除后:...

输出(Dumper 的具体格式可能略有不同):

Initial: $VAR1 = {
'101' => 'Alice',
'102' => 'Bob'
};
After add: $VAR1 = {
'101' => 'Alice',
'102' => 'Bob',
'103' => 'Charlie'
};
After update: $VAR1 = {
'101' => 'Alicia',
'102' => 'Bob',
'103' => 'Charlie'
};
Deleted Bob. Value was: Bob
After delete: $VAR1 = {
'101' => 'Alicia',
'103' => 'Charlie'
};