| 15 May 2026 | Challenge 373 |
Divide and Concat
Task 1: Equal List
Submitted by: Mohammad Sajid Anwar
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"
Solution
Perl
Interpolating a Perl array "@a" is equivalent to join $", @a.
When $" is the empty string, all that needs to be done in this task is to compare the interpolated arrays.
use strict;
use warnings;
use experimental 'signatures';
sub equal_list ($arr1, $arr2) {
local $" = '';
"@$arr1" eq "@$arr2";
}
See the full solution to task 1.
J
In J, a list of strings having different lengths has to be represented as a list of boxed strings. Removing the boxes from such a list results in the concatenation of the strings. The solution in J is as simple as in Perl: Remove the boxes and compare the results.
equal_list =: -:&;
echo ('a';'bc') equal_list 'ab';'c'
1
See the full solution.
Task 2: List Division
Submitted by: Mark Anderson
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))
Solution
With \(l\) as the list length and \(n\) as the requested number of chunks, the list can be split into two parts:
- The head consists of all elements that produce chunks with an extra element. It may be empty.
- the tail consists of the remaining elements
The chunk size for the head is \(\lfloor \frac{l}{n} \rfloor + 1\) and for the tail it is \(\lfloor \frac{l}{n} \rfloor\).
The head produces \(l \operatorname{mod} n\) chunks and the number of elements in the head therefore is: \((\lfloor \frac{l}{n} \rfloor + 1) (l \operatorname{mod} n)\)
Both parts can be evenly chunked in the respective chunk size resulting in a total number of \(n\) chunks.
I’m not going to provide the requested return value -1 for on under-sized list from the subroutine / verb as it represents an incompatible type.
This value is handled in specific I/O sections in the programs and these are not presented here.
See the full solutions for the specific implementations.
Contrary to the task description, \(n\) must be a positive number.
Perl
- Calculate the size of head chunks
- Remove the head from the list, reducing it to the tail.
- Bundle head and tail in their respective sizes.
- Gather the chunks.
use strict;
use warnings;
use experimental 'signatures';
use List::UtilsBy 'bundle_by';
use List::Gather;
sub list_division ($n, @list) {
return () if $n > @list;
my $chunk_size = 1 + int @list / $n;
my @head = splice @list, 0, $chunk_size * (@list % $n);
gather {
bundle_by {take [@_]} $chunk_size--, @$_ for \@head, \@list;
};
}
See the full solution to task 2.
J
Changing the point of view for the J implementation.
Let \(q = \lfloor \frac{l}{n}\rfloor\) and \(r = l \operatorname{mod} n\) be the integer quotient and the remainder of \(n\) and \(l\), with \(l\) as the length of the list.
There are \(n\) slots, where slot \(s_i\) for \(0 \le i \lt n\) contains \(q + 1\) elements when \(i < r\) or \(q\) elements otherwise. The slot indices, repeated for each item the slot contains, form an array with length \(l\). This array can be used as a slot map for the list’s items.
This might look a bit complicated, but it is actually fairly simple. Consider example 6:
We have \(n = 7\) and \(l = 10\), leading to \(q = 1\) and \(r = 3\).
Slot indices \(0,1,2\) are less than \(r = 3\) and thus repeated \(q + 1 = 2\) times and the remaining indices \(3,\ldots,6\)
are “repeated” \(q = 1\) time, leading to a slot map of 0 0 1 1 2 2 3 4 5 6.
Thus the first two list items go into slot 0, the next two pairs go into slots 1 and 2 and the remaining four make a slot of their own.
Assembling the solution as a tacit verb from named parts.
- Enumerate the slots.
seq =. i.@[ - Calculate \(q\) and \(r\).
divmod =. #@] #:~ 0 , [ - The core of the algorithm: Repeat the slot number according to the number of items it contains as
q r slot s. It builds the tuples, q, r, sand inserts the verbs#~,+and>between its elements, evaluatings #~ q + r > s, i.e. it replicates the slot indexsq + (r > s)-times. The resulting list is boxed.slot =. [: <@(#~)`+`>/ ] , [ , ] - Remove one level of boxing
raze =. ; - gather elements from the left argument having the same key in the right argument in a box
box_by =. </.~ - Chunk the list by creating the slot map over all slots and apply it to the list
chunk =: ] box_by [: raze divmod slot"1 0 seq - Generate an empty list.
empty =. (0$0)"_ - Perform a verb from the left operand depending on the result of \(n > l\).
if_valid =. @.([: {. [ > #@]) - Apply
chunkorempty, fix intermediate verbs and enforce dyadic usage[: : chunk`empty if_valid f.
All parts are gathered within the body of an anonymous adverb that is applied to a dummy operand, resulting in a final tacit verb that is assigned to the name list_division. The defined names are not visible outside the adverb’s body.
list_division =: _(adverb define)
seq =. i.@[
divmod =. #@] #:~ 0 , [
slot =. [: <@(#~)`+`>/ ] , [ , ]
raze =. ;
box_by =. </.~
chunk =: ] box_by [: raze divmod slot"1 0 seq
empty =. (0$0)"_
if_valid =. @.([: {. [ > #@])
[: : chunk`empty if_valid f.
)
This implementation does not depend on the type of its right argument. It can be applied to e.g. strings or lists of words and it can divide a list into multiple sizes:
7 list_division (72,57,89,55,36,84,10,95,99,35)
┌─────┬─────┬─────┬──┬──┬──┬──┐
│72 57│89 55│36 84│10│95│99│35│
└─────┴─────┴─────┴──┴──┴──┴──┘
5 list_division 'theweeklychallenge'
┌────┬────┬────┬───┬───┐
│thew│eekl│ycha│lle│nge│
└────┴────┴────┴───┴───┘
3 list_division 'the';'weekly';'challenge';'perl';'raku';'and';'other'
┌──────────────────────┬───────────┬───────────┐
│┌───┬──────┬─────────┐│┌────┬────┐│┌───┬─────┐│
││the│weekly│challenge│││perl│raku│││and│other││
│└───┴──────┴─────────┘│└────┴────┘│└───┴─────┘│
└──────────────────────┴───────────┴───────────┘
2 3 <@list_division"(0 _) 1 2 3 4 5
┌───────────┬───────────┐
│┌─────┬───┐│┌───┬───┬─┐│
││1 2 3│4 5│││1 2│3 4│5││
│└─────┴───┘│└───┴───┴─┘│
└───────────┴───────────┘
For the sake of convenience the latter examples are not available from the command line.
BTW, the resulting verb is:
[: :(] </.~ [: ; (#@] #:~ 0 , [) ([: <@(#~)`+`>`:3 ] , [ , ])"1 0 i.@[)`((0$0)"_)@.([: {. [ > #@])
See the full solution.