Palindromic Stack
with Raku and Perl

by Arne Sommer

Palindromic Stack with Raku and Perl

[111] Published 16. January 2021.

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

Challenge #095.1: Palindrome Number

You are given a number $N.

Write a script to figure out if the given number is Palindrome. Print 1 if true otherwise 0.

Example 1:
Input: 1221
Output: 1
Example 2:
Input: -101
Output: 0, since -101 and 101- are not the same.
Example 3:
Input: 90
Output: 0

Note that the challenge does not actually say how we should treat non-numeric values. I have chosen to let the program reject them, but printing 0 could be an alternative.

Multiple dispatch is fun, so let us have a go at it:

File: palindrome-number-multi
#! /usr/bin/env raku

multi sub MAIN (Numeric $N where $N >= 0 && $N eq $N.flip)  # [1]
{
  say 1;
}

multi sub MAIN (Numeric $N)                                 # [2]
{
  say 0;
}

[1] This version is used if the input parameter is numeric (the Numeric type), it is positive (see the second example above), and the number equals itself if we reverse it (with flip). Note the string comparison (with eq), so that we keep the reversed value as is.

[2] All other numbers (after [1] has declined them) results in a 0.

Running it on the examples:

$ ./palindrome-number-multi 1221
1

$ ./palindrome-number-multi -101
0

$ ./palindrome-number-multi 90
0

$ ./palindrome-number-multi 0110
1

Complex numbers do not work:

$ ./palindrome-number-multi 1+2i
Cannot convert 1+2i to Real: imaginary part not zero
  in sub MAIN at ./palindrome-number-multi line 3
  in block <unit> at ./palindrome-number-multi line 3

We do string comparison, but prior to that the comparison with zero coerces the number to a Real - which fails. The remedy is removing that comparison, as the string comparison takes care of negative numbers for us.

This time, using a ternary if:

File: palindrome-number-if
#! /usr/bin/env raku

unit sub MAIN (Numeric $N);

say $N eq $N.flip            # [1]
  ?? 1
  !! 0;

[1] The ternary if, whith a single say up front.

$ ./palindrome-number-if 1+2i
0

The ternary if is actually redundant. We can coerce the Boolean value to Numeric value (with the Prefix + Operator), and save some code:

File: palindrome-number
#! /usr/bin/env raku

unit sub MAIN (Numeric $N);

say + ($N eq $N.flip);  # [1]

[1] Ensure a numeric value (instead of True and False).

See docs.raku.org/routine/+ for more information about the Prefix Operator +.

Boolean values are built in, but are implemented as an enum (enumeration). You can inspect the real (or numeric, rather) values like this:

> Bool.enums
Map.new((False => 0, True => 1))

See docs.raku.org/language/typesystem#enum for more information about the enum type.

See docs.raku.org/routine/enums for more information about the enums method.

A Perl Version

This is straight forward translation of «palindrome-number».

File: palindrome-number-perl
#! /usr/bin/env perl

use strict;
use warnings;
use feature 'say';
use Scalar::Util qw(looks_like_number);           # [1]

my $N = $ARGV[0] // die "Specify a number";

die "Not a number" unless looks_like_number($N);  # [2]

say 0 + ($N eq reverse($N));                      # [3]

[1] Deciding if a value is numeric is hard. But the «Scalar::Util» module is helpful.

[2] Complain for non-numeric values.

[3] Ensure that we get a 0 for non-palindromic values by adding zero. (As we will get an empty string otherwise.)

Running it gives the same result as the Raku version, except for complex numbers (but that is probably ok, as the yare not built in):

$ ./palindrome-number-perl 1221
1

$ ./palindrome-number-perl -101
0

$ ./palindrome-number-perl 90
0

$ ./palindrome-number-perl AA
1

$ ./palindrome-number-perl 1+2i
Not a number at ./palindrome-number-perl line 9.

Challenge #095.2: Demo Stack

Write a script to demonstrate Stack operations like below:

push($n) - add $n to the stack
pop() - remove the top element
top() - get the top element
min() - return the minimum element

Example:
my $stack = Stack->new;
$stack->push(2);
$stack->push(-1);
$stack->push(0);
$stack->pop;       # removes 0
print $stack->top; # prints -1
$stack->push(0);
print $stack->min; # prints -1

