Perl weekly challenge 103
Here are solutions to this weeks challenges from the Perl Weekly Challenge.
You can find my full code on Github
Task 1: Chinese zodiac
You are given a year $year.
The animal cycle: Rat, Ox, Tiger, Rabbit, Dragon, Snake, Horse, Goat, Monkey, Rooster, Dog, Pig.
The element cycle: Wood, Fire, Earth, Metal, Water.
Additionally there is a two year cycle between Yin & Yang
This challenge is a relatively simple challenge - and one perl is well suited:
sub year_name {
return join q( ),
qw( Yang Yin )[ $_[0] % 2 ],
qw( Metal Water Wood Fire Earth )[ ($_[0]/2) % 5 ],
qw( Monkey Rooster Dog Pig Rat Ox
Tiger Rabbit Dragon Snake Horse Goat )[ $_[0] % 12 ];
}
As the names cycle then we can use the modulus operator %
. The the animal is a 12 year cycle, the Yin/Yang is a two year cycle (actually it's part of a 10 year cycle when you combine it with the elements).
The trick to make the code shorter is to realise you need to rotate the lists so they start with the values that year "0" would have - then it is just a simple modulus operator - rather than having to do a shift first $y%12
rather than the less clean ($x-8)%12
.
TASK #2 › What’s playing?
A slightly longer task (to read), you have a track/episode list which contains the length of each track in milliseconds. This will loop continuously. Given a start time, and an end time - which track was the last you were looking at...
sub position {
my ($start, $now, $filename ) = @_;
my $tot_duration = 0;
open my $fh, q(<), $filename;
$tot_duration += $_->[0]
foreach my @episodes = map { [split m{,?"}] } <$fh>; #"fix colour
close $fh;
my $position = 1000 * ($now-$start) % $tot_duration;
foreach( @episodes ) {
return sprintf '%s @ %02d:%02d:%02d',
$_->[1],
int( $position/3600000 ) ,
int( $position/ 60000 ) % 60,
int( $position/ 1000 ) % 60 if $position < $_->[0];
$position -= $_->[0];
}
}
The first part of the task was to slurp in csv file. Now we know the format as a file - so we cheat a little bit, we know we need to cut the string at ',"
' and '"
. Rather than load in a CSV module to do this we can just use split to do that - we
do that in one line by using map {...} <..>
.
OK - so I must admit a slight gold trick here... We can in the same statement add up the values of the lengths for each track (we will need this again shortly).
Rather than looping through until we get to the finish time, we can short cut the first loops by using the same %
modulus sign above to work out where through the list we are in the last loop. Then it is a simple loop through the episodes to work out in which one we finish.
Finally it's just a case of using sprintf
to format everything as required.
Leave a comment