我如何从哈希构造 Perl6 中的对象?
How do I construct an object in Perl6 from a hash?
在 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)? 期望的那种哈希引用。
但是我怎样才能在 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
那么我如何获取在别处创建的散列,并将其转换为 class 的默认构造函数所需的那种命名参数?
使用默认构造函数
默认的 .new
构造函数将 命名的 参数映射到 public 属性。
在您的示例中,您将散列作为 positional 参数传递。您可以使用 |
语法将散列条目作为命名参数插入到参数列表中:
$x = Local::Class.new(|%hash);
但是,请注意,如果您的 class 具有类似 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])
使用自定义构造函数
如果您更愿意在 class 本身而不是在使用 class 的代码中处理此问题,您可以根据自己的喜好修改构造函数。
请注意,默认构造函数 .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])
将项目容器中的数组绑定到 @
参数会自动删除项目容器,因此它不会遇到上述 "array in an array" 问题。
缺点是您必须在 BUILD
参数列表中列出 class 的所有 public 属性。此外,您仍然必须在使用 class.
的代码中使用 |
插入哈希
要绕过这两个限制,您可以像这样实现自定义 .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])
在 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)? 期望的那种哈希引用。
但是我怎样才能在 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
那么我如何获取在别处创建的散列,并将其转换为 class 的默认构造函数所需的那种命名参数?
使用默认构造函数
默认的 .new
构造函数将 命名的 参数映射到 public 属性。
在您的示例中,您将散列作为 positional 参数传递。您可以使用 |
语法将散列条目作为命名参数插入到参数列表中:
$x = Local::Class.new(|%hash);
但是,请注意,如果您的 class 具有类似 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])
使用自定义构造函数
如果您更愿意在 class 本身而不是在使用 class 的代码中处理此问题,您可以根据自己的喜好修改构造函数。
请注意,默认构造函数 .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])
将项目容器中的数组绑定到 @
参数会自动删除项目容器,因此它不会遇到上述 "array in an array" 问题。
缺点是您必须在 BUILD
参数列表中列出 class 的所有 public 属性。此外,您仍然必须在使用 class.
|
插入哈希
要绕过这两个限制,您可以像这样实现自定义 .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])