“Oh, look at that motorcycle, he’s ridin’ between / He’s splittin’ lanes, if you know what I mean…“
We’re just splitting strings this week. Oh, and sorting letters. Onward to Perl Weekly Challenge 279!
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
Great solutions. you can also use Raku’s %% operator like .elems %% 2
So it would be
sub splitString($str) {
return $str.lc.comb(/<[aeiou]>/).elems %% 2;
}