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))
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.
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.
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.
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.
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 }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With