Знаходження суміжних послідовностей рівних елементів у списку Раку


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

Цей код виглядає нормально, але коли після послідовності або коли один 2 видалено з нього, додається ще 2 , він говорить Як це виправити? Зверніть увагу, що я намагаюся їх знайти, не використовуючи цикл, тобто намагаюся знайти їх за допомогою функціонального коду, наскільки це можливо.2 2 2Too few positionals passed; expected 2 arguments but got 1for

Необов’язково: у напівжирному друкованому розділі:

<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зустрічається послідовність . І, аналогічно, ви, мабуть, хочете побачити два матчі, не нульові, із введенням типу:

<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Метод перетворює список в список подсписков, кожен з однією і тією ж довжини. Наприклад, say <1 2 3 4> .rotor: 2дисплеї ((1 2) (3 4)). Якщо аргумент довжини - пара, то ключовим є довжина, а значення - зміщення для запуску наступної пари. Якщо зміщення від’ємне, ви отримуєте перекриття під-списків. Таким чином say <1 2 3 4> .rotor: 2 => -1відображається ((1 2) (2 3) (3 4)).

У .flatметоді «» його більш плоский invocant. Наприклад, 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] }

Дивіться також коментар Елізабет Маттійсен щодо іншої зміни, яка узагальнює будь-який розмір підспису.


Якщо вам потрібен більш загальний шаблон кодування, ви можете написати щось на зразок:

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

.pairsМетод в списку повертає список пара, кожну пару , відповідну кожен з елементів в його invocant списку. .keyКожної пари є індексом елемента в списку invocant; the .value- значення елемента.

.value xx 2можна було написати .value, .value. (Див xx.)

@s - 1- кількість елементів у @sмінус 1.

[eq]У [eq] listце скорочення .


Якщо вам потрібна відповідність тексту тексту, щоб вирішити, що є суміжними рівними елементами, ви можете перетворити список введення в рядок, порівняйте з цим, використовуючи один із прислівників відповідності, який генерує список збігів, а потім зіставіть зі списку результатів відповідність до потрібного результат. Для узгодження з перекриттями (наприклад, 2 2 2результати ((2 2) (2 2))використання :ov:

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

Це працює чудово. Коли я додаю 2 2 с, щоб зробити послідовність, 2 2 2 2вона надрукує 3 (2 2)с, як очікувалося. Ніколи не чув про метод, який rotorя спочатку придумав, squishі перевірив, чи є у нього такі функції або аргументи, @s.squish(:length 2, :multiple_instances yes)але він не мав таких особливостей, і він не підходив до завдання. У порівнянні з squish, rotor здається, цілком підходить. Насправді це може бути навіть найкращим для цього типу операцій.
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різну довжину послідовностей.
Елізабет Маттійсен

Привіт знову @LarsMalmsteen. Будь ласка, ЛМК, якщо ви вважаєте, що дві альтернативи тому, rotorщо я додав, послабили чи посилили мою відповідь.
raiph

Вдосконалена версія rotorрішення, тобто say @s.rotor(2=>-1).grep:{.[0]eq.[1]}вітається, оскільки вона коротша (на 3 - 5 символів залежно від того, як підраховуються пробіли) і все ще виглядає пристойною. Узагальнені версії без rotorметоду також вітаються, оскільки вони показують, як деякі примхи люблять xxі :ovвикористовуються. Тож проблема дуже добре вирішена :)
Ларс

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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.