Raku listesindeki eşit elemanların bitişik dizilerini bulma


9

Bir listede eşit elemanların (örneğin, uzunluk 2) bitişik dizilerini bulmak istiyorum

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

Bu kod iyi görünüyor, ancak bir diziden sonra bir 2 daha eklendiğinde 2 2 2veya bir 2 kaldırıldığında, Too few positionals passed; expected 2 arguments but got 1nasıl düzeltilir? Ben fordöngü kullanmadan onları bulmaya çalışıyorum lütfen unutmayın , yani mümkün olduğunca fonksiyonel bir kod kullanarak onları bulmaya çalışıyorum.

İsteğe bağlı: Kalın yazılmış bölümde:

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

çoklu dizileri 2 2görülür. Onları kaç kez görüldükleri nasıl yazdırılır? Sevmek:

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

Yanıtlar:


9

Girişinizde çift sayıda öğe var:

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

Kişisel grepbloğu iki öğe her zaman tüketir:

{$^a eq $^b}

Dolayısıyla, bir öğe ekler veya kaldırırsanız, blok sonunda kalan tek bir öğe üzerinde çalıştırıldığında karşılaştığınız hatayı alırsınız.


Sorununuzu çözmenin birçok yolu vardır.

Ancak aynı zamanda üst üste binmeye izin verme seçeneğini de sordunuz; örneğin, (2 2)sekansla 2 2 2karşılaşıldığında iki alt liste alırsınız . Ve benzer bir şekilde, muhtemelen sıfır olmayan iki eşleşme görmek istiyorsunuz, giriş gibi:

<1 2 2 3 3 4>

Bu yüzden bu sorunlarla da ilgilenen çözümlere odaklanacağım.

Ekstra sorunlarla başa çıkmak için çözüm alanının daralmasına rağmen, çözümleri işlevsel olarak ifade etmenin hala birçok yolu vardır.


Sonuna biraz daha kod eklemenin bir yolu:

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

.rotorYöntem, alt-listeleri, aynı uzunlukta, her bir listesini gösteren bir listeyi dönüştürür. Örneğin, say <1 2 3 4> .rotor: 2görüntüler ((1 2) (3 4)). Uzunluk bağımsız değişkeni bir çiftse, anahtar uzunluktur ve değer sonraki çifti başlatmak için bir ofsettir. Ofset negatifse alt liste çakışması alırsınız. Böylece say <1 2 3 4> .rotor: 2 => -1görüntülenir ((1 2) (2 3) (3 4)).

.flatYöntem "derecede düzeltilir" onun invocant. Örneğin, say ((1,2),(2,3),(3,4)) .flatgörüntüler (1 2 2 3 3 4).

Yukarıdaki çözümü yazmanın belki de daha okunabilir bir yolu, tarafından döndürülen alt listelerin çıkarılması flatve kullanılması .[0]ve .[1]dizine eklenmesi olacaktır rotor:

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

Ayrıca herhangi bir alt liste boyutu için genelleştirilen başka bir varyasyon için Elizabeth Mattijsen'in yorumuna bakın.


Daha genel bir kodlama desenine ihtiyacınız varsa aşağıdaki gibi bir şey yazabilirsiniz:

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

.pairsBir listedeki bir yöntem çiftlerinin bir listesini, kendi invocant listesindeki elemanlarının her birine tekabül eden her bir çift döner. .keyHer bir çiftin invocant listedeki elementin endeksidir; .valueelemanın bir değerdir.

.value xx 2yazılabilirdi .value, .value. (Bkz xx.)

@s - 1@seksi 1'deki eleman sayısıdır .

[eq]İçinde [eq] listbir olduğunu azalma .


Bitişik eşit öğeleri neyin oluşturduğuna karar vermek için metin deseni eşleşmesine ihtiyacınız varsa, giriş listesini bir dizeye dönüştürebilirsiniz, eşleşme listesi oluşturan eşleşme zarflarından birini kullanarak eşleştirin, ardından sonuçtaki eşleşme listesinden istediğiniz eşlemeyle eşleştirin sonuç. Çakışanlarla eşleşmek için (örn 2 2 2. ((2 2) (2 2))Kullanımdaki sonuçlar :ov:

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

Oldukça iyi çalışıyor. Diziyi yapmak için 2 2 s eklediğimde , beklendiği gibi 2 2 2 23 (2 2)s yazdırır . Yöntemi hiç duymadım rotorBaşlangıçta yönteme geldim squishve bunun gibi özelliklere veya argümanlara sahip olup olmadığını kontrol ettim, @s.squish(:length 2, :multiple_instances yes)ancak bu tür özelliklere sahip değildi ve görev için uygun değildi. İle karşılaştırıldığında squish, rotor oldukça uygun görünüyor. Aslında bu tür bir işlem için en uygun olanı bile olabilir.
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)) Yalnızca $sizefarklı uzunluktaki diziler için ayarlamanız gerekir .
Elizabeth Mattijsen

Tekrar merhaba @LarsMalmsteen. Eklediğim iki alternatifin rotorcevabımı zayıflattığını veya güçlendirdiğini düşünüyorsanız lütfen LMK .
19'da raiph

rotorÇözümün rafine edilmiş versiyonu say @s.rotor(2=>-1).grep:{.[0]eq.[1]}kabul edilir, çünkü hem daha kısadır (boşlukların nasıl sayıldığına bağlı olarak 3 ila 5 karakter) ve hala iyi görünüyor. Yöntemsiz genelleştirilmiş sürümler rotorde hoş geldiniz çünkü xxve gibi bazı tuhaflıkların nasıl :ovkullanıldığını gösteriyorlar. Yani sorun çok iyi çözüldü :)
Lars Malmsteen

5

TIMTOWDI!

İşte gather/ kullanarak yinelemeli bir yaklaşım 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))

Cevap için teşekkür ederim. Bu, kendi başına oldukça iyi görünüyor. take ($last, $_)Bölüm kullanımına iyi bir örnektir gather and takeikilisi.
Lars Malmsteen
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.