如何将具有关联作用的类哈希对象传递给需要哈希的构造函数? [英] How to pass hash-like object which does associative role to a constructor expecting a hash?

查看:31
本文介绍了如何将具有关联作用的类哈希对象传递给需要哈希的构造函数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在试验自定义哈希.以下是尝试为类似配置的散列实现更简单的查找:

I am experimenting with customized hashes. The following is trying to implement a simpler lookup for config-like 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( )

(Line 70 is the line MyTest.new( )

如果我将普通哈希传递给构造函数,则代码工作正常,例如使用 %hash 而不是 %cfg:

The code works fine if I pass a normal hash to the constructor instead, for example using %hash instead of %cfg:

MyTest.new(
    config  => %hash,
);

推荐答案

该类还需要做Iterable的作用:

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

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

Which requires that an iterator method be implemented. In this case, it's probably easiest to delegate to the iterator of the nested hash:

method iterator() { %!hash.iterator }

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

With this, the error is resolved. (The default iterator gives an iterator that is a 1-item sequence containing the object itself, thus the error observed.)

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

The iterator is required because the semantics of constructing an object with a hash attribute are assignment, not binding. When we assign into a hash, then we obtain an Iterator from the thing we're assigning from, and iterate it to get the values to assign. I mention this in case your expectation was binding - that is, that MyTest will reference the instance of Config. For that, one would need to write a custom BUILD that does binding in MyTest, or to declare it as has $.config instead, which means it will just reference the Config instance rather than copying the values out of it into a new hash.

这篇关于如何将具有关联作用的类哈希对象传递给需要哈希的构造函数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