Home > Raku > Convolution

## Convolution

Flavio wrote a straightforward solution to PWC-131-1 and wondered if there is a idiomatic way. Assuming, that “idiomatic” means to use language features which lesser languages refuse to require, I’m happy to deliver convoluted code.

``````use v6.d;

sub consecutive-a(*@a) {
my @ret;

gather {
for (|@a, |@a.tail).rotor( 2 => -1 ) -> [\$a, \$b] {
@ret.push: \$a;
unless \$b == \$a + 1 {
take @ret;
@ret = [];
}
}
}
}

sub consecutive-b(*@a) {
my @gaps = @a.rotor(2 => -1).kv.grep(-> \$index, [\$a, \$b] { \$b !== \$a + 1 })[*;0];
return @a unless @gaps;
@gaps = (@gaps Z @gaps »+» 1).flat;
my \$ranges := (0, @gaps, (@a - 1)).flat.map(-> \l, \r { l .. r });

@a[\$ranges]
}

sub MAIN() {
my @examples := (1, 2, 3, 6, 7, 8, 9)
,(11, 12, 14, 17, 18, 19)
,(2, 4, 6, 8)
,(1, 2, 3, 4, 5);

.&consecutive-a.say for @examples;
say();
.&consecutive-b.say for @examples;
}``````

Both exibits use `.rotor` to create easily comparable pairs of numbers. The first variant uses `gather`/`take` to return the by PWC requested sublists lazily. If we spot a gap `take` the list and empty the `Array`-container. If numbers are consecutive we add them to the return buffer. The laziness may help with very large lists.

The 2nd version creates a list of gaps. Since we can’t point in-between two elements, we first take the last index of the last element of a sub-list and then zip the following element, hoping the Array not to contain holes. The first and last index of `@a` are added. We now have a list of begin- and end-indices of the sub-lists. Those are composes to `Range`-objects. Such a list (Rakudo doesn’t like an `Array` in this case) can be used in `Positional`-subscripts to gain the desired sub-list of consecutive `Int`s. This solution can take a shortcut if no gaps are found.

I’m not entirely sure if this is better then a boring way to solve the task. It does provide a reason for another blog post, though. Quite helpful to reach 150 posts before the end of the year.

Categories: Raku