This is my response to the Perl Weekly Challenge #095.
$N
.
Palindrome
. Print
1
if true otherwise 0
.
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:
#! /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.
#! /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.
Stack
operations like below:
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
#! /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.