在 Perl 中有tie
. Python 支持各种协议,因此对象可以像字典一样运行。Raku 有类似的东西吗?
即我可以定义一个行为像 a 的对象Hash
吗?那就是:我可以写成$myobject<key>
一个我可以自己指定的例程吗?
Perl 具有融入语言的哈希特性。因此,要扩展它以使对象表现得像 Hash,您需要告诉运行时做一些不同的事情。
Raku 的情况并非如此。Raku 中的 Hash 只是另一个对象。哈希索引操作只是另一个可以像重载其他运算符一样重载的运算符。
因此,您可以创建自己的对象,该对象具有与 Hash 相同的功能,或者甚至只是从它继承。
class Foo is Hash {
}
class Bar does Associative {
# delegate method calls to a Hash object
has %!hash handles Hash;
}
Run Code Online (Sandbox Code Playgroud)
这样做的原因does Associative
是您可以将其用作支持关联变量的类型。(Hash 已经做了 Associative,所以你也可以继承它。)
my %f is Foo;
my %b is Bar;
Run Code Online (Sandbox Code Playgroud)
要找出您可以编写哪些方法来实现 Hash 索引操作,您可以查看 Hash 实现的方法。由于我们知道自动调用的方法是大写的,我们只需要查看它们。
Hash.^methods.map(*.name).grep(/^<:Lu + [-]>+$/)
# (STORE BIND-KEY WHICH AT-KEY ASSIGN-KEY DELETE-KEY
# DUMP BUILDALL ASSIGN-KEY EXISTS-KEY AT-KEY STORE ACCEPTS BUILDALL)
Run Code Online (Sandbox Code Playgroud)
很明显,以 结尾的方法-KEY
就是我们想要编写的方法。(其他的大多只是对象工件。)
您目前不必编写任何它们来使您的对象类型关联。
如果您不编写特定方法,则该功能将不起作用。
class Point does Associative {
has Real ($.x, $.y);
multi method AT-KEY ( 'x' ){ $!x }
multi method AT-KEY ( 'y' ){ $!y }
multi method ASSIGN-KEY ( 'x', Real $new-value ){ $!x = $new-value }
multi method ASSIGN-KEY ( 'y', Real $new-value ){ $!y = $new-value }
multi method EXISTS-KEY ( 'x' --> True ){}
multi method EXISTS-KEY ( 'y' --> True ){}
multi method EXISTS-KEY ( Any --> False ){}
}
my %p is Point;
%p<x> = 1;
%p<y> = 2;
say %p.x; # 1
say %p.y; # 2
Run Code Online (Sandbox Code Playgroud)
请注意,上面有一些限制。
一次不能分配多个属性。
%p< x y > = 1,2;
Run Code Online (Sandbox Code Playgroud)
您不能在声明中分配值。
my %p is Point = 1,2;
my %p is Point = x => 1, y => 2;
Run Code Online (Sandbox Code Playgroud)
在多重赋值中,被调用的方法是AT-KEY
. 因此,要使其工作,必须将其标记为raw
或rw
class Point does Associative {
…
multi method AT-KEY ( 'x' ) is rw { $!x }
multi method AT-KEY ( 'y' ) is rw { $!y }
…
}
…
%p<x y> = 1,2;
Run Code Online (Sandbox Code Playgroud)
这会处理多重赋值,但仍将初始化留在声明中。
如果您将一个属性声明为is required
唯一的编写方式:
my %p := Point.new( x => 1, y => 2 );
Run Code Online (Sandbox Code Playgroud)
如果你没有这样做,你可以实现STORE
.
class Point does Associative {
…
method STORE ( \list ) {
($!x,$!y) = list.Hash<x y>
}
}
my %p is Point = x => 1, y => 2;
Run Code Online (Sandbox Code Playgroud)
这也使您以后也可以分配给它。
%p = x => 3, y => 4;
Run Code Online (Sandbox Code Playgroud)
这可能不是您想要的。
不过我们可以解决这个问题。只是让它有一个:INITIALIZE
争论。
class Point does Associative {
…
method STORE ( \list, :INITIALIZE($) is required ) {
($!x,$!y) = list.Hash<x y>
}
}
my %p is Point = x => 1, y => 2;
# %p = x => 3, y => 4; # ERROR
Run Code Online (Sandbox Code Playgroud)
在这种情况下,Point
我们可能希望能够使用包含两个元素的列表来声明它:
my %p is Point = 1,2;
Run Code Online (Sandbox Code Playgroud)
或按名称:
my %p is Point = x => 1, y => 2;
Run Code Online (Sandbox Code Playgroud)
为此,我们可以改变STORE
工作方式。我们将只查看列表中的第一个值并检查它是否是关联的。如果是,我们将假设所有参数也是关联的。否则,我们将假设它是一个包含两个值的列表,x
并且y
。
class Point does Associative {
…
method STORE ( \list, :INITIALIZE($) is required ) {
if list.head ~~ Associative {
($!x,$!y) = list.Hash<x y>
} else {
($!x,$!y) = list
}
}
}
my %a is Point = x => 1, y => 2;
my %b is Point = 1,2;
Run Code Online (Sandbox Code Playgroud)