如何将具有关联作用的类似哈希的对象传递给期望哈希的构造函数?

Håk*_*and 6 perl6 raku

我正在尝试定制哈希。以下是尝试为配置类哈希实现更简单的查找:

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,
);
Run Code Online (Sandbox Code Playgroud)

输出为:

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
Run Code Online (Sandbox Code Playgroud)

(第70行是行MyTest.new(

如果我将普通的哈希传递给构造函数,例如使用%hash代替,则代码可以正常工作%cfg

MyTest.new(
    config  => %hash,
);
Run Code Online (Sandbox Code Playgroud)

Jon*_*ton 8

该类还需要Iterable扮演以下角色:

class Config does Associative[Cool,Str] does Iterable {
    ...
}
Run Code Online (Sandbox Code Playgroud)

这就要求实现一种iterator方法。在这种情况下,将其委托给嵌套哈希的迭代器可能是最简单的:

method iterator() { %!hash.iterator }
Run Code Online (Sandbox Code Playgroud)

这样,错误得以解决。(默认情况下iterator,迭代器为包含对象本身的1项序列,因此会观察到错误。)

必需使用迭代器,因为使用哈希属性构造对象的语义是分配而不是绑定。当我们将其分配给哈希后,我们Iterator将从要分配的对象中获取一个,然后对其进行迭代以获取要分配的值。我提到这一点是为了防止您的期望受到约束-也就是说,MyTest它将引用的实例Config。为此,需要编写一个BUILD在中进行绑定的自定义MyTest或将其声明为has $.config替代,这意味着它将仅引用Config实例,而不是将实例中的值复制到新的哈希中。

  • 谢谢!我想我会尽量避免复制哈希,所以根据您的回答,我在`Config`和`submethod BUILD(:%config){中添加了submethod BUILD(:%hash){%!hash:=%hash} %!config:=%config}`到`MyTest`以创建绑定。 (2认同)
  • 是的,这就是这样做的方式。 (2认同)