我如何从哈希中构建perl6中的对象



在perl5中您可以做这样的事情:

#!/usr/bin/env perl
use 5.010;
package Local::Class {
  use Moo;
  has [qw( x y )] => ( is => 'ro');
  sub BUILDARGS { shift; return (@_) ? (@_ > 1) ? { @_ } : shift : {} }
}
use Local::Class;
# Create object directly
my $x = Local::Class->new( x => 1, y => 10 );
say $x->x, ' ', $x->y; # 1 10
# Arguments from a hash
my %hash = ( x => 5, y => 20 );
$x = Local::Class->new(%hash);
say $x->x, ' ', $x->y; # 5 20
# Arguments from a hash reference
$x = Local::Class->new(%hash);
say $x->x, ' ', $x->y; # 5 20

由于使用自定义BUILDARGS方法,底部的两个呼叫相同,这基本上将它们都变成了MOO(SE)期望的Hash引用的类型?。

但是我该如何在perl6中做同样的事情?

#!/usr/bin/env perl6
class Local::Class {
  has $.x;
  has $.y;
}
my $x;
# This works
$x = Local::Class.new( x => 1, y => 10 );
say $x.x, ' ', $x.y; # 1 10
# This doesn't
my %hash = %( x => 5, y => 20 );
$x = Local::Class.new(%hash);
# This doesn't either
$x = Local::Class.new(item(%hash));
# Both die with:
# Default constructor for 'Local::Class' only takes named arguments

那么,我该如何进行在其他地方创建的哈希,并将其转换为类的默认构造函数所需的命名参数?

使用默认构造函数

默认的.new构造函数映射命名参数为公共属性。

在您的示例中,您将哈希作为位置参数传递。您可以使用|语法将哈希条目插入参数列表中,如命名参数:

$x = Local::Class.new(|%hash);

但是,请注意,如果您的班级具有has @.z

的数组属性,这将引起问题
class Local::Class {
    has $.x;
    has $.y;
    has @.z;
}
my %hash = x => 5, y => 20, z => [1, 2];
my $x = Local::Class.new(|%hash);
say $x;  # Local::Class.new(x => 5, y => 20, z => [[1, 2],])

这是因为像所有哈希一样,%hash将其每个值都放在项目容器中。因此,该属性将被初始化为@.z = $([1, 2]),这会导致单个元素的数组,该数组是原始数组。

避免这种情况的一种方法是使用Capture而不是Hash

my $capture = ( x => 5, y => 20, z => [1, 2] );
my $x = Local::Class.new(|$capture);
say $x;  # Local::Class.new(x => 5, y => 20, z => [1, 2])

或使用Hash,然后使用<>将其值删除,然后将整个物品变成Map(与Hash不同,不会添加项目容器),然后将其插入参数列表:

my %hash = x => 5, y => 20, z => [1, 2];
my $x = Local::Class.new(|Map.new: (.key => .value<> for %hash));
say $x;  # Local::Class.new(x => 5, y => 20, z => [1, 2])

使用自定义构造函数

如果您想在课堂本身而不是在使用类的代码中处理此问题,则可以修改构造函数。

请注意,默认构造函数.new调用.bless实际分配对象,然后将其调用.BUILD来处理属性的初始化。

因此,最简单的方法是保持.new的默认实现,但提供自定义.BUILD。您可以将命名参数映射到其签名中的属性,因此BUILD例程的主体实际上可以保持空:

class Local::Class {
    has $.x;
    has $.y;
    has @.z;
    submethod BUILD (:$!x, :$!y, :@!z) { }
}
my %hash = x => 5, y => 20, z => [1, 2];
my $x = Local::Class.new(|%hash);
say $x;  # Local::Class.new(x => 5, y => 20, z => [1, 2])

将阵列中的数组包含器绑定到@参数会自动删除项目容器,因此它不会遭受上述"数组中的数组"问题的折磨。

缺点是您必须在该BUILD参数列表中列出类的所有公共属性。另外,您仍然必须在使用类的代码中使用|插入哈希。

要解决这两个限制,您可以这样实现自定义.new

class Local::Class {
    has $.x;
    has $.y;
    has @.z;
    method new (%attr) {
        self.bless: |Map.new: (.key => .value<> for %attr)
    }
}
my %hash = x => 5, y => 20, z => [1, 2];
my $x = Local::Class.new(%hash);
say $x;  # Local::Class.new(x => 5, y => 20, z => [1, 2])

相关内容

  • 没有找到相关文章

最新更新