Perl Weekly Challenge: Luck Concatenate Numbers Tonight!

Tonight’s musical mood: Luck Be A Lady!

Onward to Perl Weekly Challenge 251!


Task 1: Concatenation Value

You are given an array of integers, @ints.

Write a script to find the concatenation value of the given array.

The concatenation of two numbers is the number formed by concatenating their numerals.

For example, the concatenation of 10, 21 is 1021.
The concatenation value of @ints is initially equal to 0.
Perform this operation until @ints becomes empty:

If there exists more than one number in @ints, pick the first element and last element in @ints respectively and add the value of their concatenation to the concatenation value of @ints, then delete the first and last element from @ints.

If one element exists, add its value to the concatenation value of @ints, then delete it.

Example 1

Input: @ints = (6, 12, 25, 1)
Output: 1286

1st operation: concatenation of 6 and 1 is 61
2nd operation: concatenation of 12 and 25 is 1225

Concatenation Value => 61 + 1225 => 1286

Example 2

Input: @ints = (10, 7, 31, 5, 2, 2)
Output: 489

1st operation: concatenation of 10 and 2 is 102
2nd operation: concatenation of 7 and 2 is 72
3rd operation: concatenation of 31 and 5 is 315

Concatenation Value => 102 + 72 + 315 => 489

Example 3

Input: @ints = (1, 2, 10)
Output: 112

1st operation: concatenation of 1 and 10 is 110
2nd operation: only element left is 2

Concatenation Value => 110 + 2 => 112

Approach

I know there’s an iterative approach that’s probably more efficient, but when I saw the definition of how to do calculate the concatenation value of an array of integers, my brain flew back to being a 15-year-old learning how to program, and discovering the wonders of… recursion.

Edit: It occurs to me that seeing Niklaus Wirth’ obituary probably also inspired me, because part and parcel of my discovering recursion when I was 15 was learning Pascal, my favorite programming language before Perl (also the second or third (I don’t remember if it was BASIC, Pascal, then COBOL, or BASIC, COBOL, then Pascal)).

Raku

sub concatenationValue(@ints) {
  return 0        if @ints.elems == 0; # no elements
  return @ints[0] if @ints.elems == 1; # one element

  my $first = @ints.shift; # first element
  my $last  = @ints.pop;   # last element
  my $concat = "$first$last".Int; # concatenate and convert

  return $concat + concatenationValue(@ints);
}

View the entire Raku script for this task on GitHub.

Perl

Once again I got to trot out the “adding 0 to a string to make it clear I’m converting it into a numeric value” trick, and I also got to show how evaluating an array in scalar context (the @ints == 0 and @ints == 1) yields the number of elements in the array.

sub concatenationValue(@ints) {
  return 0        if @ints == 0; # no elements
  return $ints[0] if @ints == 1; # one element

  my $first = shift @ints; # first element
  my $last  = pop @ints;   # last element
  my $concat = "$first$last"+0; # concatenate and convert

  return $concat + concatenationValue(@ints);
}

View the entire Perl script for this task on GitHub.

Python

The only thing to point out here is Python’s pop method, which removes the element at the position specified by the parameter. If the parameter is omitted, it removes and returns the last value in the array, but since I had to specify position 0 to get the first element, I figured it was symmetrical to specify -1 to explicitly get the last value.

def concatenationValue(ints):
    if len(ints) == 0: # no elements
        return 0
    if len(ints) == 1: # one element
        return ints[0]

    first = ints.pop(0);  # first element
    last  = ints.pop(-1); # last element
    concat = int(f"{first}{last}") # concatenate and convert

    return concat + concatenationValue(ints)

View the entire Python script for this task on GitHub.


Task 2: Lucky Numbers

You are given a m x n matrix of distinct numbers.

Write a script to return the lucky number, if there is one, or -1 if not.

A lucky number is an element of the matrix such that it is
the minimum element in its row and maximum in its column.

Example 1

Input: $matrix = [ [ 3,  7,  8],
                   [ 9, 11, 13],
                   [15, 16, 17] ];
Output: 15

15 is the only lucky number since it is the minimum in its row
and the maximum in its column.

Example 2

Input: $matrix = [ [ 1, 10,  4,  2],
                   [ 9,  3,  8,  7],
                   [15, 16, 17, 12] ];
Output: 12

Example 3

Input: $matrix = [ [7 ,8],
                   [1 ,2] ];
Output: 7

Approach

Since the definition of the problem states that the numbers are distinct (the same number will not appear twice in the matrix), we can break this down into two problems: find the minimum elements in each row, and find the maximum elements in each column. If a value appears in both lists, we have our lucky number.

Getting the minimum in each row is easy, but getting the max in each column is a little more work; what I’m going to do is rotate the matrix so the rows are now columns and the columns are now rows, and just take the max value from what are now rows in the matrix.

Raku

sub maxCols(@matrix) {
  my @rotated; # rotate cols to rows
  for @matrix -> @row {
    for @row.kv -> $k, $v {
      @rotated[$k].push($v);
    }
  }
  # return the max of the now rows!
  return @rotated.map({ $_.max() });
}

sub luckyNumber(@matrix) {
  my @minRows = @matrix.map({ $_.min() });
  my @maxCols = maxCols(@matrix);
  return ( @minRows (&) @maxCols ) // -1;
}

View the entire Raku script for this task on GitHub.

Perl

Perl doesn’t have built-in min, max, or intersection operators, so we need to pull them in from the CPAN modules Array::Utils and List::Util (though List::Util is in the core).

use Array::Utils qw( intersect );
use List::Util qw( min max );

sub maxCols(@matrix) {
  my @rotated; # rotate cols to rows
  foreach my $row ( @matrix ) {
    foreach my $k ( 0 .. $#{$row} ) {
      my $v = $row->[$k];
      push @{$rotated[$k]}, $v;
    }
  }
  # return the max of the now rows!
  return map { max( @$_ ) } @rotated;
}

sub luckyNumber(@matrix) {
  my @minRows = map { min( @$_ ) } @matrix;
  my @maxCols = maxCols(@matrix);
  # intersect() returns an array, so get the first element
  return (intersect( @minRows, @maxCols ))[0] // -1;

View the entire Perl script for this task on GitHub.

Python

For my Python solution, I was able to do it entirely using built-in functions.

def maxCols(matrix):
    # initialize rotated with empty row for each column
    rotated = [ [] for col in matrix[0] ]
    for row in matrix:
        for k, v in enumerate(row):
            rotated[k].append(v)
    # return the max of the now rows!
    return [ max(row) for row in rotated ]

def luckyNumber(matrix):
    mRows = [ min(row) for row in matrix ]
    mCols = maxCols(matrix)
    intersection = list( set(mRows) & set(mCols) )
    if intersection:
        return intersection[0]
    else:
        return -1

View the entire Python script for this task on GitHub.


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