Perl 6 集合操作如何比较元素?

How do the Perl 6 set operations compare elements?

运行下哞哞(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)),我就没有这个问题。

你中途转错了。重要的部分是 nqp::existskey 的调用方式:k.WHICH。此方法适用于值类型,即不可变类型,其中值 - 而不是身份 - 定义两个事物是否应该是同一事物(即使创建两次)。它 returns 对象值的字符串表示形式,对于两个本应相等的事物来说是相等的。对于 <1>.WHICH 你得到 IntStr|1 而对于 1.WHICH 你只得到 Int|1.

Set documentation, sets compare object identity, same as the === 运算符中所述:

Within a Set, every element is guaranteed to be unique (in the sense that no two elements would compare positively with the === operator)

对象的标识由 .WHICH 方法定义,正如 timotimo 在他的回答中阐述的那样。

我认为这是一个错误,但不在集合中。其他答案对区分重要和不重要很有帮助。

我用的是angle-brackets form of the quote words。引用词形式应该等同于引用版本(即 eqv 下的 True)。这是文档示例:

<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 is not "4" as Str。我认为这种对数据类型的关注程度在 DWIMery 中表现不佳。这当然是我必须在课堂上非常仔细地解释的东西,并且仍然看着每个人都搞砸了。

就其价值而言,我认为 gist 的结果过于简单化往往会产生误导,有时 perl 的结果不够丰富(例如隐藏 Str 这迫使我 .WHAT)。我用得越多,就越没用。

但是,如果知道我在开始之前就搞砸了,就可以让我免于那些最终毫无意义的代码探索!

用逗号写下你的数字列表

正如您在回答中提到的,如果您将数字写成简单的逗号分隔列表而不是使用 <...> 结构,您的代码就可以工作。

原因如下:

4 ∈ set 1, 2, 3, 4 # True

左侧的 4 等代码中的纯数字文字构造具有数字类型的单个值。 (在这种情况下,类型是 Int,一个整数。)如果 set 构造函数在右侧收到相似文字的列表,那么一切正常。

<1 2 3 4> 生成 "dual values"

的列表

各种 <...> "quote words" 构造将尖括号内以空格分隔的文字元素列表转换为值的输出列表。

基础变体 (qw<...>) 只输出字符串。将它用于您的用例不起作用:

4 ∈ set qw<1 2 3 4> # False

左边的4构造一个单一的数值,类型Int。与此同时,set 构造函数接收到一个字符串列表,类型为 Str: ('1','2','3','4') 运算符在集合中找不到 Int,因为所有值都是 Str,所以 returns False.

继续前进,huffmanized <...> 变体输出 Strs 除非元素被识别为数字。如果一个元素被识别为一个数字,那么输出值就是一个“对偶值”。例如 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 都 return False.

因此, 运算符似乎没有达到引用的文档对双值互换性的要求

这可能已经被识别为 集合操作 and/or 其他操作中的错误。即使不是,<...> 列表构造函数的这种尖锐的“双值”边缘最终可能会被视为足够痛苦,以至于 Perl 6 需要改变。

只是为了添加到其他答案并指出集合与 object hashes 之间的一致性。

一个对象散列被声明为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