The Magic Equilibrium
with Raku and Perl

by Arne Sommer

The Magic Equilibrium with Raku and Perl

[179] Published 16. April 2022.

This is my response to the Perl Weekly Challenge #160.

Challenge #160.1: Four Is Magic

You are given a positive number, $n < 10.

Write a script to generate english text sequence starting with the English cardinal representation of the given number, the word ‘is’ and then the English cardinal representation of the count of characters that made up the first word, followed by a comma. Continue until you reach four.

Example 1:
Input: $n = 5
Output: Five is four, four is magic.
Example 2:
Input: $n = 7
Output: Seven is five, five is four, four is magic.
Example 3:
Input: $n = 6
Output: Six is three, three is five, five is four, four is magic.

File: four-is-magic
#! /usr/bin/env raku

unit sub MAIN (Int $n is copy where 0 < $n < 10); # [1]

my @names =                                       # [2]
[
  "zero",
  "one",
  "two",
  "three",
  "four",
  "five",
  "six",
  "seven",
  "eight",
  "nine"
];

my @result;                                       # [3]

repeat                                            # [4]
{
  my $text   = @names[$n];                        # [5]
  my $length = $text.chars;                       # [6]

  @result.push: "$text is @names[$length]";       # [7]
  $n = $length;                                   # [8]
} while $n != 4;                                  # [9]

say tc(@result.join(", ")) ~ ", four is magic.";  # [10]

[1] A positive integer, actually. Thus in the interval 1..9.

[2] From the numbers to the textual representation. Note the first entry (for zero). It is not needed, so an empty string could also have been used.

[3] The result, as an array (so that we can join them with a comma later on, in [10]).

[4] Repeat the block as long as the condition in [9] is not met.

See docs.raku.org/syntax/repeat for more information about repeat.

[5] Get the text version of the number,

[6] and the length of that text.

[7] Add the partial result to the array.

[8] Set the next number, ready for the end condition check (in [9]) - or a new iteration.

[9] Finish the loop when we have reached the number 4.

[10] Print the result. Note the join on comma to get the requested output format. tc (title case) gives us the first letter converted to uppercase.

See docs.raku.org/routine/tc for more information about tc (title case).

Running it:

$ ./four-is-magic 9
Nine is four, four is magic.

$ ./four-is-magic 8
Eight is five, five is four, four is magic.

$ ./four-is-magic 7
Seven is five, five is four, four is magic.

$ ./four-is-magic 6
Six is three, three is five, five is four, four is magic.

$ ./four-is-magic 5
Five is four, four is magic.

$ ./four-is-magic 4
Four is four, four is magic.

$ ./four-is-magic 3
Three is five, five is four, four is magic.

$ ./four-is-magic 2
Two is three, three is five, five is four, four is magic.

$ ./four-is-magic 1
One is three, three is five, five is four, four is magic.

Looking good.

As a graph, just for fun:

A Perl Version

This is straight forward translation of the Raku version.

File: four-is-magic-perl
#! /usr/bin/env perl

use strict;
use warnings;
use feature 'say';

my $n = $ARGV[0] || "";

die "Values 1..9 only" unless $n =~ /^[1-9]$/;

my @names =
(
  "zero",
  "one",
  "two",
  "three",
  "four",
  "five",
  "six",
  "seven",
  "eight",
  "nine"
);

my @result;

do
{
  my $text   = $names[$n];
  my $length = length($text);

  push(@result, "$text is $names[$length]");
  $n = $length;
} while $n != 4;

say ucfirst(join(", ", @result)) . ", four is magic.";

Running it gives the same result as the Raku version:

$ ./four-is-magic-perl 9
Nine is four, four is magic.

$ ./four-is-magic-perl 8
Eight is five, five is four, four is magic.

$ ./four-is-magic-perl 7
Seven is five, five is four, four is magic.

$ ./four-is-magic-perl 6
Six is three, three is five, five is four, four is magic.

$ ./four-is-magic-perl 5
Five is four, four is magic.

$ ./four-is-magic-perl 4
Four is four, four is magic.

$ ./four-is-magic-perl 3
Three is five, five is four, four is magic.

$ ./four-is-magic-perl 2
Two is three, three is five, five is four, four is magic.

