【问题标题】:Finding the contiguous sequences of equal elements in a list Raku在列表 Raku 中查找相等元素的连续序列
【发布时间】:2019-12-16 08:22:51
【问题描述】:

我想在一个列表中找到相等元素的连续序列(例如长度为 2)

my @s = <1 1 0 2 0 2 1 2 2 2 4 4 3 3>;
say grep {$^a eq $^b}, @s;

# ==> ((1 1) (2 2) (4 4) (3 3))

这段代码看起来不错,但是当在2 2 2 序列之后添加一个 2 或从中删除一个 2 时,它会显示Too few positionals passed; expected 2 arguments but got 1 如何要解决这个问题?请注意,我试图在不使用for 循环的情况下找到它们,即我试图尽可能使用功能代码来找到它们。

可选:在粗体部分:

&lt;1 1 0 2 0 2 1 2 2 2 4 4 3 3&gt;

可以看到多个2 2 序列。如何打印它们被看到的次数?喜欢:

((1 1) (2 2) (2 2) (4 4) (3 3))

【问题讨论】:

    标签: sequence raku


    【解决方案1】:

    您的输入中有偶数个元素:

    say elems <1 1 0 2 0 2 1 2 2 2 4 4 3 3>; # 14
    

    您的 grep 块每次消耗两个元素:

    {$^a eq $^b}
    

    所以如果你添加或删除一个元素,当块在最后剩下的单个元素上运行时,你会得到错误。


    有很多方法可以解决您的问题。

    但是您还询问了允许重叠的选项,例如,当遇到序列 2 2 2 时,您会得到两个 (2 2) 子列表。而且,以类似的方式,您可能希望看到两个匹配,而不是零,输入如下:

    <1 2 2 3 3 4>
    

    所以我也会专注于解决这些问题的解决方案。

    尽管解决额外问题的解决方案空间变窄了,但仍有很多方法可以从功能上表达解决方案。


    一种只是在你的末尾附加更多代码的方法:

    my @s = <1 1 0 2 0 2 1 2 2 2 4 4 3 3>;
    say grep {$^a eq $^b}, @s .rotor( 2 => -1 ) .flat
    

    .rotor method 将列表转换为子列表的列表,每个子列表的长度相同。例如,say &lt;1 2 3 4&gt; .rotor: 2 显示 ((1 2) (3 4))。如果长度参数是一对,那么键是长度,值是开始下一对的偏移量。如果偏移量为负,您将获得子列表重叠。因此say &lt;1 2 3 4&gt; .rotor: 2 =&gt; -1 显示((1 2) (2 3) (3 4))

    .flat method“扁平化”它的调用者。例如,say ((1,2),(2,3),(3,4)) .flat 显示 (1 2 2 3 3 4)

    编写上述解决方案的一种可能更具可读性的方法是省略flat,并使用.[0].[1] 来索引rotor 返回的子列表:

    say @s .rotor( 2 => -1 ) .grep: { .[0] eq .[1] }
    

    另请参阅 Elizabeth Mattijsen 的评论,了解可概括任何子列表大小的另一种变体。


    如果您需要更通用的编码模式,您可以编写如下内容:

    say @s .pairs .map: { .value xx 2 if .key < @s - 1 and [eq] @s[.key,.key+1] }
    

    .pairs method on a list 返回一个对列表,每对对应于其调用者列表中的每个元素。每对的.key 是调用者列表中元素的索引; .value 是元素的值。

    .value xx 2 可以写成.value, .value。 (见xx。)

    @s - 1@s 中的元素个数减1。

    [eq] list 中的[eq]reduction


    如果您需要文本模式匹配来确定连续相等元素的构成,您可以将输入列表转换为字符串,使用生成匹配列表的匹配副词之一进行匹配,然后从结果匹配列表中映射达到您想要的结果。为了匹配重叠(例如2 2 2 导致((2 2) (2 2)) 使用:ov

    say @s .Str .match( / (.) ' ' $0 /, :ov ) .map: { .[0].Str xx 2 }
    

    【讨论】:

    • 它工作得很好。当我添加 2 2s 来生成序列 2 2 2 2 时,它会按预期打印 3 (2 2)s。从未听说过rotor 方法我最初提出squish 方法并检查它是否具有@s.squish(:length 2, :multiple_instances yes) 之类的功能或参数,但它没有这些功能并且不适合该任务。与squish 相比,rotor 似乎还算合适。实际上,它甚至可能是最适合这种操作的。
    • my $size = 2; say &lt;1 1 0 2 0 2 1 2 2 2 4 4 3 3&gt;.rotor( $size =&gt; -$size + 1).grep: { [eq] $_ } # ((1 1) (2 2) (2 2) (4 4) (3 3)) 不同长度的序列只需要调整$size即可。
    • 你好@LarsMalmsteen。如果您认为我添加的 rotor 的两个替代方案削弱或加强了我的回答,请 LMK。
    • rotor 解决方案的改进版本,即say @s.rotor(2=&gt;-1).grep:{.[0]eq.[1]} 是受欢迎的,因为它更短(3 到 5 个字符,具体取决于空格的计算方式)并且看起来仍然不错。没有rotor 方法的通用版本也很受欢迎,因为它们展示了一些像xx:ov 这样的怪癖是如何使用的。所以问题很好解决了:)
    【解决方案2】:

    TIMTOWDI!

    这是一个使用gather/take的迭代方法。

    say gather for <1 1 0 2 0 2 1 2 2 2 4 4 3 3> { 
        state $last = ''; 
        take ($last, $_) if $last == $_; 
        $last = $_; 
    };
    
    # ((1 1) (2 2) (2 2) (4 4) (3 3))
    

    【讨论】:

    • 感谢您的回答。就其本身而言,这看起来相当不错。 take ($last, $_) 部分是使用 gather and take duo 的一个很好的例子。
    猜你喜欢
    • 2021-03-04
    • 2020-02-19
    • 1970-01-01
    • 2016-12-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-03
    • 2020-11-20
    相关资源
    最近更新 更多