Операторы перегрузки для объектов, которые используются для создания параметризованных ролей - PullRequest
5 голосов
/ 26 февраля 2020

В C ++ вы можете создавать шаблонные классы, которые используют определенный оператор для шаблонных объектов, и класс, из которого создаются эти объекты, должен перегружать этот конкретный оператор, чтобы его объекты работали с шаблонным классом. Например, метод insertion для реализации BST может основываться на операторе <, поэтому любой объект, сохраняемый в 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

Мое не очень образованное предположение состоит в том, что оператор < для Point2D является лексическим, и поэтому BST не поднимает его. При перегрузке оператора в модуле рекомендуется экспортировать его, чтобы он был доступен пользователям, которые use или import модуль . Однако я не думаю, что это имеет смысл с BST, так как объекты определенного класса будут определять свои отношения по-разному. Кроме того, я даже не уверен, будет ли это работать с перехватами типов.

Ответы [ 2 ]

5 голосов
/ 27 февраля 2020

Оператор infix < предназначен для сравнения действительных чисел.

Вы хотите, чтобы он численно сравнивал значение .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 для общих c сравнений (cmp, 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.
(Конечно, эта проблема в этом случае не имеет особого смысла.)

5 голосов
/ 26 февраля 2020

Я не уверен на 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, чтобы назвать несколько.

...