$ ./four-is-magic-perl 1
One is three, three is five, five is four, four is magic.

Challenge #160.2: Equilibrium Index

You are give an array of integers, @n.

Write a script to find out the Equilibrium Index of the given array, if found.

For an array A consisting n elements, index i is an equilibrium index if the sum of elements of subarray A[0…i-1] is equal to the sum of elements of subarray A[i+1…n-1].

Example 1:
Input: @n = (1, 3, 5, 7, 9)
Output: 3
Example 2:
Input: @n = (1, 2, 3, 4, 5)
Output: -1 as no Equilibrium Index found.
Example 3:
Input: @n = (2, 4, 2)
Output: 1

I have decided to accept positive integers only.

File: equilibrium-index
#! /usr/bin/env raku

unit sub MAIN (*@n where @n.elems > 0 && all(@n) ~~ /^\d+$/,  # [1]
               :v(:$verbose));

my $elems = @n.elems;                            # [2]

for 1 .. $elems - 2 -> $equilibrium              # [3]
{
  my @first  = @n[0 .. $equilibrium -1];         # [4]
  my @second = @n[$equilibrium +1 .. $elems -1]; # [5]

  say ": @first[] :: @second[]" if $verbose;

  if @first.sum == @second.sum                   # [6]
  {
    say $equilibrium;                            # [6a]
    exit;                                        # [6b]
  }
}

say -1;                                          # [7]

[1] At least one element, and they (all of them) must satisfy the regex (one or more integers). Note that zero is allowed.

[2] The number of elements.

[3] Iterate over the breaking point (the possible equilibrium), i.e. the index.

[4] Get the left hand part,

[5] and the right hand part. (Note that the equilibrium value itself is not included in either one.)

[6] Are the sums equael? If som, print the index [6a] and exit [6b].

[7] We have not found an equilibrium. Say so.

Running it:

$ ./equilibrium-index 1 3 5 7 9
3

$ ./equilibrium-index 1 2 3 4 5
-1

$ ./equilibrium-index 2 4 2
1

Looking good.

With verbose mode, where «::» is the equilibrium candidate:

$ ./equilibrium-index -v 1 3 5 7 9
: 1 :: 5 7 9
: 1 3 :: 7 9
: 1 3 5 :: 9
3

$ ./equilibrium-index -v 1 2 3 4 5
: 1 :: 3 4 5
: 1 2 :: 4 5
: 1 2 3 :: 5
-1

$ ./equilibrium-index -v 2 4 2
: 2 :: 2
1

Perl

This is a straight forward translation of the Raku version.

File: equilibrium-index-perl
#! /usr/bin/env perl

use strict;
use warnings;
use feature 'say';
use List::Util 'sum';
use Getopt::Long;
use Perl6::Junction 'all';                                        # [1]

my $verbose =  0;

GetOptions("verbose" => \$verbose);

my @n = @ARGV;

die "Positive integers only" unless @n && all(@n) == qr/^\d+$/;  # [1a]

my $elems = @n;

for my $equilibrium (1 .. $elems -2) 
{
  my @first  = @n[0 .. $equilibrium -1];
  my @second = @n[$equilibrium +1 .. $elems -1];

  say ": @first :: @second" if $verbose;

  if (sum(@first) == sum(@second))
  {
    say $equilibrium;
    exit;
  }
}

say -1;

[1] Note the useage of the Perl6::Junction module to get junction functionality. The actual syntax (at [1a]) is a little cumbersome, but everything is nicely explained in the documentation.

Running it gives the same result as the Raku version:

$ ./equilibrium-index-perl 1 3 5 6 9
3

$ ./equilibrium-index-perl 1 2 3 4 5
-1

$ ./equilibrium-index-perl 2 4 2
1

As does verbose mode:

$ ./equilibrium-index-perl -v 1 3 5 6 9
: 1 :: 5 6 9
: 1 3 :: 6 9
: 1 3 5 :: 9
3

$ ./equilibrium-index-perl -v 1 2 3 4 5
: 1 :: 3 4 5
: 1 2 :: 4 5
: 1 2 3 :: 5
-1

$ ./equilibrium-index-perl -v 2 4 2
: 2 :: 2
1

And that's it.