Поиск хэша объекта с помощью `eqv`

Есть ли способ использовать eqv для поиска хеш-значения без перебора пар ключ-значение при использовании объектных ключей?

Можно использовать объектные ключи в хеше, указав тип ключа при объявлении:

class Foo { has $.bar };
my Foo $a .= new(:bar(1));
my %h{Foo} = $a => 'A', Foo.new(:bar(2)) => 'B';

Однако при поиске по ключу используется оператор идентификации ===, который возвращает значение только в том случае, если это тот же объект, а не эквивалентный:

my Foo $a-prime .= new(:bar(1));
say $a eqv $a-prime;   # True
say $a === $a-prime;   # False
say %h{$a};            # A
say %h{$a-prime};      # (Any)

person J Hall    schedule 03.03.2016    source источник
comment
Насколько серьезна ваша заявка? У меня есть ответ, который неприемлем для любой производственной среды, но если вы просто изучаете возможности для развлечения, это может быть интересно.   -  person Marty    schedule 04.03.2016


Ответы (2)


Глядя на документацию для "===", последняя строка показывает, что оператор основан на .WHICH и что "... все типы значений должны переопределять метод WHICH." Вот почему, если вы создаете два отдельных элемента с одинаковым строковым значением , "===" возвращает Истина.

my $a = "Hello World";
my $b = join " ", "Hello", "World";

say $a === $b;     # True even though different items - because same value
say $a.WHICH ;     # "Str|Hello World"
say $b.WHICH ;     # (same as above) which is why "===" returns True

Таким образом, вместо того, чтобы создавать свой собственный тип контейнера или использовать некоторые из ловушек для индексов, вы вместо этого можно было бы скопировать способ, которым это делают «типы значений», то есть разделить (кое-что) идею идентичности. Метод .WHICH для строк, показанных выше, просто возвращает имя типа и содержимое, соединенные с '|'. Почему бы не сделать то же самое;

class Foo {
    has $.bar;
    multi method WHICH(Foo:D:) { "Foo|" ~ $!bar.Str }
}

my Foo $a .= new(:bar(1));
my %h{Foo} = $a => 'A', Foo.new(:bar(2)) => 'B';

my Foo $a-prime .= new(:bar(1));
say $a eqv $a-prime;   # True
say $a === $a-prime;   # True
say %h{$a};            # A
say %h{$a-prime};      # A

Конечно, есть небольшие затраты - концепция идентичности для экземпляров этого класса, ну, скажем так, интересная. Какие последствия? Единственное, что сразу приходит в голову, это то, что если вы планируете использовать какой-то фреймворк для сохранения объектов, он будет сжимать разные экземпляры, которые теперь выглядят одинаково, в один (возможно).

Различные объекты с одинаковыми данными атрибутов будут неразличимы - вот почему я колебался, прежде чем опубликовать этот ответ. OTOH, это ваш класс, ваше приложение, поэтому, в зависимости от размера / важности и т. Д., Это может быть прекрасный способ сделать это.

person Marty    schedule 05.03.2016
comment
Я надеялся на черту, которую можно было бы использовать при определении. К сожалению, для меня src / core / Hash.pm преимущественно npq. Src действительно дает пример того, как это можно сделать с ролью TypeHash. Возможно, вместо WHICH можно было бы использовать тип утки для объектов, реализующих метод HASH-CODE. - person J Hall; 19.03.2016
comment
Я считаю, что собственные объекты выполняются в nqp, поскольку perl6 в этот момент не существует (если вы понимаете, о чем я). Как пользователи perl6, мы можем создавать метаобъекты в perl6 (то есть забыть о nqp, если хотите). Однако, как бы вы это ни делали, вы собираетесь сопоставлять разные объекты ($ a vs $ a-prime) с одним и тем же ключом, поэтому явное изменение идентификатора, чтобы прояснить это, может быть хорошей идеей. Возможно, реализуйте keyWHICH или myVALUE или что-то в этом роде и сделайте собственный хеш, используя его для различения ключей. Тем не менее, предложенный мною метод неплох для одной лишней строчки (если я сам так говорю) :-) - person Marty; 19.03.2016

Если вам не нравится встроенный postcircumfix, предоставьте свой собственный.

class Foo { has $.bar };
my Foo $a .= new(:bar(1));
my %h{Foo} = $a => 'A', Foo.new(:bar(2)) => 'B';


multi sub postcircumfix:<{ }>(\SELF, WhateverCode $c) is raw {
    gather for SELF.keys -> $k {
        take $k if $c($k)
    }
}

dd %h;
dd %h{* eqv $a};

ВЫВОД

Hash[Any,Foo] %h = (my Any %{Foo} = (Foo.new(bar => 1)) => "A", (Foo.new(bar => 2)) => "B")
(Foo.new(bar => 1),).Seq
person Community    schedule 05.03.2016