用于实例化参数化角色的对象的重载运算符
Overloading operators for objects that are used to instantiate parameterized roles
在 C++ 中,您可以创建模板化的 classes,它们在
模板化对象和实例化这些对象的 class
必须重载该特定运算符才能使其对象与
模板化 class。例如,BST 实现的 insertion
方法
可能依赖于 <
运算符,因此任何对象都存储在 BST 中
必须实施该运算符。
如果可能的话,我怎样才能对 Raku 中的参数化角色做同样的事情?
为了提供一些上下文,以定义为自己模块的以下参数化角色为例:
role BST[::T] {
my role BinaryNode[::T] {
has T $.item is rw;
has BinaryNode $.left is rw;
has BinaryNode $.right is rw;
}
has BinaryNode $!root;
method insert( BST:D: T $x --> Nil ) {
self!rec-insert($x, $!root)
}
method !rec-insert( T $x, BinaryNode $node is rw --> Nil ) {
if !$node.defined { $node = BinaryNode[$(T)].new(item => $x) }
elsif $x < $node.item { self!rec-insert($x, $node.left) }
elsif $node.item < $x { self!rec-insert($x, $node.right) }
else { } # Duplicate; do nothing
}
}
然后,它可以用来存储整数:
use BST;
my $bst = BST[Int].new;
$bst.insert($_) for 6, 3, 2, 1, 4;
但是,我尝试了一些用户定义的类型,但无法使其工作。
假设我们定义了一个 Point2D
class 和之间的小于关系
两个 Point2D
个对象由它们到中心的距离定义
(例如,Point2D.new(:3x, :4x)
小于 Point2D.new(:6x, :8y)
):
use BST;
class Point2D {
has $.x;
has $.y;
multi method distance {
(($!x - 0) ** 2 ($!y - 0) ** 2).sqrt
}
}
multi infix:«<»( Point2D:D $lhs, Point2D:D $rhs --> Bool ) {
return $lhs.distance < $rhs.distance
}
my $bst = BST[Point2D].new;
$bst.insert(Point2D.new(:1x, :4y));
$bst.insert(Point2D.new(:3x, :4y));
=begin comment
Cannot resolve caller Real(Point:D: ); none of these signatures match:
(Mu:U \v: *%_)
in method rec-insert ...
in method insert ...
=end comment
我没有受过良好教育的猜测是 Point2D
的运算符 <
是词法运算符,因此 BST
没有接受它。 。但是,我认为 BST
没有多大意义,因为特定 class 的对象将以不同方式定义它们的关系。此外,我什至不确定这是否适用于类型捕获。
我不是 100% 确定这是一个长期解决方案,但为什么不制作自定义类型 Cool
?
class Point2D is Cool {
has $.x;
has $.y;
multi method distance {
(($!x - 0) ** 2 + ($!y - 0) ** 2).sqrt
}
method Numeric() { self.distance }
method Int() { self.Numeric.Int }
method Str() { "$!x,$!y" }
}
然后,如果您提供适当的方法,例如 Numeric
、Int
和 Str
,您就可以免费获得所有常规比较好东西。
中缀<
运算符用于比较实数。
您希望它在数值上比较 .distance
的值。
如果您尝试将对象用作实数,它会自动强制转换为距离,这也许是有道理的。
class Point2D {
has $.x;
has $.y;
method distance {
(($!x - 0) ** 2 + ($!y - 0) ** 2).sqrt
}
method Real { self.distance } # <-----
}
然后内置的 <
会自动执行正确的操作。
我个人会添加一些类型和其他注释。
这也使得 ($!x - 0)
及其等价物 (+$!x)
毫无意义。
class Point2D {
has Real ( $.x, $.y ) is required;
method distance (--> Real) {
sqrt( $!x² + $!y² );
}
method Real (--> Real) { self.distance }
}
向 Raku 添加一个功能以进行通用比较可能有意义(cmp
、before
、after
)
目前那些调用 .Stringy
两个值并比较它们。
您目前可以滥用此方法,方法是使用 .Stringy
方法,该方法 returns 不执行 Stringy 角色的方法。
也许它可以像这样工作:
role Comparable {
method COMPARE () {…}
}
class Point does Comparable {
has Real ( $.x, $.y ) is required;
method distance (--> Real) {
sqrt( $!x² + $!y² );
}
method COMPARE () {
($.distance, $!x, $!y)
}
}
multi sub infix:<cmp> ( Comparable \left, Comparable \right ) {
nextsame unless left.WHAT =:= right.WHAT;
return Same if left =:= right;
return Same if left eqv right;
left.COMPARE() cmp right.COMPARE()
}
以上将通过 .distance
然后 .x
然后 .y
.
进行比较
(当然,在这种情况下,这可能没有多大意义。)
在 C++ 中,您可以创建模板化的 classes,它们在
模板化对象和实例化这些对象的 class
必须重载该特定运算符才能使其对象与
模板化 class。例如,BST 实现的 insertion
方法
可能依赖于 <
运算符,因此任何对象都存储在 BST 中
必须实施该运算符。
如果可能的话,我怎样才能对 Raku 中的参数化角色做同样的事情?
为了提供一些上下文,以定义为自己模块的以下参数化角色为例:
role BST[::T] {
my role BinaryNode[::T] {
has T $.item is rw;
has BinaryNode $.left is rw;
has BinaryNode $.right is rw;
}
has BinaryNode $!root;
method insert( BST:D: T $x --> Nil ) {
self!rec-insert($x, $!root)
}
method !rec-insert( T $x, BinaryNode $node is rw --> Nil ) {
if !$node.defined { $node = BinaryNode[$(T)].new(item => $x) }
elsif $x < $node.item { self!rec-insert($x, $node.left) }
elsif $node.item < $x { self!rec-insert($x, $node.right) }
else { } # Duplicate; do nothing
}
}
然后,它可以用来存储整数:
use BST;
my $bst = BST[Int].new;
$bst.insert($_) for 6, 3, 2, 1, 4;
但是,我尝试了一些用户定义的类型,但无法使其工作。
假设我们定义了一个 Point2D
class 和之间的小于关系
两个 Point2D
个对象由它们到中心的距离定义
(例如,Point2D.new(:3x, :4x)
小于 Point2D.new(:6x, :8y)
):
use BST;
class Point2D {
has $.x;
has $.y;
multi method distance {
(($!x - 0) ** 2 ($!y - 0) ** 2).sqrt
}
}
multi infix:«<»( Point2D:D $lhs, Point2D:D $rhs --> Bool ) {
return $lhs.distance < $rhs.distance
}
my $bst = BST[Point2D].new;
$bst.insert(Point2D.new(:1x, :4y));
$bst.insert(Point2D.new(:3x, :4y));
=begin comment
Cannot resolve caller Real(Point:D: ); none of these signatures match:
(Mu:U \v: *%_)
in method rec-insert ...
in method insert ...
=end comment
我没有受过良好教育的猜测是 Point2D
的运算符 <
是词法运算符,因此 BST
没有接受它。 BST
没有多大意义,因为特定 class 的对象将以不同方式定义它们的关系。此外,我什至不确定这是否适用于类型捕获。
我不是 100% 确定这是一个长期解决方案,但为什么不制作自定义类型 Cool
?
class Point2D is Cool {
has $.x;
has $.y;
multi method distance {
(($!x - 0) ** 2 + ($!y - 0) ** 2).sqrt
}
method Numeric() { self.distance }
method Int() { self.Numeric.Int }
method Str() { "$!x,$!y" }
}
然后,如果您提供适当的方法,例如 Numeric
、Int
和 Str
,您就可以免费获得所有常规比较好东西。
中缀<
运算符用于比较实数。
您希望它在数值上比较 .distance
的值。
如果您尝试将对象用作实数,它会自动强制转换为距离,这也许是有道理的。
class Point2D {
has $.x;
has $.y;
method distance {
(($!x - 0) ** 2 + ($!y - 0) ** 2).sqrt
}
method Real { self.distance } # <-----
}
然后内置的 <
会自动执行正确的操作。
我个人会添加一些类型和其他注释。
这也使得 ($!x - 0)
及其等价物 (+$!x)
毫无意义。
class Point2D {
has Real ( $.x, $.y ) is required;
method distance (--> Real) {
sqrt( $!x² + $!y² );
}
method Real (--> Real) { self.distance }
}
向 Raku 添加一个功能以进行通用比较可能有意义(cmp
、before
、after
)
目前那些调用 .Stringy
两个值并比较它们。
您目前可以滥用此方法,方法是使用 .Stringy
方法,该方法 returns 不执行 Stringy 角色的方法。
也许它可以像这样工作:
role Comparable {
method COMPARE () {…}
}
class Point does Comparable {
has Real ( $.x, $.y ) is required;
method distance (--> Real) {
sqrt( $!x² + $!y² );
}
method COMPARE () {
($.distance, $!x, $!y)
}
}
multi sub infix:<cmp> ( Comparable \left, Comparable \right ) {
nextsame unless left.WHAT =:= right.WHAT;
return Same if left =:= right;
return Same if left eqv right;
left.COMPARE() cmp right.COMPARE()
}
以上将通过 .distance
然后 .x
然后 .y
.
进行比较
(当然,在这种情况下,这可能没有多大意义。)