The two new challenges in The Weekly Challenge #240 are pretty straightforward. It’s spelled right out for you in the instructions. Read it carefully and the code writes itself.
This was the perfect week to try to simplify my Perl code. I’ve said previously that I’m no Perl Golfer, but maybe this is the week to create a one-liner solution. In both challenges, the heart of the answer is, indeed, one line long.
But then I go and mess everything up by making the script run both samples from the challenge and pretty print everything out to the screen. Suddenly, a one-line script has three subroutines and 57 lines supporting the one that does the actual work.
Sorry, I just spoiled Challenge #2.
“Acronym”
You are given an array of strings and a check string.
Write a script to find out if the check string is the acronym of the words in the given array.
In other words, grab the first letter of each word, smash them together, and see if the resulting string is equal to the check string. In my mind, I see a substr() call and a join() and bob’s my uncle.
I added a ternary, a map{}. and a loc() to fill it all out.
Here’s the variable set-up and the single line of code you need to solve this one:
my @string = ("Perl", "Python", "Pascal");
my $chk = "ppp";
print $chk eq join('', map { lc( substr($_, 0, 1) ) } @string ) ? "True" : "False";
Yes, the pipe operator in Elixir would make this prettier. It’s slightly frustrating to read this from the inside out, but it’s not that hard. I’ve grown used to it from years of experience.
- Map over the string
- Grab the first letter of each word
- Convert it to lower case.
- Smash the letters into one big string.
- Compare that string to the check string and print out whether they’re the same.
I know it’s a bit controversial and you shouldn’t use it that much, but I love the $_ in Perl. Yes, it is dangerous and if you’re not careful you might get unexpected results. As with all programming, if you know what’s happening, the magic isn’t so magical. It’s only useful.
Only use the magic where appropriate and it’ll work for you, not against you.
If there were multiple lines inside the map{} function there, I’d turn $_ into a named variable with something that describes better what it is. For a one-liner like this, that’s not a problem.
If we exploded out that one-liner into a series of steps, it would look something like this:
my @firsts = map{ substr($_, 0, 1) } @string;
my $str_compare = join('', @firsts);
$str_compare = lc($str_compare);
if ($str_compare eq $chk) {
print "True";
} else {
print "False";
}
The important thing to note here is that Perl’s lowercase function will lowercase the entire word. You don’t need to pass each individual letter through the lc() function to get the desired results. Logically, though, I like the way it reads. I changed it for the exploded example, but kept the lc() call per-character in my main solution.
There’s not much more to it than that, so let’s go to the second challenge.
“Build Array”
I’ll be honest, I stared at this challenge for a long time before I understood it. Thank goodness for the examples given in the write-up:
You are given an array of integers.
Write a script to create an array such that new[i] = old[old[i]] where 0 <= i < new.length.
Basically, loop over the old array. The first position’s value will tell you the position in the old array whose value you want to put in the first position of the new array. Then, you walk straight down the line and fill out the rest of the array.
The simple solution is this:
@new = map{ $old[$_] } @old;
I built everything else up around that. I made a main() and re-used the pretty_print_array() from an earlier challenge, though I did need to modify it to accept an array and not a reference to an array. I suppose there’s a lesson here about not bothering to send a reference to an array when it’s just a one parameter thing.
I’ll throw the whole thing in here next, but then I’m calling it a week.
Happy coding!
#!/usr/bin/perl
use strict;
use warnings;
main( (0, 2, 1, 5, 3, 4) );
print "\n";
main( (5, 0, 1, 2, 3, 4) );
sub main {
my @int = @_;
print "Input: ";
pretty_print_array( @int );
print "Output: ";
pretty_print_array( build_array( @int ) );
}
sub build_array {
my @int = @_;
return map{ $int[$_] } @int;
}
sub pretty_print_array {
## I copied-and-pasted this from my solution for Task 1.
## If I copy-and-paste it again, by law I need to turn
## it into a library. ;-)
my @array = @_;
my $length = scalar @array;
my $count = 1;
print "(";
foreach my $value(@array) {
print $value;
print ", " if $count < $length;
$count++;
}
print ")\n";
return;
}
That’s a lot of boilerplate for a one line piece of code, isn’t it?