Perl Weekly Challenge: Split ’em away!

Task 1: Sort Letters

You are given two arrays, @letters and @weights.

Write a script to sort the given array @letters based on the @weights.

Example 1

Input: @letters = ('R', 'E', 'P', 'L')
       @weights = (3, 2, 1, 4)
Output: PERL

Example 2

Input: @letters = ('A', 'U', 'R', 'K')
       @weights = (2, 4, 1, 3)
Output: RAKU

Example 3

Input: @letters = ('O', 'H', 'Y', 'N', 'P', 'T')
       @weights = (5, 4, 2, 6, 1, 3)
Output: PYTHON

Approach

I’m a little disappointed, because this week’s Task 1 is pretty much last week’s Task 1 with the ordering being specified in a separate array.

Raku

Ok, it’s not entirely like last week. I need to get the letters and weights into the hash somehow, and using the Zip metaoperator (Z) to apply the infix pair operator (=>) (as suggested by brian d foy in Learning Raku) accomplishes this quite neatly.

sub sortLetters(@letters, @weights) {
  my %letters = @weights Z=> @letters;
  return %letters.sort(*.key.Int)>>.values.join(q{});
}

View the entire Raku script for this task on GitHub.

$ raku/ch-1.raku
Example 1:
Input: @letters = ('R', 'E', 'P', 'L')
       @weights = (3, 2, 1, 4)
Output: PERL

Example 2:
Input: @letters = ('A', 'U', 'R', 'K')
       @weights = (2, 4, 1, 3)
Output: RAKU

Example 3:
Input: @letters = ('O', 'H', 'Y', 'N', 'P', 'T')
       @weights = (5, 4, 2, 6, 1, 3)
Output: PYTHON

Example 4:
Input: @letters = ('C', 'd', 'F', 'i', 'l', 'n', 'o', 'o', 's', 'u')
       @weights = (1, 4, 5, 8, 3, 10, 9, 2, 7, 6)
Output: ColdFusion

Perl

Perl doesn’t have a zip metaoperator, but there’s a zip in List::AllUtils that does exactly what we need.

use List::AllUtils qw( zip );

sub sortLetters($letters, $weights) {
  my %letters = zip @$weights, @$letters;
  return join q{}, map {
    $letters{$_}
  } sort { $a <=> $b } keys %letters;
}

View the entire Perl script for this task on GitHub.

Python

In Python, we just combine the zip function with the dict function to interleave the values and turn the two lists into a dict.

def sortLetters(letters, weights):
  letterDict = dict(zip(weights, letters))
  return (''.join([
    letterDict[k] for k in sorted(letterDict.keys(), key=int)
  ]))

View the entire Python script for this task on GitHub.

Elixir

In Elixir, Enum.zip/2 handles the interleaving, but it produces tuples, so I’ll use Tuple.to_list/1 to convert them to lists and then List.first/2 and List.last/2 to get the parts I want from the lists.

  def sortLetters(letters, weights) do
    Enum.zip(weights, letters)
    |> Enum.sort_by( &( &1 |> Tuple.to_list |> List.first ))
    |> Enum.map_join("", &( &1 |> Tuple.to_list |> List.last ))
  end

View the entire Elixir script for this task on GitHub.


Task 2: Split String

You are given a string, $str.

Write a script to split the given string into two containing exactly same number of vowels and return true if you can otherwise false.

Example 1

Input: $str = "perl"
Output: false

Example 2

Input: $str = "book"
Output: true

Two possible strings "bo" and "ok" containing exactly one vowel each.

Example 3

Input: $str = "good morning"
Output: true

Two possible strings "good " and "morning" containing two vowels each or "good m" and "orning" containing two vowels each.

Approach

I’m not going to produce the explanatory text this time, because that’s going to complicate the problem. With just having to produce true or false, this is a simple exercise in count how many vowels there are, and if the number is even, it is possible to split the string in two such that there are the same number of vowels in each part. For the purposes of this problem, we will not consider y to be a vowel.

Raku

Remembering that the .comb routine on the Str type has a matcher parameter that can take a Regex… but first pass the string through .lc so we do case-insensitive matching, get the number of letters returned through .elems, and see if that’s even.

sub splitString($str) {
  return $str.lc.comb(/<[aeiou]>/).elems % 2 == 0;
}

View the entire Raku script for this task on GitHub.

$ raku/ch-2.raku
Example 1:
Input: $str = "perl"
Output: False

Example 2:
Input: $str = "book"
Output: True

Example 3:
Input: $str = "good morning"
Output: True

Perl

In Perl, we just split on an empty regex, use grep with a case-insensitive regex to filter for just vowels, and take advantage that the value of a list in scalar context is the number of elements in the list.

sub splitString($str) {
  return (
    ( grep { /[aeiou]/i } split //, $str ) % 2 == 0
    ? 'True' : 'False'
  );
}

View the entire Perl script for this task on GitHub.

Python

Rather than use a regex to match vowels, I’m taking advantage of Python’s if scalar in list construction, and the neat little idiom list(string) to convert a string into a list of characters:

vowels = ['a', 'e', 'i', 'o', 'u']

def splitString(str):
    return len([
        v for v in list(str.lower()) if v in vowels
    ]) % 2 == 0

View the entire Python script for this task on GitHub.

Elixir

`We’re just piping output from function to function here: String.downcase/2 converts the string to lowercase, String.graphemes/1 returns the string as a list of Unicode characters, Enum.filter/2 filters the list by the specified function, String.match/2 checks to see if the given string (in this case, a single character) matches the regex, Kernel.length/1 gives us the length of the filtered list as an integer, and Integer.is_even/1 lets us know if that integer is even.

  require Integer
  def splitString(str) do
    str
    |> String.downcase
    |> String.graphemes
    |> Enum.filter(&( String.match?(&1, ~r/[aeiou]/) ))
    |> length
    |> Integer.is_even
  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-279/packy-anderson

2 thoughts on “Perl Weekly Challenge: Split ’em away!

    • So it would be

      sub splitString($str) {
      return $str.lc.comb(/<[aeiou]>/).elems %% 2;
      }

Comments are closed.