Perl Weekly Challenge: A-ray Sunshine!

The tasks for Perl Weekly Challenge 344 both have names that talk about being arrays, and nothing musically jumped out at me relating to arrays. So I started free-associating, and arrays made me think of rays, and rays of sunshine, and sunshine made me think of The Beatles.

So let’s have a good day and go ahead with PWC 344!

Task 1: Array Form Compute

You are given an array of integers, @ints and an integer, $x.

Write a script to add $x to the integer in the array-form.

The array form of an integer is a digit-by-digit representation stored as an array, where the most significant digit is at the 0th index.

Example 1

Input: @ints = (1, 2, 3, 4), $x = 12
Output: (1, 2, 4, 6)

Example 2

Input: @ints = (2, 7, 4), $x = 181
Output: (4, 5, 5)

Example 3

Input: @ints = (9, 9, 9), $x = 1
Output: (1, 0, 0, 0)

Example 4

Input: @ints = (1, 0, 0, 0, 0), $x = 9999
Output: (1, 9, 9, 9, 9)

Example 5

Input: @ints = (0), $x = 1000
Output: (1, 0, 0, 0)

Approach

It took me a minute to figure out what this meant. The array we’re passed is the digits of an integer. We need to add $x to that integer, and return it as an array of digits.

I could either break up $x the same way and try to add the digits manually, but that sounds like hard work. Why not take the digits of the integer in the array, reconstitute it into a number, then use the language’s built-in addition to take care of the addition, and just break the result up into the individual characters when I’m done?

Raku

Ok, I’m doing a bunch of things I don’t have to here, but I want to so it’s clear what’s happening. The @ints.join('') should be clear; it’s taking the values in the @ints array and joining them together with no characters separating them. But even though just by adding $x to it means Raku will treat the result as a numeric, I’m calling .Int on the string to explicitly coerce it as an integer. Then, once I’ve added $x, I call .Str on the integer to explicitly coerce it back to string, and then .comb with no matcher returns a sequence of the characters in the string.

sub compute(@ints, $x) {
  (@ints.join('').Int + $x).Str.comb;
} 

View the entire Raku script for this task on GitHub.

$ raku/ch-1.raku
Example 1:
Input: @ints = (1, 2, 3, 4), $x = 12
Output: (1, 2, 4, 6)

Example 2:
Input: @ints = (2, 7, 4), $x = 181
Output: (4, 5, 5)

Example 3:
Input: @ints = (9, 9, 9), $x = 1
Output: (1, 0, 0, 0)

Example 4:
Input: @ints = (1, 0, 0, 0, 0), $x = 9999
Output: (1, 9, 9, 9, 9)

Example 5:
Input: @ints = (0), $x = 1000
Output: (1, 0, 0, 0)

Perl

Perl doesn’t have those explicit markers for coercion, however, but the addition of $x ensures that join('', @$ints) is coerced into a numeric, and then passing that as the second argument to split ensures it’s coerced into a string.

sub compute($ints, $x) {
  split //, join('', @$ints) + $x;
}

View the entire Perl script for this task on GitHub.

Python

The thing that makes the Python solution a little more verbose is that we have to explicitly map the integers in ints to strings before we can join them together, but from there this pretty much reads like the previous solutions: "".join(map(str, ints)) to get a string of from the array, int() to cast it as an integer, adding x to it, then str() to cast it back to a string, and list() to turn it into a list of characters.

def compute(ints, x):
  return list(str(int("".join(map(str, ints))) + x))

View the entire Python script for this task on GitHub.

Elixir

In Elixir, I knew I could just pipe the result from one operation to another and it would read the way it works. Unfortunately, I can’t pipe to String.to_integer + x, so I needed to label the result as num and then start a new pipe with num + x. It still reads beautifully, though.

  def compute(ints, x) do
    num = ints
    |> Enum.join
    |> String.to_integer
    num + x
    |> Integer.to_string
    |> String.graphemes
  end

View the entire Elixir script for this task on GitHub.

UPDATE!!!

One of my coworkers, Diego, read my blog post, and he had suggestions for the Elixir solution:

  def compute(ints, x) do
    ints
    |> Enum.join
    |> String.to_integer
    |> Kernel.+(x)
    |> Integer.to_string
    |> String.graphemes
    |> Enum.map(&String.to_integer/1)
  end

