Perl Weekly Challenge 200: Arithmetic Slices and Seven Segment Display
These are some answers to the Week 200 of the Perl Weekly Challenge organized by Mohammad S. Anwar.
Spoiler Alert: This weekly challenge deadline is due in a couple of days from now (on January 22, 2023 at 23:59). This blog post offers some solutions to this challenge. Please don’t read on if you intend to complete the challenge on your own.
Task 1: Arithmetic Slices
You are given an array of integers.
Write a script to find out all Arithmetic Slices for the given array of integers.
An integer array is called arithmetic if it has at least 3 elements and the differences between any three consecutive elements are the same.
Example 1
Input: @array = (1,2,3,4)
Output: (1,2,3), (2,3,4), (1,2,3,4)
Example 2
Input: @array = (2)
Output: () as no slice found.
Arithmetic Slices in Raku
The find_slices
subroutine loops over the input array, computes the difference ($gap
) between any two consecutive integers and checks whether the same difference can be found between the next integers.
sub find_slices (@in) {
my @out;
return @out if @in.elems < 3;
for 0..@in.end - 2 -> $i {
my $gap = @in[$i+1] - @in[$i];
for $i+2..@in.end -> $j {
last if @in[$j] - @in[$j-1] != $gap;
push @out, @in[$i..$j];
}
}
return @out;
}
for <1 2 3 4>, <2 5>, <3 5 7 9>, <2 5 9> -> @test {
say (~@test).fmt("%-10s => "), find_slices @test;
}
This script displays the following output:
$ raku ./arithmetic-slices.raku
1 2 3 4 => [(1 2 3) (1 2 3 4) (2 3 4)]
2 5 => []
3 5 7 9 => [(3 5 7) (3 5 7 9) (5 7 9)]
2 5 9 => []
Arithmetic Slices in Perl
This a port to Perl of the Raku program above:
use strict;
use warnings;
use feature "say";
sub find_slices {
my @in = @_;
my @out;
# return [] if @in < 3;
for my $i (0..$#in - 2) {
my $gap = $in[$i+1] - $in[$i];
for my $j ($i+2..$#in) {
last if $in[$j] - $in[$j-1] != $gap;
push @out, [@in[$i..$j]];
}
}
return @out ? @out : [];
}
for my $test ([<1 2 3 4>], [<2 5>], [<3 4 5 6 8>],
[<3 5 7 9>], [<2 5 9>]) {
printf "%-10s => ", "@$test";
say map "(@$_) ", find_slices @$test;
}
This script displays the following output:
$ perl ./arithmetic-slices.pl
1 2 3 4 => (1 2 3) (1 2 3 4) (2 3 4)
2 5 => ()
3 4 5 6 8 => (3 4 5) (3 4 5 6) (4 5 6)
3 5 7 9 => (3 5 7) (3 5 7 9) (5 7 9)
2 5 9 => ()
Task 2: Seven Segment 200
A seven segment display is an electronic component, usually used to display digits. The segments are labeled ‘a’ through ‘g’ as shown:
The encoding of each digit can thus be represented compactly as a truth table:
my @truth = qw<abcdef bc abdeg abcdg bcfg acdfg a cdefg abc abcdefg abcfg>;
For example, $truth[1] = ‘bc’
. The digit 1 would have segments ‘b’ and ‘c’ enabled.
Write a program that accepts any decimal number and draws that number as a horizontal sequence of ASCII seven segment displays, similar to the following:
------- ------- -------
| | | | |
| | | | |
-------
| | | | |
| | | | |
------- ------- -------
To qualify as a seven segment display, each segment must be drawn (or not drawn) according to your @truth table.
The number “200” was of course chosen to celebrate our 200th week!
For the 200th week of the Perl Weekly Challenge, Ryan J Thompson, the author of this task, has decided to bring us decades backward, back in the 1970s when they started to be widely used (remember lieutenant Theo Kojak’s LED watch?). We’re actually driven back more than decades, since the first seven-segment display devices date as far back as 1903 (but obviously did not use LEDs).
The difficulty in this task is that, for ASCII art display, we need to slice the digits into horizontal lines. I’ve decided not to use the suggested truth table, as this is quite unpractical. Instead, I’m using a table (@nums
) dividing each digit into seven horizontal lines
Seven Segment Display in Raku
my %c; # ascii coding of digit's slices
%c<h> = "-" x 7; # Horizontal line
%c<l> = "| "; # Vertical bar, left
%c<r> = " |"; # Vertical bar, right
%c<2> = "| |"; # 2 vertical bars
%c<n> = " " x 7; # empty horizontal line
my @nums = # Digit horizontal slices
<h 2 2 n 2 2 h>, # 0
<n r r n r r n>, # 1
<h r r h l l h>, # 2
<h r r h r r h>, # 3
<n l l h 2 2 n>, # 4
<h l l h r r h>, # 5
<n l l h 2 2 h>, # 6
<h r r n r r n>, # 7
<h 2 2 h 2 2 h>, # 8
<h 2 2 h r r n>; # 9
sub display ($num) {
my @digits = $num.comb;
for 0..6 -> $l { # Lines 0 to 6 iof the display
say join " ", map {%c{@nums[$_][$l]}}, @digits;
}
}
for <200 2023 01234 56789> -> $test {
display $test;
}
This program displays the following output:
$ raku ./seven_segments.raku
------- ------- -------
| | | | |
| | | | |
-------
| | | | |
| | | | |
------- ------- -------
------- ------- ------- -------
| | | | |
| | | | |
------- ------- -------
| | | | |
| | | | |
------- ------- ------- -------
------- ------- -------
| | | | | | |
| | | | | | |
------- ------- -------
| | | | | |
| | | | | |
------- ------- ------- -------
| | | | | | |
| | | | | | |
------- ------- ------- -------
| | | | | | |
| | | | | | |
------- ------- -------
Note that digits 6 and 9 could have an additional horizontal bar and 7 an additional vertical bar:
@nums[6] = <h l l h 2 2 h>; # 6
@nums[7] = <h 2 2 n r r n>; # 7
@nums[9] = <h 2 2 h r r h>; # 9
This allegedly provides a more legible display (for some eyes, at least). Thus, the test line with the last five digits would be displayed like so:
------- ------- ------- ------- -------
| | | | | | | |
| | | | | | | |
------- ------- ------- -------
| | | | | | |
| | | | | | |
------- ------- ------- -------
Seven Segment Display in Perl
This a port to Perl of the Raku program above:
use strict;
use warnings;
use feature "say";
my %c; # ascii coding of digit's slices
$c{'h'} = "-" x 7; # Horizontal line
$c{'l'} = "| "; # Vertical bar, left
$c{'r'} = " |"; # Vertical bar, right
$c{'2'} = "| |"; # 2 vertical bars
$c{'n'} = " " x 7; # empty horizontal line
my @nums = ( # Digit hoirizontal slices
[<h 2 2 n 2 2 h>], # 0
[<n r r n r r n>], # 1
[<h r r h l l h>], # 2
[<h r r h r r h>], # 3
[<n 2 2 h r r n>], # 4
[<h l l h r r h>], # 5
[<n l l h 2 2 h>], # 6
[<h r r n r r n>], # 7
[<h 2 2 h 2 2 h>], # 8
[<h 2 2 h r r n>]); # 9
sub display{
my @digits = split //, shift;
for my $l (0..6) {
say join " ", map {$c{$nums[$_][$l]}} @digits;
}
}
for my $test (<200 2023 01234 56789>) {
display $test;
}
This program displays the following output:
$ perl ./seven_segments.pl
------- ------- -------
| | | | |
| | | | |
-------
| | | | |
| | | | |
------- ------- -------
------- ------- ------- -------
| | | | |
| | | | |
------- ------- -------
| | | | |
| | | | |
------- ------- ------- -------
------- ------- -------
| | | | | | |
| | | | | | |
------- ------- -------
| | | | | |
| | | | | |
------- ------- -------
------- ------- ------- -------
| | | | | | |
| | | | | | |
------- ------- ------- -------
| | | | | | |
| | | | | | |
------- ------- -------
We could use the changes that we did to the Raku program (definition of digits 6, 7, and 9) to presumably improve their legibility:
------- ------- ------- ------- -------
| | | | | | | |
| | | | | | | |
------- ------- ------- -------
| | | | | | |
| | | | | | |
------- ------- ------- -------
Wrapping up
The next week Perl Weekly Challenge will start soon. If you want to participate in this challenge, please check https://perlweeklychallenge.org/ and make sure you answer the challenge before 23:59 BST (British summer time) on January 29, 2023. And, please, also spread the word about the Perl Weekly Challenge if you can.
Your Perl solution to Task 1 doesn't output 3 7 11 as one of the solutions to 3 5 7 9 11. Does it fit your definition of a slice?
Hi Choroba, thanks for your comment. It appears that we don't understand the task in the same way. My understanding of the task is that the differences between any three *consecutive* elements of the original array of integers should be the same. 3 7 11 is indeed an arithmetic sequence, but these numbers are not consecutive items in the original 3 5 7 9 11 input sequence.