首页 > 解决方案 > 用于实例化参数化角色的对象的重载运算符

问题描述

在 C++ 中,您可以创建在模板化对象上使用特定运算符的模板化类,并且实例化这些对象的类必须重载该特定运算符才能使其对象与模板化类一起使用。例如,insertionBST 实现的方法可能依赖于<运算符,因此要存储在 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类,并且两个对象之间的小于关系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

我不太受过教育的猜测是运算符<forPoint2D是词法的,因此BST不会选择它。如果在一个模块中重载了一个操作符,建议将它导出,这样它就可以被用户use或者import模块使用。但是,我认为这没有多大意义,BST因为特定类的对象将以不同的方式定义它们的关系关系。此外,我什至不确定这是否适用于类型捕获。

标签: operator-overloadingrakuparametric-polymorphism

解决方案


中缀<运算符用于比较实数。

您希望它以数字方式比较 的值.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 }
}

cmp向 Raku 添加一个功能以进行通用比较 ( , before, after)可能是有意义的

目前,那些调用.Stringy这两个值并比较它们。

.Stringy您目前可以通过使用返回不执行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
(当然,在这种情况下,这可能没有多大意义。)


推荐阅读