I hadn’t realized that the Kernel module had all the operators in it, so instead of assigning my number to a label, I could continue the pipe by calling Kernel.+/2 with x as the argument (remember, the pipe |> takes the output of the last function and makes it the first argument to the next one). And at the end, Diego had a point: since the array form is an array of integers, I should be converting the individual numeric characters back to integers for the final results.

And, because Python has the same strict type representation, I also tweaked my Python implementation to convert the stringy numeric characters back to integers:

def compute(ints, x):
  return map(int, list(str(int("".join(map(str, ints))) + x)))

Task 2: Array Formation

You are given two list: @source and @target.

Write a script to see if you can build the exact @target by putting these smaller lists from @source together in some order. You cannot break apart or change the order inside any of the smaller lists in @source.

Example 1

Input: @source = ([2,3], [1], [4])
       @target = (1, 2, 3, 4)
Output: true

Use in the order: [1], [2,3], [4]

Example 2

Input: @source = ([1,3], [2,4])
       @target = (1, 2, 3, 4)
Output: false

Example 3

Input: @source = ([9,1], [5,8], [2])
       @target = (5, 8, 2, 9, 1)
Output: true

Use in the order: [5,8], [2], [9,1]

Example 4

Input: @source = ([1], [3])
       @target = (1, 2, 3)
Output: false

Missing number: 2

Example 5

Input: @source = ([7,4,6])
       @target = (7, 4, 6)
Output: true

Use in the order: [7, 4, 6]

Approach

Since the first task usually informs the second task, the way I’m going to approach this is to take @source and convert the smaller lists into strings, convert @target into a single string, and then determine whether the list of source strings can be arranged to produce the target string. Since the output is true/false, I don’t have to convert anything back to an array.

The easy way to do this is use some library routine that produces permutations, and just join those permutations.

Raku

In Raku, we have List.permutations.

sub formation(@source is copy, @target) {
  @source = @source.map({ $_.join('') });
  my $target = @target.join('');
  for @source.permutations -> @perm {
    return 'true' if @perm.join('') eq $target;
  }
  return 'false';
}

View the entire Raku script for this task on GitHub.

$ raku/ch-2.raku
Example 1:
Input: @source = ([2,3], [1], [4])
       @target = (1, 2, 3, 4)
Output: true

Example 2:
Input: @source = ([1,3], [2,4])
       @target = (1, 2, 3, 4)
Output: false

Example 3:
Input: @source = ([9,1], [5,8], [2])
       @target = (5, 8, 2, 9, 1)
Output: true

Example 4:
Input: @source = ([1], [3])
       @target = (1, 2, 3)
Output: false

Example 5:
Input: @source = ([7], [4], [6])
       @target = (7, 4, 6)
Output: true

Perl

For Perl, I’m using Algorithm::Combinatoricspermutations method.

use Algorithm::Combinatorics qw(permutations);

sub formation($source, $target) {
  my @source = map { join('', @$_) } @$source;
  $target = join('', @$target);
  my $iter = permutations(\@source);
  while (my $perm = $iter->next) {
    return 'true' if join('', @$perm) eq $target;
  }
  return 'false';
}

View the entire Perl script for this task on GitHub.

Python

In Python, it’s the permutations function from itertools:

from itertools import permutations

def formation(source, target):
  source = [ "".join(map(str, s)) for s in source ]
  target = "".join(map(str, target))
  for perm in permutations(source):
    if "".join(perm) == target: return 'true'
  return 'false'

View the entire Python script for this task on GitHub.

Elixir

In Elixir, there doesn’t appear to be a built-in function for generating the permutations of a list, but it’s incredibly easy to roll your own:

def permutations([]), do: [[]]

def permutations(list) do
  for head <- list, rest <- permutations(list -- [head]),
    do: [head|rest]
end

And from there, the rest of it is fairly straightforward.

  def formation(source, target) do
    source = Enum.map(source, fn s -> Enum.join(s, "") end)
    target = Enum.join(target)
    {_, result} = Enum.map_reduce(
      permutations(source),
      "false",
      fn perm, result ->
        {
          perm,
          cond do
            Enum.join(perm) == target -> "true"
            true -> result
          end
        }
      end
    )
    result
  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/master/challenge-344/packy-anderson

Leave a Reply