Perl Weekly Challenge: Week 373
Challenge 1:
Equal List
You are given two arrays of strings.
Write a script to return true if the two given array represent the same strings otherwise false.
Example 1
Input: @arr1 = ("a", "bc")
@arr2 = ("ab", "c")
Output: true
Array 1: "a" + "bc" = "abc"
Array 2: "ab" + "c" = "abc"
Example 2
Input: @arr1 = ("a", "b", "c")
@arr2 = ("a", "bc")
Output: true
Array 1: "a" + "b" + "c" = "abc"
Array 2: "a" + "bc" = "abc"
Example 3
Input: @arr1 = ("a", "bc")
@arr2 = ("a", "c", "b")
Output: false
Array 1: "a" + "bc" = "abc"
Array 2: "a" + "c" + "b" = "acb"
Example 4
Input: @arr1 = ("ab", "c", "")
@arr2 = ("", "a", "bc")
Output: true
Array 1: "ab" + "c" + "" = "abc"
Array 2: "" + "a" + "bc" = "abc"
Example 5
Input: @arr1 = ("p", "e", "r", "l")
@arr2 = ("perl")
Output: true
Array 1: "p" + "e" + "r" + "l" = "perl"
Array 2: "perl"
This was obviously going to be a one-liner. The only question was how would I get the input into the script as it consists of two arrays and there is no way to signify the end of an array. I chose to represent them as two command-line string arguments with the elements of each array separated within the string by whitespace.
So first we reconstitute the arrays (called @a and @b for brevity) with .words().
Then we join them back up into strings with .join(), compare them with the
eq oporator and print the result with say(). If they are equal, True will be
output, otherwise False.
my @a = @*ARGS[0].words; my @b = @*ARGS[1].words; say @a.join eq @b.join
The Perl version is also one line but substantially more verbose because we have to use
split() instead of .words(), join() explcitly requires an empty strin to join and
boolean operations don't automatically convert to true and false when printed.
my @a = split /\s+/, $ARGV[0]; my @b = split /\s+/, $ARGV[1]; say join(q{}, @a) eq join("",@b) ? "true" : "false"
Challenge 2:
List Division
You are given a list and a non-negative integer.
Write a script to divide the given list into given non-negative integer equal parts. Return -1 if the integer is more than the size of the list.
Example 1
Input: @list = (1,2,3,4,5), $n = 2
Output: ((1,2,3), (4,5))
5 / 2 = 2 remainder 1.
The extra element goes into the first chunk.
Example 2
Input: @list = (1,2,3,4,5,6), $n = 3
Output: ((1,2), (3,4), (5,6))
6 / 3 = 2 remainder 0.
Example 3
Input: @list = (1,2,3), $n = 2
Output: ((1,2), (3))
Example 4
Input: @list = (1,2,3,4,5,6,7,8,9,10), $n = 5
Output: ((1,2), (3,4), (5,6), (7,8), (9,10))
Example 5
Input: @list = (1,2,3), $n = 4
Output: -1
Example 6
Input: @list = (72,57,89,55,36,84,10,95,99,35), $n = 7;
Output: ((72,57), (89,55), (36,84), (10), (95), (99), (35))
We start by dealing with the case where the length of the @list is smaller than
$n as in example 5. In that case -1 is printed out and nothing more needs to be done.
if @list.elems < $n {
say "-1";
If @list is long enough...
} else {
We get the length of each part here by dividing the length of @list by $n. Notice
we are using the integer division operator div here because we can't have a fractional
length of a part.
my $len = @list.elems div $n;
Instead, if $n does not divide evenly into the length of @list, we use the modulus
operator to determine the remainder.
my $rem = @list.elems % $n;
We create an array @groups with $n elements, each initially containing $len.
my @groups = $len xx $n;
If there was a remainder it is distributed into the first $rem elements of @groups.
my $i = 0;
while $rem != 0 {
@groups[$i++] += 1;
$rem--;
}
Now @list is divided into $n parts of an array called @results. The number
of elements that get assigned to each part is determined by the value of the
corresponding element in @groups.
my @results = @groups.map({ @list.splice(0, $_) });
Finally, @results is printed out. The slightly complicated code below is so it can be formatted in the same style as the spec.
say q{(},
@results.map({ q{(} ~ @$_.join(q{,}) ~ q{)} }).join(q{, }),
q{)};
}
This is the Perl version.
if (scalar @list < $n) {
say "-1";
} else {
We don't have a dedicated operator for integer division like Raku but we can
simulate it easily enough with int().
my $len = int(scalar @list / $n);
my $rem = scalar @list % $n;
my @groups = ($len) x $n;
my $i = 0;
while ($rem != 0) {
$groups[$i++] += 1;
$rem--;
}
An annoying thing about Perl is that if you want lists of lists, the inner ones have to be list references.
my @results = map { [ splice @list, 0, $_ ] } @groups;
say q{(},
(join q{, }, map { q{(} . (join q{,}, @{$_}) . q{)} } @results),
q{)};
}