Как в операциях множеств Perl 6 сравниваются элементы?
Бег под морем (2016.10)
Рассмотрим этот код, который создает набор и проверяет членство:
my $num_set = set( < 1 2 3 4 > );
say "set: ", $num_set.perl;
say "4 is in set: ", 4 ∈ $num_set;
say "IntStr 4 is in set: ", IntStr.new(4, "Four") ∈ $num_set;
say "IntStr(4,...) is 4: ", IntStr.new(4, "Four") == 4;
say "5 is in set: ", 5 ∈ $num_set;
Прямо 4
отсутствует в наборе, но версия IntStr:
set: set(IntStr.new(4, "4"),IntStr.new(1, "1"),IntStr.new(2, "2"),IntStr.new(3, "3"))
4 is in set: False
IntStr 4 is in set: True
IntStr(4,...) is 4: True
5 is in set: False
Я думаю, что большинство людей не собираются этого ожидать, но ∈
Документы ничего не говорят о том, как это может работать. У меня нет этой проблемы, если я не использую слова в кавычках (т.е. set( 1, 2, 3, 4)
).
5 ответов
Я думаю, что это ошибка, но не в наборе вещей. Другие ответы были очень полезны для выяснения того, что важно, а что нет.
Я использовал форму угловых скобок в кавычках. Форма слов в кавычках должна быть эквивалентна версии в кавычках (то есть True под eqv
). Вот пример документа:
<a b c> eqv ('a', 'b', 'c')
Но когда я пытаюсь сделать это со словом, состоящим из всех цифр, это не работает:
$ perl6
> < a b 137 > eqv ( 'a', 'b', '137' )
False
Но другие формы работают:
> qw/ a b 137 / eqv ( 'a', 'b', '137' )
True
> Q:w/ a b 137 / eqv ( 'a', 'b', '137' )
True
В кавычках в угловых скобках используется IntStr:
> my @n = < a b 137 >
[a b 137]
> @n.perl
["a", "b", IntStr.new(137, "137")]
Без кавычек слово цифр выходит как [Str]:
> ( 'a', 'b', '137' ).perl
("a", "b", "137")
> ( 'a', 'b', '137' )[*-1].perl
"137"
> ( 'a', 'b', '137' )[*-1].WHAT
(Str)
> my @n = ( 'a', 'b', '137' );
[a b 137]
> @n[*-1].WHAT
(Str)
Обычно вы видите такие ошибки, когда есть два пути к коду для получения конечного результата, а не общий код, который очень рано сходится к одному пути. Это то, что я искал бы, если бы хотел отследить это (но мне нужно поработать над книгой!)
Это подчеркивает, однако, что вы должны быть очень осторожны с сетами. Даже если эта ошибка была исправлена, есть и другие, не глючные способы, которые eqv
может потерпеть неудачу. Я бы все-таки потерпел неудачу, потому что 4 как Int не "4", как Str. Я думаю, что этот уровень внимания к типам данных в unperly в это DWIMery. Это, безусловно, то, что я должен был бы очень тщательно объяснить в классе и все еще наблюдать, как все облажаются.
Для чего это стоит, я думаю, что результаты gist
имеют тенденцию вводить в заблуждение в их упрощении, а иногда и результаты perl
не достаточно богаты (например, прячутся Str
что заставляет меня .WHAT
). Чем больше я их использую, тем менее полезными я их нахожу.
Но, зная, что я испортил еще до того, как начал, спас бы меня от написания кода, который в итоге ничего не значил!
Вы сделали неправильный поворот в середине. Важной частью является то, что nqp::existskey
вызывается с: k.WHICH
, Этот метод существует для типов значений, то есть неизменяемых типов, где значение, а не идентичность, определяет, должны ли две вещи быть одной и той же (даже если они созданы дважды). Он возвращает строковое представление значения объекта, равное двум вещам, которые должны быть равны. За <1>.WHICH
ты получаешь IntStr|1
и для 1.WHICH
вы получаете просто Int|1
,
Как объяснено в документации Set, устанавливает идентичность объекта сравнения, так же как ===
оператор:
Внутри набора каждый элемент гарантированно уникален (в том смысле, что никакие два элемента не будут положительно сравниваться с оператором ===)
Идентичность объекта определяется методом .WHICH, как поясняет Тимотимо в своем ответе.
Напишите список номеров, используя запятые
Как вы упоминаете в своем ответе, ваш код работает, если вы пишете свои числа в виде простого списка, разделенного запятыми, а не используете <...>
построить.
Вот почему:
4 ∈ set 1, 2, 3, 4 # True
Голый числовой литерал в коде вроде 4
слева от ∈
создает одно значение с числовым типом. (В этом случае тип Int, целое число.) Если set
конструктор получает список похожих литералов справа, тогда все работает отлично.
<1 2 3 4>
производит список "двойных значений"
Различные <...>
конструкции "quote words" превращают список разделенных пробелами буквенных элементов в угловых скобках в выходной список значений.
Основополагающий вариант (qw<...>
) ничего не выводит, кроме строк. Использование его в вашем случае не работает:
4 ∈ set qw<1 2 3 4> # False
4
слева строит одно числовое значение, типа Int
, Тем временем set
конструктор получает список строк, типа Str
: ('1','2','3','4')
, ∈
Оператор не находит Int
в наборе, потому что все значения Str
с так возвращается False
,
Двигаясь вперед, униженный <...>
вариантные выводы Str
s, если элемент не распознается как число. Если элемент распознается как число, то выходным значением является "двойное значение". Например, 1
становится IntStr.
Согласно документу "IntStr может использоваться взаимозаменяемо, где можно использовать Str или Int". Но может ли это?
Ваш сценарий является тому примером. В то время как 1 ∈ set 1,2,3
а также <1> ∈ set <1 2 3>
обе работают, 1 ∈ set <1 2 3>
а также <1> ∈ set 1, 2, 3
оба возвращаются False
,
Так что кажется ∈
Оператор не соответствует заявленному документу о взаимозаменяемости двойных значений.
Это уже может быть признано ошибкой в ∈
установить операцию и / или другие операции. Даже если нет, этот острый край "двойного значения" <...>
Конструктор списка может в конечном итоге рассматриваться как достаточно болезненный, что Perl 6 необходимо изменить.
Просто чтобы добавить к другим ответам и указать здесь постоянство между наборами и объектными хешами.
Хеш объекта объявлен как my %object-hash{Any}
, Это эффективно хэши на объектах .WHICH
метод, который похож на то, как наборы различают отдельные элементы.
Подстановка множества хешем объекта:
my %obj-hash{Any};
%obj-hash< 1 2 3 4 > = Any;
say "hash: ", %obj-hash.keys.perl;
say "4 is in hash: ", %obj-hash{4}:exists;
say "IntStr 4 is in hash: ", %obj-hash{ IntStr.new(4, "Four") }:exists;
say "IntStr(4,...) is 4: ", IntStr.new(4, "Four") == 4;
say "5 is in hash: ", %obj-hash{5}:exists;
дает результаты, аналогичные вашему первоначальному примеру:
hash: (IntStr.new(4, "4"), IntStr.new(1, "1"), IntStr.new(2, "2"), IntStr.new(3, "3")).Seq
4 is in hash: False
IntStr 4 is in hash: True
IntStr(4,...) is 4: True
5 is in hash: False