Weekly Challenge 200
Tags: perl, raku
Published: 2023-01-16 21:15:44

I've been doing [The Weekly Challenge](https://theweeklychallenge.org/) for a few weeks now, I thought this one was tricky/fun.

[Challenge 200](https://theweeklychallenge.org/blog/perl-weekly-challenge-200/)

## Task One
An integer array is called _arithmetic_ if it has at least 3 elements and the differences between any three consecutive elements are the same. Write a script to find all Arithmetic Slices for the given array of integers.

## Task Two
A seven segment display is an electronic component, usually used to display digits. Write a program that accepts any natural number and draws it as an ASCII seven segment display.

[My solutions on github](https://github.com/manwar/perlweeklychallenge-club/tree/master/challenge-200/zapwai)
## Solution to Task One
Initially I was constructing the slices during one pass of the array, but decided to create a list of start/end points to make the logic easier.
As we traverse the list, if a difference is detected that's identical to the previous, we push the starting index to the begin array. When the difference between list elements becomes different again, the ending index is pushed to the end array.
```
use v5.30.0;
my @array = (1,2,3,4);
#my @array = (1,2,3,5,7,9,10,11,14);
#my @array = (1,2,3,5,7,9,10,11,14,5,32,33,40,43,46,49);

sub wrap {
    my @array = @_;
    "(" . join(",", @array).")"
}

# return all sets of size 3+
sub bundle {
    my $len = @_;
    my $str; 
    unless (@_ < 4) {
	do {
	    $len--;
	    for (0 .. @_ - $len) {
		my @list;
		for my $i ($_ .. $_ + $len - 1) {
		    push @list, $_[$i];
		}
		$str .= wrap(@list).", ";
	    }
	} while ($len > 3);
    }
    $str .= wrap(@_);    
    print $str;
}

say "Input: \@array = " . wrap(@array);
if ($#array < 2) {
    say "Output: No slice found."; exit;
}

# indices as we traverse list
my $begin = 0;
my $end = 0;			

my @begin;
my @end;

$begin[0] = 0 if ($array[2] - $array[1] == $array[1] - $array[0]);
for my $i (0 .. $#array - 2) {
    next if ($i < $end);
    $begin = $i;
    my $diff = $array[$i + 1] - $array[$i];
    for my $j ($i + 1 .. $#array - 1) {
	if ($array[$j + 1] - $array[$j] == $diff) {
	    $end = $j + 1;
	    push @begin, $begin unless ($begin[$#begin] == $begin);
	} else {
	    last if (($end == 0) or ($end[$#end] == $end));
	    push @end, $end;
	    last;
	}
    }
}
push @end, $end if (@begin != @end);
print "Output: ";
for my $i (0 .. $#begin - 1) {
    bundle @array[$begin[$i] ..  $end[$i]];
    print ", ";
}
bundle @array[$begin[$#begin] ..  $end[$#begin]];
say;
```
When I converted this solution to Raku, I had to address the numerical use of an uninitialized value. Perl did not mind `($end[$#end] == $end)` where the end array is empty, but in Raku I decided to initialize the arrays to avoid the "Use of Nil in numeric context" warning. Part of the Raku code:

```
my $begin = 0;
my $end = 0;			

my @begin = (-1);
my @end = (-1);

@begin[1] = 0 if (@array[2] - @array[1] == @array[1] - @array[0]);
loop (my $i = 0; $i < @array.elems - 2; $i++) {
    next if ($i < $end);
    $begin = $i;    
    my $diff = @array[$i + 1] - @array[$i];
    loop (my $j = $i + 1; $j < @array.elems - 1; $j++) {
	if (@array[$j + 1] - @array[$j] == $diff) {
	    $end = $j + 1;
	    push @begin, $begin unless (@begin.tail == $begin);
	} else {
	    last if (($end == 0) or (@end.tail == $end));
	    push @end, $end;
	    last;
	}
    }
}
push @end, $end if (@begin.elems != @end.elems);
print "Output: ";

shift @begin;
shift @end;
```

## Solution to Task Two
This was a fun one. Initially I wrote it with newlines so it only printed one digit. Then I modified it by adding a newline_flag to determine if we're on the final digit and require a newline character printed instead of a space.

```
use v5.30.0;
my @truth = qw( 1110111 0010010 1011101 1011011 0111010 1101011 0101111 1010010 1111111 1111010 );
my $N = $ARGV[0] || 200;

my $vert = 2;
my $horiz = 2*$vert + 1;
    
sub hor {
    my ($newline_flag, $i, @a) = @_;
    if ($a[$i]) {
	print " ";
	print "-" x $horiz;
	($newline_flag) ? { say " " } : { print " " };
    } else {
	($newline_flag) ? { say " " x $horiz } : { print " " x ($horiz + 2) }
    }
}

sub ver {
    my ($newline_flag, $i, @a) = @_;

    if ($a[$i]) {
	print "|";
    } else {
	print " ";
    }
    print " " x $horiz;
    if ($a[$i + 1]) {
	($newline_flag) ? { say "|" } : { print "|" };	
    } else {
	($newline_flag) ? { say " " } : { print " " };	
    }
}
    
sub d {
    my @num = @_;
    my $last = pop @num;
    foreach (@num) {
	my @a = split("", $truth[$_]);
	hor(0,0, @a);
    }
    my @b = split("", $truth[$last]);    
    hor(1,0, @b);

    my $N = $vert;
    do {
	$N--;    
	foreach (@num) {
	    my @a = split("", $truth[$_]);
	    ver(0,1, @a);
	}
	ver(1,1,@b);
    } while ($N > 0);
    
    foreach (@num) {
	my @a = split("", $truth[$_]);
	hor(0,3, @a);
    }
    hor(1,3, @b);

    $N = $vert;
    do {
	$N--;    
	foreach (@num) {
	    my @a = split("", $truth[$_]);	
	    ver(0,4, @a);
	}
	ver(1,4, @b);
    } while ($N > 0);

    foreach (@num) {
	my @a = split("", $truth[$_]);	
	hor(0,6, @a);	
    }
    hor(1,6, @b);
}

my @nums = split("",$N);
d(@nums);
```

Converting this to Raku, I had some issues with the ternary operator, and had some bugs with the split operator until I added the :skip-empty option. e.g. `my @a = split("", @truth[$_], :skip-empty);`