Reverse Power
with Raku

by Arne Sommer

Reverse Power with Raku

[274] Published 30. January 2024.

This is my response to The Weekly Challenge #254.

Challenge #254.1: Three Power

You are given a positive integer, $n.

Write a script to return true if the given integer is a power of three otherwise return false.

Example 1:
Input: $n = 27
Output: true

27 = 3 ^ 3
Example 2:
Input: $n = 0
Output: true

0 = 0 ^ 3
Example 3:
Input: $n = 6
Output: false
File: three-power
#! /usr/bin/env raku

unit sub MAIN ($n is copy where $n ~~ UInt, :v($verbose));  # [1]

while $n > 3                                                # [2]
{
  print ": $n / 3 -> " if $verbose;
  $n /= 3;                                                  # [3]
  say $n if $verbose;

  last if $n != $n.Int;                                     # [4]
}

say $n == 0|3 ?? 'true' !! 'false';                         # [5]

[1] Note the is copy so that we can change the value later on (in [3]). Also note that we check the value against the Unsigned Int type UInt instead of enforcing the type on the variable, as the value in $n can become a non-integer (in [3]).

[2] As long as we have more 3s to extract (by factoring them out).

[3] Divide the value by three, and assign the value back. This may result in a non-integer.

[4] This check will bail out of the loop if we got a non-integer value. This will spare us for a lot of loop iterations in the worst case, at the cost of adding code (and time). Feel free to comment it out.

[5] Are we left with 3 (or the special case of 0, for the input value 0), if so we have succeeded and print «true». If not, print «false».

Running it:

$ ./three-power 27
true

$ ./three-power 0
true

$ ./three-power 6
false

Looking good.

With verbose mode:

$ ./three-power -v 27
: 27 / 3 -> 9
: 9 / 3 -> 3
true

$ ./three-power -v 0
true

$ ./three-power -v 6
: 6 / 3 -> 2
false

Let us try an arbitrary largish number. First with the check in [4] intact, and then without it:

$ ./three-power -v 999
: 999 / 3 -> 333
: 333 / 3 -> 111
: 111 / 3 -> 37
: 37 / 3 -> 12.333333
false

$ ./three-power -v 999
: 999 / 3 -> 333
: 333 / 3 -> 111
: 111 / 3 -> 37
: 37 / 3 -> 12.333333
: 12.333333 / 3 -> 4.111111
: 4.111111 / 3 -> 1.37037
false

Why restrict ourselves with the power of 3?

File: multi-power
#! /usr/bin/env raku

unit sub MAIN ($n is copy where $n ~~ UInt,
               UInt :p(:$power) where $power > 0 = 3,
               :v(:$verbose));

while $n > $power
{
  print ": $n / $power -> " if $verbose;
  $n /= $power;
  say $n if $verbose;
  last if $n != $n.Int;
}

say $n == 0|$power ?? 'true' !! 'false';

Specify the power as e.g. -p=2 or -power=2, if the default 3 does not suit you.

$ ./multi-power -power=2 512
true

$ ./multi-power -power=2 -v 512
: 512 / 2 -> 256
: 256 / 2 -> 128
: 128 / 2 -> 64
: 64 / 2 -> 32
: 32 / 2 -> 16
: 16 / 2 -> 8
: 8 / 2 -> 4
: 4 / 2 -> 2
true

Challenge #254.2: Reverse Vowels

You are given a string, $s.

Write a script to reverse all the vowels (a, e, i, o, u) in the given string.

Example 1:
Input: $s = "Raku"
Output: "Ruka"
Example 2:
Input: $s = "Perl"
Output: "Perl"
Example 3:
Input: $s = "Julia"
Output: "Jaliu"
Example 4:
Input: $s = "Uiua"
Output: "Auiu"
File: reverse-vowels
#! /usr/bin/env raku

unit sub MAIN ($s, :v(:$verbose));                   # [1]

my @letters = $s.comb;                               # [2]
my @vowels  = @letters.grep: * ~~ /<[aeiouAEIOU]>/;  # [3]
my @reverse = @vowels.reverse;                       # [4]

