This is my response to The Weekly Challenge #373.
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"
File: equal-list
#! /usr/bin/env raku
unit sub MAIN (*@arrays where @arrays.elems > 2,
:s(:$separator) = '|';
:v(:$verbose));
my $sep-at = @arrays.first($separator, :k)
|| die "Did not find array separator";
my @arr1 = @arrays[0 .. $sep-at -1];
my @arr2 = @arrays[$sep-at +1 .. *];
my $arr1 = @arr1.join;
my $arr2 = @arr2.join;
if $verbose
{
say ":\@arr1: { @arr1.map({ '"' ~ $_ ~ '"' }).join(", ") } \
-> \"$arr1\"";
say ":\@arr2: { @arr2.map({ '"' ~ $_ ~ '"' }).join(", ") } \
-> \"$arr2\"";
}
say $arr1 eq $arr2;
[3] We cannot specify two slurpy arrays, so one will have to do.
[4] Then we specify a dividing string, to be used to separate the two parts. Note the default value.
[7] Get the index of the first instance of the
separator string in the array. The k part tells first to give
the key - i.e. the index - instead of the value itself.
See docs.raku.org/routine/first for more information about first.
There may be additional instances of the separator string, but the split will be done on the first one (from the left). So any duplicates will end up in the second array. It is highly advisable to avoid using a separator string that occurs natively in the arrays.
[10] Extract the first array, with an array slice; everything before the separator.
[11] And the second array; everything after the separator.
[13] Join the elements in the first array to a string.
[14] Do the same with the second one.
[24] Are they equal?
Running it:
$ ./equal-list "a" "bc" "|" "ab" "c"
True
$ ./equal-list "a" "b" "c" "|" "a" "bc"
True
$ ./equal-list "a" "bc" "|" "a" "b" "c"
True
$ ./equal-list "ab" "c" "" "|" "" "a" "bc"
True
$ ./equal-list p e r l "|" perl
True
Looking good.
With verbose mode:
$ ./equal-list -v "a" "bc" "|" "ab" "c"
:@arr1: "a", "bc" -> "abc"
:@arr2: "ab", "c" -> "abc"
True
$ ./equal-list -v "a" "b" "c" "|" "a" "bc"
:@arr1: "a", "b", "c" -> "abc"
:@arr2: "a", "bc" -> "abc"
True
$ ./equal-list -v "a" "bc" "|" "a" "b" "c"
:@arr1: "a", "bc" -> "abc"
:@arr2: "a", "b", "c" -> "abc"
True
$ ./equal-list -v "ab" "c" "" "|" "" "a" "bc"
:@arr1: "ab", "c", "" -> "abc"
:@arr2: "", "a", "bc" -> "abc"
True
$ ./equal-list -v p e r l "|" perl
:@arr1: "p", "e", "r", "l" -> "perl"
:@arr2: "perl" -> "perl"
True
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))
#! /usr/bin/env raku
unit sub MAIN (*@list is copy where @list.elems > 1,
:$n,
:v(:$verbose));
if $n > @list.elems
{
say -1;
exit;
}
my $bucket-size = @list.elems div $n;
my $extra = @list.elems % $n;
if $verbose
{
say ":Bucket size: $bucket-size";
say ":Extra values: $extra";
}
my @output = gather
{
while @list.elems
{
my $count = $bucket-size;
if $extra { $count++; $extra--; }
take @list.splice(0, $count);
}
}
say "(" ~ @output.map({"(" ~ $_.join(",") ~ ")"}).join(", ")
~ ")";
[3] Note the is copy adverb, so that we can change the
value later (in [28]). Ensure at lest two elements.
See docs.raku.org/type/Parameter#method_copy for more information about is copy.
[4] Specify the $n value as a named argument.
[7] Report failure (-1) in [9] and exit in [10] if $n is too
large.
[13] Get the bucket size; the minimum number of elements in each division (or bucket).
[14] The number of extra elements left over after calculating the bucket size [13].
[22] The gathering (so to speak) of the values in the buckets is a good match
for gather/take. We collect the substrings in this
array.
See my Raku Gather, I Take article or docs.raku.org/language/control#gather/take for more information about gather/take.
[24] As long as we have more elements to bucketise.
[26] The number of elemets to put in the current bucket.
[27] Add one, if we have any leftovers from [14]. And adjust the leftover count as we use them.
[28] Use splice to extract the required number of elements
from the array, and return them with take.
See docs.raku.org/routine/splice for more information about splice.
[32] Pretty print the result, a list of list.
Running it:
$ ./list-division -n=2 1 2 3 4 5
((1,2,3), (4,5))
$ ./list-division -n=3 1 2 3 4 5 6
((1,2), (3,4), (5,6))
$ ./list-division -n=2 1 2 3
((1,2), (3))
$ ./list-division -n=5 1 2 3 4 5 6 7 8 9 10
((1,2), (3,4), (5,6), (7,8), (9,10))
$ ./list-division -n=4 1 2 3
-1
$ ./list-division -n=7 72 57 89 55 36 84 10 95 99 35
((72,57), (89,55), (36,84), (10), (95), (99), (35))
Looking good.
With verbose mode:
$ ./list-division -v -n=2 1 2 3 4 5
:Bucket size: 2
:Extra values: 1
((1,2,3), (4,5))
$ ./list-division -v -n=3 1 2 3 4 5 6
:Bucket size: 2
:Extra values: 0
((1,2), (3,4), (5,6))
$ ./list-division -v -n=2 1 2 3
:Bucket size: 1
:Extra values: 1
((1,2), (3))
$ ./list-division -v -n=5 1 2 3 4 5 6 7 8 9 10
:Bucket size: 2
:Extra values: 0
((1,2), (3,4), (5,6), (7,8), (9,10))
$ ./list-division -v -n=4 1 2 3
-1
$ ./list-division -v -n=7 72 57 89 55 36 84 10 95 99 35
:Bucket size: 1
:Extra values: 3
((72,57), (89,55), (36,84), (10), (95), (99), (35))
And that's it.