The Bear's Den

Enter at your own risk

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 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

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.

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.