我正在尝试customized hashes。以下是尝试为配置类哈希实现更简单的查找:
use v6;
class X::Config::KeyNotFound is Exception {
method message() {
"Key not found!";
}
}
# A hash that allows for nested lookup using a '.' to separate keys.
# (This means that keys themselves cannot contain a dot)
# For example:
#
# %h = Config.new(%(a => %(b => 1)));
# my $foo = %h<a.b>; # <-- $foo = 1
#
class Config does Associative[Cool,Str] {
has %.hash;
multi method AT-KEY ( ::?CLASS:D: $key) {
my @keys = $key.split('.');
my $value = %!hash;
for @keys -> $key {
if $value{$key}:exists {
$value = $value{$key};
}
else {
X::Config::KeyNotFound.new.throw;
}
}
$value;
}
multi method EXISTS-KEY (::?CLASS:D: $key) {
my @keys = $key.split('.');
my $value = %!hash;
for @keys -> $key {
if $value{$key}:exists {
$value = $value{$key};
}
else {
return False;
}
}
return True;
}
multi method DELETE-KEY (::?CLASS:D: $key) {
X::Assignment::RO.new.throw;
}
multi method ASSIGN-KEY (::?CLASS:D: $key, $new) {
X::Assignment::RO.new.throw;
}
multi method BIND-KEY (::?CLASS:D: $key, $new){
X::Assignment::RO.new.throw;
}
}
my %hash = a => %(aa => 2, ab => 3), b => 4;
my %cfg := Config.new( hash => %hash );
# A dummy class to illustrate the problem:
class MyTest {
has %.config;
}
# Now this code does not work:
MyTest.new(
config => %cfg,
);
输出为:
Odd number of elements found where hash initializer expected:
Only saw: Config.new(hash => {:a(${:aa(2), :ab(3)}), :b(4)})
in block <unit> at ./p.p6 line 70
(第70行是MyTest.new(
行)
如果我将普通哈希传递给构造函数,例如使用%hash
而不是%cfg
,则代码可以正常工作
MyTest.new(
config => %hash,
);
答案 0 :(得分:8)
该类还需要扮演Iterable
角色:
class Config does Associative[Cool,Str] does Iterable {
...
}
这要求实现iterator
方法。在这种情况下,将其委托给嵌套哈希的迭代器可能是最简单的:
method iterator() { %!hash.iterator }
由此,错误得以解决。 (默认的iterator
给出的迭代器是一个包含对象本身的1个项目的序列,因此会观察到错误。)
迭代器是必需的,因为使用哈希属性构造对象的语义是赋值,而不是绑定。当我们将其分配给哈希后,我们将从要分配的对象中获取一个Iterator
,然后对其进行迭代以获取要分配的值。我提到这一点是为了使您的期望具有约束力-即MyTest
将引用Config
的实例。为此,需要编写一个在BUILD
中进行绑定的自定义MyTest
,或者将其声明为has $.config
,这意味着它将仅引用Config
实例而不是将其中的值复制到新的哈希中。