リストから等しい要素の連続したシーケンスを見つける


9

リスト内で等しい要素(例:長さ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))

このコードは問題ないように見えますが、シーケンスの後に1つ以上の2が追加される2 2 2か、1つ2つが削除されると、Too few positionals passed; expected 2 arguments but got 1「修正方法は?」forループを使用せずにそれらを検索しようとしていることに注意してください。つまり、可能な限り関数コードを使用してそれらを検索しようとしていることに注意してください。

オプション:太字の印刷セクション:

<1 1 0 2 0 2 1 2 2 2 4 4 3 3>

複数のシーケンス2 2が見られます。それらが見られた回数を印刷する方法は?お気に入り:

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

回答:


9

入力に含まれる要素の数は偶数です:

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が検出されたときに2つのサブリストが表示されます。そして、同様に、おそらく次のような入力で、ゼロではなく2つの一致を表示する必要があります。

<1 2 2 3 3 4>

したがって、これらの問題にも対処するソリューションに焦点を当てます。

余分な問題に対処するためのソリューションスペースの狭小化にもかかわらず、ソリューションを機能的に表現する方法はまだたくさんあります。


あなたの最後にもう少しコードを追加する1つの方法:

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メソッドは、リストを、同じ長さのサブリストのリストに変換します。たとえば、をsay <1 2 3 4> .rotor: 2表示します((1 2) (3 4))。length引数がペアの場合、キーは長さで、値は次のペアを開始するためのオフセットです。オフセットが負の場合、サブリストが重複します。したがって、がsay <1 2 3 4> .rotor: 2 => -1表示されます((1 2) (2 3) (3 4))

この.flatメソッドは、その呼び出し元を「平坦化」します。たとえば、を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リストメソッドはペアのリストを返します。各ペアは、その呼び出し元リストの各要素に対応しています。.key各対のインボカントリストにおける要素のインデックスです。これ.valueは要素の値です。

.value xx 2書かれている可能性があります.value, .value。(を参照してくださいxx。)

@s - 1@sマイナス1 の要素数です。

[eq]中には[eq] listあるの減少


連続する等しい要素を構成するものを決定するためにテキストパターンマッチングが必要な場合は、入力リストを文字列に変換し、一致のリストを生成する一致副詞の1つを使用して一致させ、結果の一致リストから目的のリストにマッピングします。結果。オーバーラップと照合するには(たとえば2 2 2((2 2) (2 2))使用中の結果:ov

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

それはかなりうまくいきます。シーケンスを作成するために2 2秒を追加すると、期待どおり2 2 2 23 (2 2)秒が出力されます。rotor最初にメソッドを思いついて、そのsquishような機能や引数@s.squish(:length 2, :multiple_instances yes)があるかどうかを確認したが、そのような機能がなく、タスクに適さなかったという方法を聞いたことがない。比較するとsquishrotor かなりフィットらしいです。実際には、このタイプの操作に最も適したものになる可能性もあります。
Lars Malmsteen

3
my $size = 2; say <1 1 0 2 0 2 1 2 2 2 4 4 3 3>.rotor( $size => -$size + 1).grep: { [eq] $_ }#((1 1)(2 2)(2 2)(4 4)(3 3))$sizeシーケンスの長さを変えるだけで調整できます。
Elizabeth Mattijsen

こんにちは@LarsMalmsteen。rotor私が追加した2つの代替案が私の答えを弱めたり強めたりしたと思われる場合は、LMKに連絡してください。
ライフ

rotorソリューションの洗練されたバージョン(つまりsay @s.rotor(2=>-1).grep:{.[0]eq.[1]}、スペースのカウント方法に応じて3〜5文字分)が短く、見栄えも良いため、歓迎されます。rotorメソッドのない一般化バージョンも、xxやのような癖がどのように:ov使用されるかを示しているため、歓迎されます。したがって、問題は非常によく解決されています:)
Lars Malmsteen

5

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デュオ。
Lars Malmsteen
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.