if $verbose
{
  say ":Letters: { @letters.join(",") }";
  say ":Vowels: { @vowels.join(",") }";
  say ":Reverse: { @reverse.join(",") }";
}

my @output = @letters.map({ m:i/<[aeiou]>/ ?? @reverse.shift !! $_ }); # [5]

say @output.join;                                                      # [6]

[1] A string of arbitrary size (and content).

[2] Get the individual letters.

[3] Get the vowels. Note the «ignore case» :i modifier, instead of an explicit list of uppercase vowels in the regex.

[4] Reverse the list of vowels.

[5] Map each letter in the input (the list) to either itself (non-vowel) or the first remaing (unused) reversed list of vowels.

[6] Print the result as a single string.

Running it:

$ ./reverse-vowels Raku
Ruka

$ ./reverse-vowels Perl
Perl

$ ./reverse-vowels Julia
Jaliu

$ ./reverse-vowels Uiua
auiU

The last one is not as specified in the challenge, but let us have a go at verbose mode before looking into that.

$ ./reverse-vowels -v Raku
:Letters: R,a,k,u
:Vowels: a,u
:Reverse: u,a
Ruka

$ ./reverse-vowels -v Perl
:Letters: P,e,r,l
:Vowels: e
:Reverse: e
Perl

$ ./reverse-vowels -v Julia
:Letters: J,u,l,i,a
:Vowels: u,i,a
:Reverse: a,i,u
Jaliu

$ ./reverse-vowels -v Uiua
:Letters: U,i,u,a
:Vowels: U,i,u,a
:Reverse: a,u,i,U
auiU

Ok. Let us try a palindrome:

$ ./reverse-vowels "never odd or even"
never odd or even

$ ./reverse-vowels -v "never odd or even"
:Letters: n,e,v,e,r, ,o,d,d, ,o,r, ,e,v,e,n
:Vowels: e,e,o,o,e,e
:Reverse: e,e,o,o,e,e
never odd or even

$ ./reverse-vowels "NEVER ODD or even"
NeVeR oDD Or EvEn

$ ./reverse-vowels -v "NEVER ODD or even"
:Letters: N,E,V,E,R, ,O,D,D, ,o,r, ,e,v,e,n
:Vowels: E,E,O,o,e,e
:Reverse: e,e,o,O,E,E
NeVeR oDD Or EvEn

Then we can have a go at the case of the wrong case, so to speak...

File: reverse-vowels-case
#! /usr/bin/env raku

unit sub MAIN ($s, :v(:$verbose));

my @letters = $s.comb;
my @vowels  = @letters.grep: * ~~ /<[aeiouAEIOU]>/;
my @reverse = @vowels.reverse;

if $verbose
{
  say ":Letters: { @letters.join(",") }";
  say ":Vowels: { @vowels.join(",") }";
  say ":Reverse: { @reverse.join(",") }";
}

my @output = @letters.map({
	       if    /<[aeiou]>/ { @reverse.shift.lc; } # [1]
	       elsif /<[AEIOU]>/ { @reverse.shift.uc; } # [2]
	       else	         { $_;                }
             });

say @output.join;

[1] If the original vowel is lowercase, apply lc to force the replacement vowel to lowercase as well.

See docs.raku.org/routine/lc for more information about lc.

[2] If the original vowel is uppercase, apply uc to force the replacement vowel to uppercase as well.

See docs.raku.org/routine/uc for more information about uc.

And magically nothing seems to happen...

$ ./reverse-vowels-case "NEVER ODD or even"
NEVER ODD or even

Verbose mode will tell you otherwise:

$ ./reverse-vowels-case -v "NEVER ODD or even"
:Letters: N,E,V,E,R, ,O,D,D, ,o,r, ,e,v,e,n
:Vowels: E,E,O,o,e,e
:Reverse: e,e,o,O,E,E
NEVER ODD or even

Another one:

$ ./reverse-vowels-case -v "THIS IS not a test"
:Letters: T,H,I,S, ,I,S, ,n,o,t, ,a, ,t,e,s,t
:Vowels: I,I,o,a,e
:Reverse: e,a,o,I,I
THES AS not i tist

Ind that's at. (Pun intended)