Menemukan urutan yang berdekatan dari elemen yang sama dalam daftar Raku

9

Saya ingin menemukan urutan yang berdekatan dari elemen yang sama (misalnya panjang 2) dalam daftar

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))

Kode ini terlihat ok tetapi ketika satu lagi 2 ditambahkan setelah urutan 2 2 2atau ketika satu 2 dihapus dari itu, ia mengatakan Too few positionals passed; expected 2 arguments but got 1Bagaimana cara memperbaikinya? Harap dicatat bahwa saya mencoba untuk menemukannya tanpa menggunakan forloop, yaitu saya mencoba untuk menemukan mereka menggunakan kode fungsional sebanyak mungkin.

Opsional: Di bagian cetak tebal:

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

beberapa urutan 2 2terlihat. Bagaimana cara mencetaknya berapa kali mereka terlihat? Suka:

((1 1) (2 2) (2 2) (4 4) (3 3))
Lars Malmsteen
sumber

Jawaban:

9

Ada sejumlah elemen dalam input Anda:

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

grepBlok Anda mengkonsumsi dua elemen setiap kali:

{$^a eq $^b}

Jadi, jika Anda menambah atau menghapus elemen Anda akan mendapatkan kesalahan yang Anda dapatkan ketika blok dijalankan pada elemen tunggal yang tersisa di akhir.


Ada banyak cara untuk menyelesaikan masalah Anda.

Tetapi Anda juga bertanya tentang opsi untuk memungkinkan tumpang tindih jadi, misalnya, Anda mendapatkan dua (2 2)sub-daftar saat urutan 2 2 2ditemui. Dan, dalam nada yang sama, Anda mungkin ingin melihat dua pertandingan, bukan nol, dengan input seperti:

<1 2 2 3 3 4>

Jadi saya akan fokus pada solusi yang menangani masalah itu juga.

Meskipun ruang solusi menyempit untuk mengatasi masalah tambahan, masih ada banyak cara untuk mengekspresikan solusi secara fungsional.


Salah satu cara yang menambahkan kode sedikit lebih banyak ke akhir Anda:

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

The .rotorMetode mengubah daftar ke daftar sub-daftar, masing-masing sama panjang. Misalnya, say <1 2 3 4> .rotor: 2menampilkan ((1 2) (3 4)). Jika argumen panjang adalah pasangan, maka kuncinya adalah panjang dan nilainya adalah offset untuk memulai pasangan berikutnya. Jika offset negatif, Anda mendapatkan tumpang tindih sub-daftar. Dengan demikian say <1 2 3 4> .rotor: 2 => -1menampilkan ((1 2) (2 3) (3 4)).

The .flatMetode "merata" invocant nya. Misalnya, say ((1,2),(2,3),(3,4)) .flatmenampilkan (1 2 2 3 3 4).

Cara yang mungkin lebih mudah dibaca untuk menulis solusi di atas adalah dengan menghilangkan flatdan menggunakan .[0]dan .[1]mengindeks ke dalam sub-daftar yang dikembalikan oleh rotor:

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

Lihat juga komentar Elizabeth Mattijsen untuk variasi lain yang digeneralisasi untuk ukuran sub-daftar apa pun.


Jika Anda membutuhkan pola pengkodean yang lebih umum, Anda mungkin menulis sesuatu seperti:

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

The .pairsmetode pada daftar mengembalikan daftar pasangan, masing-masing pasangan sesuai dengan masing-masing unsur dalam daftar invocant nya. Masing .key-masing pasangan adalah indeks elemen dalam daftar yang tidak diundang; itu .valueadalah nilai elemen.

.value xx 2bisa saja ditulis .value, .value. (Lihat xx.)

@s - 1adalah jumlah elemen dalam @sminus 1.

The [eq]dalam [eq] listadalah pengurangan .


Jika Anda memerlukan pencocokan pola teks untuk memutuskan apa yang merupakan elemen yang berdekatan yang berdekatan, Anda dapat mengubah daftar input menjadi string, cocokkan dengan yang menggunakan salah satu kata keterangan pertandingan yang menghasilkan daftar kecocokan, lalu petakan dari daftar kecocokan yang dihasilkan ke yang Anda inginkan hasil. Untuk mencocokkan dengan tumpang tindih (mis. 2 2 2Hasil ((2 2) (2 2))digunakan :ov:

say @s .Str .match( / (.) ' ' $0 /, :ov ) .map: { .[0].Str xx 2 }
raiph
sumber
Ini bekerja dengan sangat baik. Ketika saya menambahkan 2 2 s untuk membuat urutan 2 2 2 2itu mencetak 3 (2 2)s seperti yang diharapkan. Belum pernah mendengar tentang metode ini rotorSaya awalnya datang dengan squishmetode dan memeriksa apakah ia memiliki fitur atau argumen seperti @s.squish(:length 2, :multiple_instances yes)tetapi tidak memiliki fitur seperti itu dan itu tidak cocok untuk tugas itu. Dibandingkan dengan squish, rotor sepertinya cukup pas. Sebenarnya itu mungkin yang paling cocok untuk jenis operasi ini.
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)) Anda hanya perlu menyesuaikan $sizepanjang urutan yang berbeda.
Elizabeth Mattijsen
Hai lagi @LarsMalmsteen. Tolong LMK jika Anda berpikir dua alternatif rotoryang saya tambahkan telah melemahkan atau memperkuat jawaban saya.
raiph
Versi halus dari rotorsolusi yaitu say @s.rotor(2=>-1).grep:{.[0]eq.[1]}diterima karena keduanya lebih pendek (dengan 3 hingga 5 karakter tergantung pada bagaimana ruang dihitung) dan masih terlihat layak. Versi umum tanpa rotormetode dipersilakan juga karena mereka menunjukkan bagaimana beberapa kebiasaan seperti xxdan :ovdigunakan. Jadi masalahnya diselesaikan dengan sangat baik :)
Lars Malmsteen
5

TIMTOWDI!

Berikut ini adalah pendekatan berulang menggunakan 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))
Holli
sumber
Terima kasih atas jawabannya. Itu terlihat cukup bagus dengan sendirinya. Bagian take ($last, $_)ini merupakan contoh yang layak tentang penggunaan gather and takeduo.
Lars Malmsteen