关于slice:实现AT-POS以返回对象而不是事物列表

Implementing AT-POS to return an object instead of a list of things

我有以下课程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Names {
    has @!names;

    method add-name( $name ) { @!names.push($name) }

    multi method AT-POS( ::?CLASS:D: $index ) {
        my $new-names-obj = Names.new;

        for @!names[$index] -> $name {
            $new-names-obj.add-name($name);
        }

        return $new-names-obj;
    }

    method gist {
        @!names.join("\
")
    }
}

我希望能够切片Names对象和返回的值
应该是另一个Names对象,其元素与
原始的Names对象。例如:

1
2
3
4
5
6
7
8
my $original = Names.new;
$original.add-name($_) for <jonathan joseph jotaro josuke giorno>;
my $sliced-off = $original[0..2];

say $original.^name;   #=> Names
say $original;         #=> jonathan, joseph, jotaro, josuke, giorno
say $sliced-off.^name; #=> List
say $sliced-off;       #=> (jonathan joseph jotaro)

当传递单个参数时,它按预期且如此答案中所述工作,但范围不是这种情况,因为AT-POS最终被多次调用并将结果收集在列表中。因此,我想知道使用范围时是否可以返回单个对象$sliced-off,而不是结果列表。


AT-POS方法旨在让对象充当Positional对象。这不是您似乎想要的。您需要object[slice] DWIM。

实现此目标的最佳方法是为对象创建postcircumfic:<[ ]>(多个)候选对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class A {
    method slice(@_) {
        say @_;  # just to show the principle
    }
}
sub postcircumfix:<[ ]>($object, *@indices) {
   constant &slicer = &postcircumfix:<[ ]>;
   $object ~~ A
     ?? $object.slice(@indices)
     !! slicer($object, @indices)
}

A.new[1,2,4,5];  # [1 2 4 5]

my @a = ^10;     # check if foo[] still works
say @a[1,2,4,5]; # (1 2 4 5)

为确保保留@a[]的常见行为,我们在编译时(使用constant)保存系统的postcircumfix:[ ]>的值。然后在运行时,如果对象不是正确的类,请使用给定的参数调用postcircumfix:<[ ]>的原始版本。


基于Liz的指导:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Names {
    has @.names;                                      # Make public so [] can access.
    method new (*@names) { nextwith :@names }         # Positional .new constructor.
    submethod BUILD (:@!names) {}                     # Called by nextwith'd Mu new.
    multi sub postcircumfix:<[ ]>                     # Overload [] subscript.
      ( Names $n, $index, *@indices )                 # Why `$index, *@indices`?
      is default is export                            # And why `is default`?
      { Names.new: |$n.names[ |$index, |@indices ] }  # Why? See my comment
    method gist { @!names.join(', ') }                # below Liz's answer.
}
import Names;

my $original = Names.new: <jonathan joseph jotaro josuke giorno>;
my $sliced-off = $original[0..2];

say $original.^name;   #=> Names
say $original;         #=> jonathan, joseph, jotaro, josuke, giorno
say $sliced-off.^name; #=> Names
say $sliced-off;       #=> jonathan, joseph, jotaro

如果代码或说明不足,则为PLMK。