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



这里有一个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,在这里、这里和这里发现了三个相关的问题。。。我的代码中的第2次尝试试图将@lizmats-Answer应用于第三次。令人鼓舞的是,我的MRE中的两次尝试都有相同的行为。但我不能锻炼

  • 为什么未输入when Whatever {}选项(尝试#1(
  • |c正在做什么——尽管我可以看到它在单下标的情况下工作(尝试#2(

我已经用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方法只传递整数数组索引。

处理切片(具有*、range、其他可迭代项、zen切片(的逻辑位于数组索引运算符中,该运算符被实现为用于一维索引的多调度子程序postcircumfix:<[ ]>和用于多维索引的多分派子程序postcircumfix:<[; ]>。其思想是,一个想要充当类似数组的类不必担心重新实现所有的切片行为,而且切片行为将在不同的用户定义类型上表现一致。

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

method elems() { @!data.elems }

Series和:

method elems() { @!series.elems }

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

如果真的想要不同的切片语义,或者可能有比标准语义更高效的实现,那么还可以为索引运算符添加multi候选(记住将它们标记为is export(。

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

您可以通过使用句柄来简化代码

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

使用handles,您可以将这两个类中的每一个从9个非空白行简化为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>;
}

(或者你甚至可以把每个类都放在一行上,如果你按照我想的方式格式化的话。(

该代码对问题中的代码中的say语句产生了所有相同的结果。

最新更新