# The Weekly Challenge ‐ Perl and Raku

## CY's Take on The Weekly Challenge #120 ‐ Histories, Experiences, Reflections

If you want to challenge yourself on programming, especially on Perl and/or Raku, go to https://theweeklychallenge.org, code the latest challenges, submit codes on-time (by GitHub or email).

Do tell me, if I am wrong or you strongly oppose my statements!

It's time for challenges in Week #120 !

"One must have good tools in order to do a good job." ‐ Confucius

I am learning bash recently therefore the tasks were coded in both Perl and bash.

#### Task 1: Swap Odd/Even bits

I have been noticed (or even "used", just copied from Perl Cookbook) pack and unpack recently and want an opportunity to practise on them. However this task is closely related to base-4, rather than base-8 or base-16. Then I have chosen an analytical approach towards the task. I want to "show-off" on bitwise operations but what I have used is only \$ans <<= 2.

My idea is simple: get the modulo of the number modulo 4, swap odd-even bits, put the result (0, 1, 2, or 3) into a quene, \$NUMBER=int \$NUMBER/4, until the number becomes zero. The rest should be history.

The core of the Perl script:
``` sub soeb {
my \$x = \$_[0];
my @ans_arr;
while (\$x != 0) {
unshift @ans_arr, qp(\$x % 4);
\$x = int \$x / 4;
}
my \$ans = 0;
for (@ans_arr) {
\$ans <<= 2;
\$ans += \$_;
}
return \$ans;
}

```
&qp does this: 0 → 0, 1 → 2, 2 → 1, 3 → 3. The bash script:
``` while [ \$x -ne 0 ]
do
arr+=(\$(qp \$x%4))
x=\$((\$x/4))
done

ANS=0

for ((i=\$((\${#arr[@]}-1)); i>=0; i=\$i-1))
do
ANS=\$((\$ANS*4+\$[arr[\$i]]))
done

echo \$ANS

```

#### Task 2: Clock Angle

I have made "virtual" friends with programmers through The Weekly Challenge, and, as a reversal, I have recommended a friend to join the challenges and succeded!

Both sides have been rewarding. I am blessed. The story is like this. I coded the Perl scripts at Monday night, but I was a bit sceptical on the output "5 deg" for an input "11:59". As lazy as usual, I did not do pen-and-pencil check and went to bed. Eric sent me a link in Tuesday morning while I was working on the bash scripts, but I checked for the time "11:59" first: the clock angle at 11:59 should be 5.5°!

I found the buggy code is the use of the modulo operator %. In Perl, it makes every output become integer:

```\$ perl -E 'say 6.7 % 5'
1
\$ perl -E 'say 6.7 % -5'
-4
\$ perl -E 'say -6.7 % 5'
4
\$ perl -E 'say -6.7 % -5'
-1```

Trained in math, I don't get what the output of a modulo operation when floating point numbers or negative integers should be given as operands. See what happens in Java for example:

```|  Welcome to JShell -- Version 11.0.11
|  For an introduction type: /help intro

jshell> 26 % -5
\$1 ==> 1

jshell> -26 % 5
\$2 ==> -1

jshell> -26 % -5
\$3 ==> -1

jshell> 6.7 % 5
\$4 ==> 1.7000000000000002

jshell> 6.7 % -5
\$5 ==> 1.7000000000000002

jshell> -6.7 % 5
\$6 ==> -1.7000000000000002

jshell> -6.7 % -5
\$7 ==> -1.7000000000000002```

Anyway, after using a while-loop instead of the modulo, the Perl script works well. Later I even added a feature to display the clock angle of the current time (via localtime(time)) when there are no input arguments.

My idea is primitive ‐ just calculating the difference of angles:

``` #unit: degree(s) per minute
my \$hour_hand_rate = 0.5;
my \$minute_hand_rate = 6.0;

sub clock_angle {
my \$time = \$_[0];
my \$h = substr(\$time,0,2);
my \$m = substr(\$time,-2,2);
my \$deg = abs((\$h*30+\$hour_hand_rate*\$m - \$minute_hand_rate*\$m));
while (\$deg >= 360) {
\$deg-=360;
}
return \$deg > 180 ? 360-\$deg : \$deg;
}

```

Back to a linear narration. The fact that bash does NOT support floating-point operations was hit me as a bolt out of the blue. By luck(?), 360 is a "friendly" composite number, the inverse of the hour hand rate (0.5) is an integer, hence a roundabout was not hard to get. I also stole the idea of seperating the hour hand angle calculation and minute hand angle calculation from the weblink mentioned.

``` deg_h=\$(((\$h*30+\$m/\$hr_hand_inc)%360))
deg_m=\$(((\$minute_hand_rate*\$m)%360 ))
half=0
if [ \$((\$m%\$hr_hand_inc)) -eq 1 ];
then
half=5
if [ \$deg_h -gt \$deg_m ];
then
deg_h=\$((\$deg_h))
else
deg_h=\$((\$deg_h+1))
fi
fi

if [ \$deg_h -ge \$deg_m ];
then
deg=\$((\$deg_h-\$deg_m))
else
deg=\$((\$deg_m-\$deg_h))
fi

if [ \$deg -gt 180 ];
then
if [ \$half -eq 5 ];
then
deg=\$((361-\$deg))
else
deg=\$((360-\$deg))
fi
fi

echo \$deg.\$half deg

```

I had gave up using input format "hh:mm" and relied on the user entering space-seperated integers instead. That was because from the bash scripting books available on my hand, I couldn't find how to extract a substring.

I thought everything was well settled. No more ripples in still water. By luck again(?), at Tuesday night, I checked out the experience team member, Abigail's bash solution, and learnt that 01,..,07 are treated as base-8 numbers, therefore 08 and 09 would be issues for the task.

Finally in this morning, I also found a tutorial on regular expressions in bash, the script can now have the "hh:mm" as the default input. I also took the trick that Abigail mentioned. The script has been fixed and I checked it manually against all nine test cases in the corresponding Perl script.

Departing words: Well, we should not blame bash for having no floating-point-arithmetic (as well as bitwise operation) because its main purpose is in shell scripting and, as I could construe, one of the motives of the creation of Perl from Mr Larry Wall was an one-off alternative for awk, bash, sed, grep, (C? C shell?)... . (By the way awk treats every numeric variable as floating point variable.) However, the bash requirement on spacing has made me annoyed. However, if any one of the tasks next week is not heavily relying on floating point or number of lines, I am going to continue practising coding in bash (and Perl, of course).

Stay alert and healthy! □