Perl Weekly Challenge: Week 307
Challenge 1:
Check Order
You are given an array of integers,
@ints
.Write a script to re-arrange the given array in an increasing order and return the indices where it differs from the original array.
Example 1
Input: @ints = (5, 2, 4, 3, 1)
Output: (0, 2, 3, 4)
Before: (5, 2, 4, 3, 1)
After : (1, 2, 3, 4, 5)
Difference at indices: (0, 2, 3, 4)
Example 2
Input: @ints = (1, 2, 1, 1, 3)
Output: (1, 3)
Before: (1, 2, 1, 1, 3)
After : (1, 1, 1, 2, 3)
Difference at indices: (1, 3)
Example 3
Input: @ints = (3, 1, 3, 2, 3)
Output: (0, 1, 3)
Before: (3, 1, 3, 2, 3)
After : (1, 2, 3, 3, 3)
Difference at indices: (0, 1, 3)
Basically, all we need to do for this one is compare the list of integers (taken from command-line arguments) and compare them to a sorted version of the same list.
This line creates the sorted (in ascending numeric order) list.
my @sorted = @ints.sort({ $^a <=> $^b });
We also need a list to store the indices that differ; @diffs
seems an appropriate name for it.
my @diffs;
We find the indices for @ints
with .keys()
and compare the element at that index with the element at the same index in @sorted
.
for @ints.keys -> $i {
If the two elements are not equal, the index is added to @diffs
.
if @ints[$i] != @sorted[$i] {
@diffs.push($i);
}
}
Finally we output @diffs
. The extra code is just so the output will have the same format as that in the examples.
say q{(}, @diffs.join(q{, }), q{)};
The Perl version works exactly the same as in Raku.
my @sorted = sort { $a <=> $b } @ints;
my @diffs;
for my $i (keys @ints) {
if ($ints[$i] != $sorted[$i]) {
push @diffs, $i;
}
}
say q{(}, (join q{, }, @diffs), q{)};
Challenge 2:
Find Anagrams
You are given a list of words, @words.
Write a script to find any two consecutive words and if they are anagrams, drop the first word and keep the second. You continue this until there is no more anagrams in the given list and return the count of final list.
Example 1
Input: @words = ("acca", "dog", "god", "perl", "repl")
Output: 3
Step 1: "dog" and "god" are anagrams, so dropping "dog" and keeping "god" => ("acca", "god", "perl", "repl")
Step 2: "perl" and "repl" are anagrams, so dropping "perl" and keeping "repl" => ("acca", "god", "repl")
Example 2
Input: @words = ("abba", "baba", "aabb", "ab", "ab")
Output: 2
Step 1: "abba" and "baba" are anagrams, so dropping "abba" and keeping "baba" => ("baba", "aabb", "ab", "ab")
Step 2: "baba" and "aabb" are anagrams, so dropping "baba" and keeping "aabb" => ("aabb", "ab", "ab")
Step 3: "ab" and "ab" are anagrams, so dropping "ab" and keeping "ab" => ("aabb", "ab")
This challenge is much simpler than the wording of the spec might have you think. Actually, you don't need to do all that dropping the first and keeping the second business. To get the right answer, you just need to count the number of unique words ignoring anagrams.
In Raku, you can do this as a one-liner like this:
@*ARGS.map({ $_.comb.sort.join }).unique.elems.say
First we take the input from command-line arguments (in @*ARGS
) and using .map()
normalize the form of each word by splitting it into individual characters with .comb()
, .sort()
ing them and .join()
ing them back into a word. If two words have the same
form, they are anagrams. We find how many unique normalized words there are with .unique()
, count them with .elems()
and print
the result with .say()
.
For Perl, I used a slightly different approach.
A hash called %anagrams
will hold the normalized words.
my %anagrams;
For each word, its' normalized form will become a key in %anagrams
and each time that form occurs, the keys' value will
be incremented.
foreach my $word (@words) {
my $sorted = join q{}, sort split //, $word;
$anagrams{$sorted}++;
}
Now we can get the number of unique normalized words simply be counting the number of keys.
say scalar %anagrams;