Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Finding the contiguous sequences of equal elements in a list Raku

Tags:

sequence

raku

I'd like to find the contiguous sequences of equal elements (e.g. of length 2) in a list

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

This code looks ok but when one more 2 is added after the sequence of 2 2 2 or when one 2 is removed from it, it says Too few positionals passed; expected 2 arguments but got 1 How to fix it? Please note that I'm trying to find them without using for loop, i.e. I'm trying to find them using a functional code as much as possible.

Optional: In the bold printed section:

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

multiple sequences of 2 2 are seen. How to print them the number of times they are seen? Like:

((1 1) (2 2) (2 2) (4 4) (3 3))
like image 918
Lars Malmsteen Avatar asked Dec 10 '19 11:12

Lars Malmsteen


People also ask

Which is the longest subsequence of consecutive elements?

Input: arr [] = {1, 9, 3, 10, 4, 20, 2} Output: 4 Explanation: The subsequence 1, 3, 4, 2 is the longest subsequence of consecutive elements Input: arr [] = {36, 41, 56, 35, 44, 33, 34, 92, 43, 32, 42} Output: 5 Explanation: The subsequence 36, 35, 33, 34, 32 is the longest subsequence of consecutive elements.

Can We have infinite lists in raku?

These are called sequences, which are of type Seq. As it so happens, loops return Seq s. So, it is fine to have infinite lists in Raku, just so long as you never ask them for all their elements.

What is the first element of a list in raku?

The first element of a list is at index number zero: Variables in Raku whose names bear the @ sigil are expected to contain some sort of list-like object. Of course, other variables may also contain these objects, but @ -sigiled variables always do, and are expected to act the part.

What is an array in raku?

For most uses, Array s consist of a number of slots each containing a Scalar of the correct type. Every such Scalar, in turn, contains a value of that type. Raku will automatically type-check values and create Scalars to contain them when Arrays are initialized, assigned to, or constructed.


1 Answers

There are an even number of elements in your input:

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

Your grep block consumes two elements each time:

{$^a eq $^b}

So if you add or remove an element you'll get the error you're getting when the block is run on the single element left over at the end.


There are many ways to solve your problem.

But you also asked about the option of allowing for overlapping so, for example, you get two (2 2) sub-lists when the sequence 2 2 2 is encountered. And, in a similar vein, you presumably want to see two matches, not zero, with input like:

<1 2 2 3 3 4>

So I'll focus on solutions that deal with those issues too.

Despite the narrowing of solution space to deal with the extra issues, there are still many ways to express solutions functionally.


One way that just appends a bit more code to the end of yours:

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 .rotor method converts a list into a list of sub-lists, each of the same length. For example, say <1 2 3 4> .rotor: 2 displays ((1 2) (3 4)). If the length argument is a pair, then the key is the length and the value is an offset for starting the next pair. If the offset is negative you get sub-list overlap. Thus say <1 2 3 4> .rotor: 2 => -1 displays ((1 2) (2 3) (3 4)).

The .flat method "flattens" its invocant. For example, say ((1,2),(2,3),(3,4)) .flat displays (1 2 2 3 3 4).

A perhaps more readable way to write the above solution would be to omit the flat and use .[0] and .[1] to index into the sub-lists returned by rotor:

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

See also Elizabeth Mattijsen's comment for another variation that generalizes for any sub-list size.


If you needed a more general coding pattern you might write something like:

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

The .pairs method on a list returns a list of pairs, each pair corresponding to each of the elements in its invocant list. The .key of each pair is the index of the element in the invocant list; the .value is the value of the element.

.value xx 2 could have been written .value, .value. (See xx.)

@s - 1 is the number of elements in @s minus 1.

The [eq] in [eq] list is a reduction.


If you need text pattern matching to decide what constitutes contiguous equal elements you might convert the input list into a string, match against that using one of the match adverbs that generate a list of matches, then map from the resulting list of matches to your desired result. To match with overlaps (eg 2 2 2 results in ((2 2) (2 2)) use :ov:

say @s .Str .match( / (.) ' ' $0 /, :ov ) .map: { .[0].Str xx 2 }
like image 185
raiph Avatar answered Oct 21 '22 01:10

raiph