如何通过 AT-POS 为不同的 类 实现 2d 下标?

How can I implement 2d subscripts via AT-POS for different classes?

这里是一个 MRE(显示了两次尝试,留下调试以提供帮助)尝试在具有系列列的 DataFrame 中使用 AT-POS 进行 2d 下标...

class Series does Positional {
    has Real @.data = [0.1,0.2,0.3];

    method AT-POS( $p ) { 
        @!data[$p]
    }   
}

class DataFrame does Positional {
    has Series @.series;

#`[ ATTEMPT #1
    method AT-POS( $p, $q? ) { 
        given $q {
            when Int {                  #say 'Int';
                @!series[$p][$q]
            }   
            when Whatever {             #say '*';
                @!series[$p].data 
            }   
            default {                   #say 'default';
                @!series[$p] 
            }   

        }   
    }   
#]
    # ATTEMPT #2
    method AT-POS(|c) is raw { #`[dd c;] @!series.AT-POS(|c) }
}

my $df = DataFrame.new( series => [Series.new xx 3] );

say $df[1].data;            #[0.1 0.2 0.3]
say $df[1][2];              #0.3
say $df[0,1];               #(Series.new(data => $[0.1, 0.2, 0.3]) Series.new(data => $[0.1, 0.2, 0.3]))
say $df[1;2];               #0.3
say $df[1;*];               #got (0.1) ... expected [0.1 0.2 0.3]
say $df[*;1];               #got (0.2) ... wanted [0.2 0.2 0.2]

我已经研究过 SO 并发现了三个相关问题 , and ... 我的代码中的尝试 #2 试图将 @lizmats Answer 应用于第三个问题。令人鼓舞的是,我的 MRE 中的两次尝试都具有相同的行为。但是我不能运动

我已经对 multi postcircumfix:<[ ]>( DataFrame:D $df, @slicer where Range|List ) is export {} 进行了一些试验,但这似乎使事情过于复杂。

==================

@jonathan 在@Lizmat 的原始答案基础上做出了很好的回答 - 谢谢!这是最终的工作代码:

class Series does Positional {
    has Real @.data = [0.1,0.2,0.3];

    method elems {
        @!data.elems
    }   

    method AT-POS( |p ) is raw {
        @!data.AT-POS( |p )
    }   
}

class DataFrame does Positional {
    has Series @.series;

    method elems { 
        @!series.elems
    }   

    method AT-POS( |p ) is raw { 
        @!series.AT-POS( |p )
    }   
}

my $df = DataFrame.new( series => Series.new xx 3 );

say $df[1].data;            #[0.1 0.2 0.3]
say $df[1][2];              #0.3
say $df[0,1];               #(Series.new(data => $[0.1, 0.2, 0.3]) Series.new(data => $[0.1, 0.2, 0.3]))
say $df[1;2];               #0.3
say $df[1;*];               #(0.1 0.2 0.3)
say $df[*;1];               #(0.2 0.2 0.2)

AT-POS 方法只传递整数数组索引。

处理切片(使用 *、范围、其他可迭代对象、zen 切片)的逻辑位于数组索引运算符中,它作为 multiple-dispatch 子例程 postcircumfix:<[ ]> 用于 single-dimension 索引和 postcircumfix:<[; ]> 用于 multi-dimension 索引。这个想法是,想要充当 array-alike 的 class 不需要担心 re-implementing 所有的切片行为,而且切片行为将在不同的 user-defined 类型。

为了切片工作,必须实现 elemsAT-POS。添加:

method elems() { @!data.elems }

Series和:

method elems() { @!series.elems }

DataFrame中给出了您要查找的结果。

如果真的想要不同的切片语义,或者比标准更有效的实现是可能的,还可以为索引运算符添加 multi 个候选对象(记得标记它们 is export ).

这个答案只是对@raiph 在评论中提出的观点的阐述:

You may be able to simplify the code by using handles

确实可以 - 如此之多以至于我认为值得在没有注释格式限制的代码块中展示它的样子。

使用 handles,您可以将两个 class 中的每一个从 9 non-whitespace 行简化为 3:

class Series does Positional {
    has Real @.data handles <elems AT-POS> = [0.1,0.2,0.3];
}

class DataFrame does Positional {
    has Series @.series handles <elems AT-POS>;
}

(或者你甚至可以将每个 class 放在一行中,如果你按照我想要的方式格式化它们。)

此代码生成的结果与问题代码中的 say 语句完全相同。