It is boring to duplicate the example code, so I have written an interactive program where the user specifies the commands.

File: demo-stack
#! /usr/bin/env raku

unit sub MAIN;

class stack
{
  has @.values is rw;        # [1]

  method push ($value)       # [2]
  {
    @.values.push: $value;
  }

  method pop                 # [3]
  {
    return @.values.pop;
  }
  
  method top                # [4]
  {
    return @.values[0];
  }
  
  method min                # [5]
  {
    return @.values.min;
  }

  method all                # [6]
  {
    return @.values.join(" -> ");
  }
}

my $stack = stack.new;      # [7]

loop                        # [8]
{
  given prompt 'stack> '    # [9]
  {
    when /^push\s+(.*)$/  { $stack.push: $_ for $0.Str; }   # [2a]
    when 'pop'            { $stack.pop; }                   # [3a]
    when 'top'            { say $stack.top; }               # [4a]
    when 'min'            { say $stack.min; }               # [5a]
    when 'all'            { say $stack.all; }               # [6a]
    when 'exit' | 'quit'  { exit; }                         # [10]
  }

[1] Just one element in the class, the stack.

[2] A method to add one element. Note the trailing .Str in [2a] to coerce the match object to a string.

[3] A method to remove one element. The method returns the elment, but the code in [3a] does not print it.

[4] A method to show the top eleement on the stack.

[5] A method to show the smallest element on the stack. Note that this will fail if we add non-numeric values to the stack - which is permissible.

[6] A method to show the content of the stack. The top is to the right. This method is not specified in the challenge, but I have added it as it is useful for debugging.

[7] Set up an initial (empty) stack.

[8] An eternal loop. Note the exit strategy (in [10]).

[9] Print the text and wait for user input.

[10] We need an exit strategy, and these commands fit the bill. Using Control-C works as well.

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

Running it:

$ ./demo-stack
stack> push 2
stack> push -1
stack> push 0
stack> all
2 -> -1 -> 0
stack> pop
stack> top
2
stack> push 0
stack> min
-1
stack> all
2 -> -1 -> 0
stack> quit

Looking good.

We can support several values in one «push»:

File: demo-stack-list (changes only)
    when /^push\s+(.*)$/  { $stack.push: $_ for $0.words; }

Running it:

$ ./demo-stack-list
stack> push 1 2 3 4
stack> all
1 -> 2 -> 3 -> 4
stack> quit

Perl

This is a straight forward translation of «demo-stack-list», but requires about 50% more code due to the unique (not meant as a compliment) object system in Perl.

File: demo-stack-perl
#! /usr/bin/env perl

use strict;
use warnings;
use feature 'say';
use List::Util;

package stack 
{  
  sub new
  {
    my $class = shift;
    my $self  = { 'values' => [] };

    bless $self, $class;
    return $self;
  }

  sub push
  {
    my ($self, $value) = @_;
    push(@{$self->{values}}, $value);
  }

  sub pop
  {
    my $self = shift;
    return pop(@{$self->{values}});
  }
  
  sub top
  {
    my $self = shift;
    return ${$self->{values}}[0];
  }
  
  sub min
  {
    my $self = shift;
    my @values = @{$self->{values}};
    
    return List::Util::min @values;
  }

  sub all
  {
    my $self = shift;
    return join(" -> ", @{$self->{values}});
  }
}

my $stack = stack->new();

my $input;

while (1)
{
  print 'stack> '; $input = ; chomp($input);

  if    ($input =~ /^push\s+(.*)$/)            { $stack->push($_)       # [1]
                                                   for split(/\s+/, $1); }
  elsif ($input eq 'pop')                      { $stack->pop; }
  elsif ($input eq 'top')                      { say $stack->top; }
  elsif ($input eq 'min')                      { say $stack->min; }
  elsif ($input eq 'all')                      { say $stack->all; }
  elsif ($input eq 'exit' || $input eq 'quit') { exit; }
}

[1] Note that the first match is at $1, and not $0 as in Raku.

Running it gives the same result as the Raku version:

./demo-stack-perl
stack> push 2 -1 0
stack> all
2 -> -1 -> 0
stack> pop
stack> top
2
stack> push 0
stack> min
-1
stack> all
2 -> -1 -> 0
stack> exit

I used «exit» instead of «quit» this time.

And that's it.