【问题标题】:How to pass hash-like object which does associative role to a constructor expecting a hash?如何将具有关联作用的类哈希对象传递给期望哈希的构造函数?
【发布时间】:2019-04-07 09:05:37
【问题描述】:

我正在尝试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,
);

【问题讨论】:

    标签: raku


    【解决方案1】:

    类还需要做Iterable角色:

    class Config does Associative[Cool,Str] does Iterable {
        ...
    }
    

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

    method iterator() { %!hash.iterator }
    

    这样,错误就解决了。 (默认的 iterator 给出了一个迭代器,它是一个包含对象本身的 1 项序列,因此观察到了错误。)

    迭代器是必需的,因为构造具有哈希属性的对象的语义是赋值,而不是绑定。当我们分配到一个散列中时,我们会从我们正在分配的东西中获得一个Iterator,并对其进行迭代以获取要分配的值。我会提到这一点,以防您的期望具有约束力——也就是说,MyTest 将引用Config 的实例。为此,需要编写一个自定义的BUILD,它在MyTest 中进行绑定,或者将其声明为has $.config,这意味着它只会引用Config 实例,而不是从中复制值进入一个新的哈希。

    【讨论】:

    • 谢谢!我想我会尽量避免复制哈希,所以根据你的回答,我将 submethod BUILD(:%hash) { %!hash := %hash } 添加到 Configsubmethod BUILD(:%config) { %!config := %config }MyTest 以创建绑定。
    • 是的,就是这样。
    猜你喜欢
    • 2011-02-27
    • 1970-01-01
    • 2017-07-22
    • 1970-01-01
    • 2023-03-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-07
    相关资源
    最近更新 更多