Perl Weekly Challenge 379‘s tasks are “Reverse String” and “Armstrong Number”.
So, I think for music this week, I’ll give you a number by Armstrong.1
Task 1: Reverse String
You are given a string.
Write a script to reverse the given string without using standard reverse function.
Input: $str = ""
Output: ""
Input: $str = "reverse the given string"
Output: "gnirts nevig eht esrever"
Input: $str = "Perl is Awesome"
Output: "emosewA si lreP"
Input: $str = "v1.0.0-Beta!"
Output: "!ateB-0.0.1v"
Input: $str = "racecar"
Output: "racecar"
Approach
Ah! Since I can’t use the builtin functions, there’s a couple ways we can do this: split the string into characters, manipulate the array, and then join it back into a string, or pull the characters off the string one at a time and append them to a new string.
Raku
For Raku, I did a mix: split the string into characters, but then build a reversed string as I process the characters.
sub my_reverse($str) {
my $rts = "";
for $str.comb -> $char {
$rts = $char ~ $rts;
}
$rts;
}View the entire Raku script for this task on GitHub.
$ raku/ch-1.raku
Example 1:
Input: $str = ""
Output: ""
Example 2:
Input: $str = "reverse the given string"
Output: "gnirts nevig eht esrever"
Example 3:
Input: $str = "Perl is Awesome"
Output: "emosewA si lreP"
Example 4:
Input: $str = "v1.0.0-Beta!"
Output: "!ateB-0.0.1v"
Example 5:
Input: $str = "racecar"
Output: "racecar"Perl
And the same in Perl.
sub my_reverse($str) {
my $rts = "";
foreach my $char (split //, $str) {
$rts = $char . $rts;
}
$rts;
}View the entire Perl script for this task on GitHub.
Python
Python, makes it really easy to loop over a string character by character.
def my_reverse(string):
gnirts = ""
for char in string:
gnirts = char + gnirts
return gnirtsView the entire Python script for this task on GitHub.
Elixir
And the same thing in Elixir
def my_reverse(str) do
String.codepoints(str)
|> Enum.reduce("", fn char, rts -> char <> rts end)
endView the entire Elixir script for this task on GitHub.
Task 2: Armstrong Number
You are given two integers, $base and $limit.
Write a script to find all Armstrong numbers in base $base that are less than $limit.
If raising each of the digits of a nonnegative integer to the power of the total number of digits, then taking the sum, equals the original number, it is an Armstrong number.
Input: $base = 10, $limit = 1000
Output: (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 153, 370, 371, 407)
Input: $base = 7, $limit = 1000
Output: (0, 1, 2, 3, 4, 5, 6, 10, 25, 32, 45, 133, 134, 152, 250)
Input: $base = 16, $limit = 1000
Output: (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 342, 371, 520, 584, 645)
Approach
This took me a moment to grok. It’s obvious that for a single digit number n, n1 will be n. So I looked at 153: 110. Ahh! But I have to worry about the numbers being in different bases, although all the output appears to be in base10.3 + 5103 + 3103 = 15310
So, to get the digits in whatever base we’re in, we do modulo division of the number by the base to get the least significant digit, and then we do an integer division (DIV) of the number by the base to discard that least significant digit and shift everything right, and we repeat until the number is 0. As a special case, if the number is 0 to begin with, the number has one digit: 0.
Edit: Oriel Jutty pointed out that I misread the requirement that the solution only produce output strictly less than the limit. My bad!
Raku
In Raku, the operators we need are %, div, and **.
sub digits($base, $num is copy) {
return [0] if $num == 0; # base case
my @digits;
while ($num > 0) {
@digits.unshift($num % $base);
$num = $num div $base;
}
@digits;
}
sub armstrong($base, $limit) {
my $n = 0;
my @armstrong;
while ($n <= $limit) {
my @dig = digits($base, $n);
my $pow = @dig.elems;
my $n2 = @dig.map({ $_ ** $pow }).sum;
@armstrong.push($n) if $n == $n2;
$n++;
}
@armstrong
}View the entire Raku script for this task on GitHub.
$ raku/ch-2.raku
Example 1:
Input: $base = 10, $limit = 1000
Output: (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 153, 370, 371, 407)
Example 2:
Input: $base = 7, $limit = 1000
Output: (0, 1, 2, 3, 4, 5, 6, 10, 25, 32, 45, 133, 134, 152, 250)
Example 3:
Input: $base = 16, $limit = 1000
Output: (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 342, 371, 520,
584, 645)Perl
Perl has % and **, but it doesn’t have a div, so we have to use a combination of / and int.
use List::AllUtils qw( sum );
sub digits($base, $num) {
return (0) if $num == 0; # base case
my @digits;
while ($num > 0) {
unshift @digits, ($num % $base);
$num = int($num / $base);
}
@digits;
}
sub armstrong($base, $limit) {
my $n = 0;
my @armstrong;
while ($n <= $limit) {
my @dig = digits($base, $n);
my $pow = @dig;
my $n2 = sum map { $_ ** $pow } @dig;
push @armstrong, $n if $n == $n2;
$n++;
}
@armstrong
}View the entire Perl script for this task on GitHub.
Python
In Python, the operators we need are %, // (floor division), and **. Because it’s difficult to push new digits on the beginning of a list, I’m appending them to the end and then just reversing the list.
def digits(base, num):
if num == 0: return [0]
digits = []
while num > 0:
digits.append( num % base )
num = num // base
return digits[::-1] # reverse them
def armstrong(base, limit):
n = 0
armstrong = []
while n <= limit:
dig = digits(base, n)
pow = len(dig)
n2 = sum([ d ** pow for d in dig ])
if n == n2: armstrong.append(n)
n += 1
return armstrongView the entire Python script for this task on GitHub.
Elixir
But Elixir lets me cheat using the Integer.digits/2 function, which returns the ordered digits for the given integer in base base. So all I have to do is check to see if it’s an Armstrong number or not.
def armstrong(_, limit, n, armstrong) when n > limit,
do: armstrong
def armstrong(base, limit, n, armstrong) do
dig = Integer.digits(n, base)
pow = length(dig)
n2 = Enum.reduce(dig, 0, fn d, n2 ->
n2 + (d ** pow)
end)
armstrong = if n == n2 do
armstrong ++ [n]
else
armstrong
end
armstrong(base, limit, n+1, armstrong)
end
def armstrong(base, limit) do
armstrong(base, limit, 0, [])
endView the entire Elixir script for this task on GitHub.
Here’s all my solutions in GitHub: https://github.com/packy/perlweeklychallenge-club/tree/challenge-379-packy-anderson/challenge-379/packy-anderson
- The song was written by Bob Thiele and George David Weiss, but it was first recorded by Louis Armstrong. ↩︎