Perl Weekly Challenge: Because your list, your list I can’t resist

Perl Weekly Challenge 373‘s tasks are “Equal List” and “List Division”.

But if you insist on knowing my BLISS
I’ll tell you this
If you want to know what the Reason is
I only smile when I lie, then I’ll tell you why…

Because your kiss, your kiss is an equal list
Because your kiss, your kiss divides my list
Because your kiss is on my list of the best things in life

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

Approach

This problem screams for a join. Yes, we could use other methods to concatenate strings together, but in all the languages I’m using, join accepts an array of arbitrary length, so I don’t have to account for how many entries there are in the array.

Raku

And in two of these languages, I don’t even need to pass a separator. In Raku, List.join defaults to the empty string as a separator.

sub equal_list(@arr1, @arr2) {
  return @arr1.join eq @arr2.join;
}

View the entire Raku script for this task on GitHub.

$ raku/ch-1.raku
Example 1:
Input: @arr1 = ("a", "bc")
       @arr2 = ("ab", "c")
Output: True

Example 2:
Input: @arr1 = ("a", "b", "c")
       @arr2 = ("a", "bc")
Output: True

Example 3:
Input: @arr1 = ("a", "bc")
       @arr2 = ("a", "c", "b")
Output: False

Example 4:
Input: @arr1 = ("ab", "c", "")
       @arr2 = ("", "a", "bc")
Output: True

Example 5:
Input: @arr1 = ("p", "e", "r", "l")
       @arr2 = ("perl")
Output: True

Perl

Perl’s join, however does require a separator.

sub equal_list($arr1, $arr2) {
  return join('', @$arr1) eq join('', @$arr2) ? 'true' : 'false';
}

View the entire Perl script for this task on GitHub.

Python

In Python, .join is a method is a method off the string object acting as the separator.

def equal_list(arr1, arr2):
  return ''.join(arr1) == ''.join(arr2)

View the entire Python script for this task on GitHub.

Elixir

Elixir is the other language where Enum.join/2 doesn’t require a separator; if joiner is not passed at all, it defaults to an empty string.

def equal_list(arr1, arr2) do
  Enum.join(arr1) == Enum.join(arr2)
end

View the entire Elixir script for this task on GitHub.


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

Approach

Really, the first example gives the approach away. We divide the length of the list by $n, and get back the integer result ($c) and the remainder ($r). We then start making lists of $c elements, adding an extra element to the first $r lists.

Python

Because last week in PWC 372’s first task, I discovered Python’s divmod, I’m doing the Python solution first.

def list_div(arr, n):
  if len(arr) < n: return -1
  c, r = divmod(len(arr), n)
  result = []
  while arr:
    a = []
    for i in range(0, c):
      a.append(arr.pop(0))
    if r: # we have a remainder to deal with
      a.append(arr.pop(0))
      r -= 1
    result.append(a)
  return result

View the entire Python script for this task on GitHub.

$ python/ch-2.py
Example 1:
Input: @list = (1, 2, 3, 4, 5), $n = 2
Output: ((1,2,3), (4,5))

Example 2:
Input: @list = (1, 2, 3, 4, 5, 6), $n = 3
Output: ((1,2), (3,4), (5,6))

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 5:
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))

Raku

And because I’m so enamored of divmod, I’m rolling my own in the other languages…

sub divmod($x, $y) { ($x div $y, $x % $y) }

sub list_div(@list, $n) {
  return -1 if @list.elems < $n;
  my ($c, $r) = divmod(@list.elems, $n);
  my @result;
  while @list {
    my @a;
    @a.push(@list.shift) for 1..$c;
    @a.push(@list.shift) if --$r >= 0;
    @result.push(@a);
  }
  @result;
}

View the entire Raku script for this task on GitHub.

Perl

sub divmod($x, $y) { (int($x / $y), $x % $y) }

sub list_div($list, $n) {
  return -1 if @$list < $n;
  my ($c, $r) = divmod(scalar(@$list), $n);
  my @result;
  while (@$list) {
    my @a;
    push @a, shift(@$list) for 1..$c;
    push @a, shift(@$list) if --$r >= 0;
    push @result, \@a;
  }
  @result;
}

View the entire Perl script for this task on GitHub.

Elixir

I rolled a bunch of custom functions in this solution:

  • divmod/2 – like I did in each of the previous two
  • shift_first/3 – takes the first c elements in the provided list and appends them to shifted. This made mode sense to me than trying to modify both list and a result array in an Enum.reduce/3.
  • build_result/4 – similarly, once we calculated c and r, it would probably have been possible to do the looping with an Enum.reduce/3, but it made a lot more sense to build a recursive function to process list.
  • I used a lot of guards and even a default value in a function head.
def divmod(x,y), do: { div(x,y), rem(x, y) }

def shift_first(list, c, shifted \\ [])
def shift_first(list, c, shifted) when c == 0, do:
  {list, shifted}
def shift_first([e | rest], c, shifted) do
  shift_first(rest, c-1, shifted ++ [e])
end

def build_result([], _, _, result), do: result
def build_result(list, c, r, result) do
  {list, a} = shift_first(list, c)
  {list, a} = if r > 0 do
    shift_first(list, 1, a)
  else
    {list, a}
  end
  build_result(list, c, r-1, result ++ [a])
end

def list_div(list, n) when length(list) < n, do: -1
def list_div(list, n) do
  {c, r} = divmod(length(list), n)
  build_result(list, c, r, [])
end

View the entire Elixir script for this task on GitHub.


Here’s all my solutions in GitHub: https://github.com/packy/perlweeklychallenge-club/tree/challenge-373-packy-anderson/challenge-373/packy-anderson

Leave a Reply