Perl 6设置操作如何比较元素?

时间:2016-11-26 04:32:52

标签: set perl6 raku moarvm

在呐喊下奔跑(2016.10)

考虑构造一个集合并测试成员资格的代码:

%PATH%

直接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; 不在集合中,但IntStr版本为:

4

我认为大多数人都没有想到这一点,但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 文档并没有说明这可能会如何起作用。如果我不使用引用词(即),我就不会遇到这个问题。

5 个答案:

答案 0 :(得分:7)

你在中间转了个弯。重要的部分是调用nqp::existskeyk.WHICH。这个方法适用于值类型,即不可变类型,其中值 - 而不是标识 - 定义两个事物是否应该是相同的东西(即使创建两次)。它返回一个对象值的字符串表示,该值对于应该相等的两个事物是相等的。对于<1>.WHICH,您获得IntStr|1,而1.WHICH只获得Int|1

答案 1 :(得分:4)

Set文档中所述,设置比较对象标识,与===运算符相同:

  

在Set中,每个元素都保证是唯一的(在某种意义上,没有两个元素会与===运算符进行正比较)

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

答案 2 :(得分:2)

使用逗号

编写您的号码列表

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

这就是原因:

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,因此返回False

除非元素被识别为数字,否则霍夫曼<...>变体输出Str s。如果元素被识别为数字,则输出值为&#34;双值&#34;。例如,1变为IntStr

根据文档&#34; IntStr可以互换使用,可以使用Str或Int&#34;。但是可以吗?

您的方案就是一个很好的例子。虽然1 ∈ set 1,2,3<1> ∈ set <1 2 3>都有效,1 ∈ set <1 2 3><1> ∈ set 1, 2, 3都会返回False

因此,运营商似乎并不辜负被引用的doc双重互换性声明

这可能已被识别为设置操作和/或其他操作中的错误。即使不是,这个尖锐的双重价值&#34; <...>列表构造函数的边缘最终可能被视为Perl 6需要更改的足够痛苦。

答案 3 :(得分:1)

我认为这是一个错误,但不是在设置中。其他答案非常有助于理清重要内容和非重要内容。

我使用了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失败的其他非错误方法。我仍然会失败,因为Int不是{4}而不是{4}}。我认为这种对数据类型的关注程度在它的DWIMery中是不正确的。这当然是我必须在课堂上非常仔细地解释的,并且仍然会看到每个人都在搞乱它。

对于它的价值,我认为gist的结果在过度简化中往往会产生误导,有时perl的结果不够丰富(例如隐藏Str强迫我.WHAT)。我使用的越多,我发现它们的用处就越少。

但是,知道我在开始之前就搞砸了,这会让我从那些最终没有任何意义的代码中拯救出来!

答案 4 :(得分:1)

只需添加其他答案,并在集合和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