我如何使用Set或Setty存储唯一的数组?

时间:2017-06-19 06:50:27

标签: perl6

我有兴趣使用Set来保存数组,如下所示:

my @v1 = 1, 2, 3;
my @v2 = 1, 2, 3;

my $set Set.new(@v1, @v2);

Set可以很好地识别出两个数组是相同的,就像~~运算符所观察到的那样,但Set使用===运算符来比较大多数对象。

# desired outcome: set([1 2 3])
# actual outcome: set([1 2 3], [1 2 3])

可以序列化数组,然后将其添加到Set,使用精彩的Set魔法,然后反序列化。这看起来很尴尬。

可以创建一个Setty类,使用~~作为比较器。这似乎是一个很好的学习,但也许是错误的学习。

还有一些惯用的方法吗?

3 个答案:

答案 0 :(得分:5)

两个数组不相同,因为它们不是值类型:即,它们可以在添加到Set之后更改:

my @a = 1,2,3;
my @b = 1,2,3;
my $s = Set.new(@a,@b);
dd $s;  # Set $s = set([1, 2, 3],[1, 2, 3])
@a.push(4);
dd $s;  # Set $s = set([1, 2, 3, 4],[1, 2, 3])

Perl 6中对象的标识由.WHICH方法确定。对于值类型,这将返回值的唯一表示形式。对于像Array这样的引用类型,它会根据类型及其对象ID返回唯一标识对象的内容。

目前,身份是使用ObjAt类实现的,但功能仍然有点不稳定。但是,只要.WHICH方法返回的内容唯一地描述了认为标识对象的内容,您就应该能够创建自己的.WHICH实现。例如:

role frozen {
    method WHICH() {
        ObjAt.new(self.join("\0")) # something not occurring in values
    }
}
my @a = 1,2,3;
my @b = 1,2,3;
my $s = Set.new(@a but frozen,@b but frozen);
dd $s;  # Set $s = set([1, 2, 3])

在创建@a之后,你不应该忘记@bSet因为那样你会违反你所承诺的合同{{1 }}

希望这有帮助!

答案 1 :(得分:2)

也许Set并不是您真正想要的,可能是classifycategorize

my @v1 = 1, 2, 3;
my @v2 = 1, 2, 3;

my %classified  := classify *.perl, @v1, @v2;
my %categorized := categorize *.perl, @v1, @v2;

在这种特殊情况下,它们都会导致:

my Any %{Any} = "[1, 2, 3]" => $[[1, 2, 3], [1, 2, 3]]

my Any %{Any}创建一个匿名哈希值,Any表示值,Any表示密钥。

两者之间的区别是classify始终将结果作为单个值,而categorize始终将结果作为值列表,并分发它们。

答案 2 :(得分:1)

序列化和反序列化可能不像您想象的那么笨拙,因为.perl方法会将它序列化为一些可以在以后简单地进行评估的东西。

假设您在阵列数组中收集数组,当您将AoA添加到新Set时,您可以通过地图将所有值传递给.perl。此时你有一组字符串。

之后您可以通过另一个地图将所有设置元素传递给EVAL,分配给变量并获取您的值。

my @v1 = 1, 2, 3;
my @v2 = 1, 2, 3;

my @vs = @v1, @v2;

my $set = set( @vs.map(*.perl) );

for $set.keys.map(&EVAL) -> @v {
    say @v.^name;         # Array
    say @v.elems;         # 3
    say @v.join(', ');    # 1, 2, 3
}

另外,考虑到有用的集合,有上面的糖来创建它们,而不是完整的Set.new语法,即:my $set = set( @things );

或者,您也可以使用my $set = @things.Set;

将列表强制转换为集合(前提是它不是无限的)