Colin Crain › Perl Weekly Review #095

Tuesday, Feb 2, 2021| Tags: perl

( …continues from previous week. )

Welcome to the Perl review for Week 095 of the Weekly Challenge! Here we will take the time to discuss the submissions offered by the team, factor out the common methodologies that came up in those solutions, and highlight some of the unique approaches and unusual code created.


Why do we do these challenges?

I suppose any answers to that would be as wide ranging and varied as the people who choose to join the team. One thing is clear: it’s not a competition, and there are no judges, even if there is a “prize” of sorts. About that – I think of it more as an honorarium periodically awarded to acknowledge the efforts we make towards this strange goal. So there’s no determination to find the fastest, or the shortest, or even, in some abstract way, the best way to go about things, although I’m certain the individuals have their own aspirations and personal drives. As Perl is such a wonderfully expressive language, this provides quite a bit of fodder to the core idea of TMTOWTDI, producing a gamut of wonderfully varied techniques and solutions.

Even the tasks themselves are often open to a certain amount of discretionary interpretation. What we end up with is a situation where each participant is producing something in the manner they find the most interesting or satisfying. Some team members focus on carefully crafted complete applications that thoroughly vet input data and handle every use case they can think up. Others chose to apply themselves to the logic of the underlying puzzle and making it work in the most elegant way they can. Some eschew modules they would ordinarily reach for, others embrace them, bringing to light wheels perhaps invented years ago that happen to exactly solve the problem in front of them today.

I’ve been considering this question for some time and have found one binding commonality between all of us solving these challenges each week, in that however we normally live our lives, the task in front of us more than likely has nothing to do with any of that . And I think this has great value. We all do what we do, out in the real world, and hopefully we do it well. The Weekly Challenge provides a opportunity to do something germane to that life yet distinctly different; if we only do the things we already know how to do we only do the same things over and over. This is where the “challenge” aspect comes into play.

So we can consider the Weekly Challenge as providing a problem space outside of our comfort zone, as far out from comfort as we wish to take things. From those reaches we can gather and learn things and bring what we want back into our lives. Personally, I think that’s its greatest value of all.


Every week there is an enormous global collective effort made by the team, analyzing and creatively coding the submissions, and that effort deserves credit due. And that’s why I’m here, to try and figure out how to do that.

Let’s have a look and see what we can find.


For context before we begin, you may wish to revisit either of the pages for the original tasks or the summary recap of the challenge. But don’t worry, the challenges themselves will be briefly summarized, presented below as we progress from task by task. Oh, and one more thing before we get started:

Getting in Touch with Us

Email › Please email me (Colin) with any feedback, notes, clarifications or whatnot about this review.

GitHub › Submit a pull request to us for any issues you may find with this page.

Twitter › Join the discussion on Twitter! I’m always curious as to what the people think of these efforts. Everyone here at the PWC would like to hear any feedback you’d like to give.

So finally, without further ado…


•       Task 1       •       Task 2       •       BLOGS       •


TASK 1

Palindrome Number

Submitted by: Mohammad S Anwar

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

about the solutions

There were 28 submissions for the first task this past week, up a bit from the ususal lately. Perhaps the uncomplicated nature of the task boosted the turnout, combined with the similarities to another string-wise palindrome problem in an earlier challenge.

In any account there were a variety of basic methods drawn on by to decide whether or not a given number was a palindrome. There was also a fair bit of analysis as to the differences between numbers and strings in this context.

REVERSE and a STRING EQ

Ali Moradi, Arne Sommer, Bartosz Jarzyna, Duncan C. White, E. Choroba, Gustavo Chaves, James Smith, Jorg Sommrey, Laurent Rosenfeld, Lubos Kolouch, Niels van Dijke, Nuno Vieira, Paulo Custodio, Pavel Kuptsov, Pete Houston, Roger Bell_West, Simon Green, Ulrich Rieke, W. Luis Mochan, and Wanderdoc

By far the largest faction chose to treat the number as a string, reverse it and do a eq comparison. This sidesteps any considerations as to what constitutes a number, and for the most part this makes no difference.

Ali Moradi

Ali demonstrates the basic idea. There really isn’t a whole lot you need.

    my $N = $ARGV[0];
    say $N eq reverse($N) ? 1 : 0;

Don’t believe me? There’s a dozen more where that came from.

Wanderdoc

    my $rev = reverse $num;
    return ($num eq $rev ) ? 1 : 0;

Laurent Rosenfeld

    my $num = shift // 1221;
    say "$num: ", $num eq (reverse $num) ? 1 : 0;

Gustavo Chaves

    say $N eq reverse($N) ? 1 : 0;

Bartosz Jarzyna

Bartosz takes the time to make sure the input is a number, using a handy routine from Scalar::Util, but the underlying logic remains the same.

    sub palindrome_number
    {
        my ($number) = @_;

        return 0 unless looks_like_number($number);
        return 0 unless $number eq scalar reverse $number;
        return 1;
    }

… I can do this all day you know. Oh, now you believe me? Ok then, moving on…

In a slight variation a number of submissions were determined to utilize reverse in list context, necessitating a conversion to an intermediate array and back:

Paulo Custodio

    join('', reverse split(//, $N)) eq $N ? 1 : 0;

Roger Bell_West

    return (join('',reverse split '',$n) eq $n)?1:0;

Ulrich Rieke

    return $N eq join( '' , reverse split( // , $N ) ) ;

In contrast, Niels was contrarian in insisting his context to be scalar:

Niels van Dijke

As always, Niels provides us with a well-formatted descriptive output.

    my $A = shift(@ARGV) // 12210;
    my $B = reverse scalar $A;
    my $C = ($A eq $B);

    printf "Input: %d\n", $A;
    printf "Output: %d%s\n",
           $C,($C ? '' : ", since $A and $B are not the same");

ITERATE over the THINGS

Athanasius, Cheok-Yin Fung, Cristina Heredia, Flavio Poletti, and Joel Crosswhite

An alternate way of comparing for symmetry is to look at the digits individually, working inward, comparing those from the beginning to their complement from the end. Using an external iterator as an index, this could be accomplished two ways: either by breaking the number into an array of characters and addressing them that way, or by using the index value to directly examine the digits using substr.

Athanasius

The monk demonstrates the array approach, iterating across the first half of the array and comparing each to its mathematically derived mirror converging from the end.

    for my $i (0 .. int($#chars / 2))
    {
        if ($chars[$i] ne $chars[$#chars - $i])
        {
            $is_pal = 0;
            last;
        }
    }

Cristina Heredia

Christina also walks an array, only using a separate decrementing counter to find her complementary element.

    my $lastField = @array - 1;

    for (my $i = 0; $i<@array; $i++) {
        if ($array[$i] != $array[$lastField]) {
            print "Output: 0\n";
            exit;
        }
        $lastField = $lastField - 1;

    }

Using substr, of course, we can directly look into the numbers-viewed-as-strings, and so breaking the original apart isn’t strictly required.

Flavio Poletti

Flavio demonstrates the technique in a nice compact manner:

    sub is_palindrome ($s) {
       for my $i (0 .. length($s) / 2 - 1) {
          return 0 if substr($s, $i, 1) ne substr($s, -1 - $i, 1);
       }
       return 1;
    }

as does

Joel Crosswhite

Joel gives us a separate is_palindrome() routine in his Palindrome.pm module.

    printf("%s\n", (Palindrome->is_palindrome($input)) ? 1 : '`');
    foreach my $index (0..int(length($N) / 2)) {
        return 0
            if (substr($N, $index, 1) != substr($N, length($N) - 1 - $index, 1));
    }

SHIFT and POP

Adam Russell and James Smith

A destructive way to scan the digits without using iterators is to apply shift and pop on a number transformed into an array. In this way the actual elements can be held up and directly compared.

James Smith

James gives us two solutions, the first a string reverse-and-equals style as we saw above, and the second a more numerical method using shift and pop.

Of note is his use a mathematical expansion to construct his array from successive divisions to the number, rather than treating it as a string, which I find a nice touch.

    sub is_palindrome_array {
      my $n = shift;
      return 0 if $n < 0;
      my @digits  = $n%10;
      push @digits, $n%10 while $n = int ($n/10);
      while( @digits>1 ) {
        return 0 if shift @digits != pop @digits;
      }
      return 1;
    }

Adam Russell

Adam breaks his structure down into two cases — those numbers whose number of digits is evenly divisible by 2, and, well, the others:

    if(@digits % 2 == 0){
        do {
            my $a = shift @digits;
            my $b = pop @digits;
            return false if $a != $b;
        } while(@digits);
        return true;
    }
    while(@digits != 1){
        my $a = shift @digits;
        my $b = pop @digits;
        return false if $a != $b;
    };
    return true;

use a REGEX

Abigail and Colin Crain

To me it just seemed a natural thing to make a regex to match a palindrome. There is one available in the perl regular expression tutorial, but here are two others to join the party:

Abigail

Abigail has given us a recursive definition of a palindrome to guide his regex:

A recursive definition of a palindrome:

  • Empty, but followed by a digit (1)
  • A decimal dot, but followed by a digit (1)
  • A single digit
  • A digit, followed by a palindrome, followed by the same digit

and here is the result:

    /^(*sr:            # Start a script run

        (?<PAL>        # Start a group named "PAL"

           \.?(?=\d)   # Empty string, or a dot, followed by a digit

         | \d          # Or a single digit

         | (\d)        # Or a digit

           (?&PAL)     # ... followed by a palindrome

           \g{-1}      # ... and the same digit again

        )              # End of the "PAL" group

     )$/x              # End of the script run.

The *sr: is a script run assertion which guarantees that all characters within the expression matched are from the same unicode script. Mixing multiple scripts within a normal word is generally not done, and as such this is most commonly seen in URL homograph spoofing. In those cases where it is ordinary for certain scripts to be intermixed exceptions are made, but those shouldn’t apply here to our numbering systems.

I just want to add that I find recursive regular expressions fascinating.

My Own Solution

For my own solution I took the position that a leading zero is not part of the essential “number nature” of a written representaion, and thus should not be considered in evaluating whether it’s a palindrome in base-10. To this end I numify the input string by adding 0 before handing it to a regex. I’ve rolled the reversing of the capture into a pattern code expression, which is a code evaluation expression where the result is immediately inserted into the regular expression and evaluated as if it were originally written there. Obviously writing code that writes code in this way is a very powerful tool to have in one’s kit.

    my $num = shift @ARGV;
    $num += 0;

    say 1 and exit if $num =~  m/^(.*).?(??{reverse($1)})$/;
    say 0;

a note on the subject of LEADING ZEROS

An oddity in the positional numbering systems that came up a few times (and conspicuously didn’t come up in another), is the concept of the leading zero. Although not outright incorrect, the use of leading zeros on positional numbers is meaningless to the numeric identity of what is being represented, and thus is normally removed as spurious — the value being referred to remains the same number. In practice on our palindrome tests this has very little effect — 1210 is not palindromic as neither 0121 nor 121 equal the original so whether to consider the zero is moot. But given 0121 what should we do? Or 01210, for that matter?

I don’t think there is one correct answer to this conundrum. We are specifically asked about a number, not a string. As a Perl-specific question this is interesting as well because internally deep within the guts a scalar value can either be a number or a pointer to to a C-string, with the interpreter quietly converting behind the scenes as required. Under exceptional circumstances a scalar can hold a number and a string at the same time.

A leading zero is valid in positional numbering, but implicit: those positions contain no value but continue to exist, and are always there. Thus there’s generally no reason to record them, and obviously inking in an infinite number of zeros every time one wished to write out a number would render the whole system useless. The zero is unique among the digits as it is not so much a number but more of an idea of absence notated.

Further complicating the issue are cases when leading, and trailing digits behind a decimal, hold numerical meaning, such as preserving significant digits in scientific data. In these cases the extra, explicit nulls are not artifacts of textual convenience but hold real mathematical data.

So ultimately, when we are asking about palindrome numbers using a base-10 positional system, are are we being asked about a theoretical number or a physical, notational representation of that number? I don’t really know. We’re working in the hairy interface between number and text and I don’t think the line can be so easily drawn.

UNIQUE approaches and other THINGS THAT CAUGHT MY EYE

Cheok-Yin Fung, Jorg Sommrey and Lubos Kolouch

Cheok-Yin Fung

CY breaks her number into an array of digits, using an iterator to traverse the first half and compare the values to the second, walking in from the end.

    for my $i (0..int (scalar @digit / 2) ) {
        if ($digit[$i] != $digit[-$i-1]) {$n = undef; last;}
    }
    if ($n) {return 1;} else {return 0;}

This is not unlike the technique used by Athanasius, above. However from there she goes well above and beyond, using the underlying recurrence pattern to deduce the position of any palindrome found in the sequence:

    1221 is the 112th palindrome.

Nice! Her order routine calculates the count:

    sub order {
        my $a = $_[0];
        my $o;
        if ((length $a) % 2 == 1) {
            my $p = ((length $a) - 1) / 2 ;
            my $hr = substr($a, 0, $p+1);
            $o = 2 * ("9" x $p)
                + ($hr - ("1" . ("0" x ($p)) )) + 1;
        } else {
            my $r = (length $a) / 2 ;
            my $temp = "9". "0" x ( (length $a)/2 - 1 );
            my $temp2 = ( "1". "0" x ( (length $a)/2 - 1 ) ) - 1;
            $o = "9" x ( (length $a)/2 - 1 );
            $o = 2*$o + $temp - $temp2 + substr($a, 0, $r) ;
        }

        $o += 1;   # include "0" in the counting.

        return $o;
    }

Jorg Sommrey

Jorg goes the route of reversing the number as a string and checking string equality on it, but transcends the simple case, making a serious attempt to grapple with the vagaries surrounding applying what is essentially a string patterning to a number. What is a number in this context anyway?

As he explains his reasoning:

The concept of “number palindromes” needs further specifications to be well defined because “palindromic” is a string property and there is no unique string representation of a number. Following the examples I’ll take the decimal representation of a number that has to form a palindrome. Though there seems to be some consensus on considering integer numbers only, any numeric value is allowed here.

A string shall be considered as palindromic number, iff the transformation chain

numify->stringify->reverse->numify->stringify

resembles the input string. (All transformations from a Perl view.)

This sub uses different false return values to signal the failed test:

  • undef for a non-numeric input string
  • the empty string for a non-numeric reversed numified input string
  • zero for a numeric forward and backward value that is not a palindrome number

To this end he constructs a veritable battery of tests to examine every various case he can come up with, many of them quite exotic.

I find it interesting that he and I diverge on the idea of leading zeros, or perhaps it never occurred to him (a perfectly reasonable thing to do; he didn’t test fot it). In any case for him the number 0121 is not palindromic, and the number 01210 is. I would argue the opposite on both counts, as the number 0121 is just 121, and the number 01210 would be 1210. But this whole discussion is pretty fringy and strongly dependent on definitions on the input data. As I said elsewhere, I can’t see a single correct answer for this case and can see it both ways.

But that said he does do some forced numification in his solution:

    sub palindrome_number ($str) {
        return unless looks_like_number $str;
        my $reverse = reverse $str + 0;
        return '' unless looks_like_number $reverse;

        0 + ($str eq $reverse + 0);
    }

Lubos Kolouch

Finally, the last submission we will look at to grapple with the idea of leading zeros is Lubos’. In company with Jorg and myself, Lubos gives us yet another variant on the string reverse method, however choosing to cast his input explicitly as an integer:

    sub is_palindrome {
        my $what = shift;

        # int to cover cases like 00010

        return 1 if int($what) eq reverse int($what);
        return 0;
    }

Like I said, more than one way to look at it.


 

 

 

 


TASK 2

Demo Stack

Submitted by: Mohammad S Anwar

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

about the solutions

There were 24 submissions for the second task this past week. The task proved an unusual challenge to interpret, not the least because the primary functionality of a stack is already rolled into Perl’s built-in array data structure. Using push and pop on a standard Perl array gets you half-way there right out of the box, and several people took this path.

The most popular interpretation held the created Stack to be demonstrated as a defined object with methods; a thing to be held up and examined, even if internally it still used the available core functionality for its operations. Such an object would appear like the example given. Exactly how the object was constructed varied.

Then there were a few of us who seemed to consider that if we’re doing this odd task (building a stack from scratch) we should progress as though Perl dynamic arrays didn’t already do what we wanted; the spirit of the thing would be to implement the structure without them. Restricting one’s toolbox can be a very enlightening creative exercise.

use a PERL ARRAY

Abigail, Laurent Rosenfeld, and Pavel Kuptsov

Perl, of course, provides all of the functionality we need for a stack structure, so several people chose this sensible, correct route. If you needed to do this out in the world, this would be the perlish way to go.

Abigail

Abigail is having none of reinventing the wheel, delivering a fine demonstration of stack behavior from the command line. Enter push 2 and 2 gets pushed on the stack. It’s practical and functional.

    my @stack;
    while (<>) {
        if (/^push\s+(.*)/) {push @stack => $1}
        if (/^pop/)         {pop  @stack}
        if (/^top/)         {say  $stack [-1]  // $ERROR}
        if (/^min/)         {say  min (@stack) // $ERROR}
    }

Laurent Rosenfeld

Laurent uses a simple array for his stack, devising a set of subroutines to enact his functionality, passing in the stack as a reference. He uses the core push and pop, but implements his own top and minimum functions.

    sub top {
        my $stack = shift;
        return undef if is_empty $stack;
        return $stack->[-1];
    }
    sub minimum {  # assuming stack of numeric values

        my $stack = shift;
        return undef if is_empty $stack;
        my $min = $stack->[0];
        for my $item (@$stack) {
            $min = $item if $item < $min;
        }
        return $min;
    }

Pavel Kuptsov

Pavel similarly uses existing functions and methods, drawing on the core routines and min borrowed over from List::Util. He reuses the push and pop names for his subroutines, fully qualifying the internal functions as CORE::push and List::Util::min to avoid namespace collisions and avoid confusion. @stack is a package variable.

    sub push
    {
      CORE::push @stack, $_[0];
    }

    sub min
    {
      return List::Util::min @stack;
    }

use a blessed ARRAY OBJECT

Duncan C. White, E. Choroba, Flavio Poletti, Gustavo Chaves, James Smith, Jorg Sommrey, Niels van Dijke, Nuno Vieira, Paulo Custodio, Pete Houston, Roger Bell_West, and Wanderdoc

Given the example, most interpreted the model stack they were building to mean some sort of Stack object. Because the primary functionality is all based on a linear sequence of items, a straight-to-the-point start for an object would be a blessed array.

E. Choroba

Here’s Choroba with a nice compact demonstration of how straightforward the definition can be.

    {   package Stack;
        use List::Util;

        sub new  { bless [], $_[0] }
        sub push { push @{ $_[0] }, $_[1] }
        sub top  { $_[0][-1] }
        sub min  { List::Util::min(@{ $_[0] }) }
        sub pop  { die "Can't pop empty stack" unless @{ $_[0] }; pop @{ $_[0] } }
    }

All the components are there as methods, with min borrowed from List::Util and a fatal error should we try and pop a stack without anything in it. Paulo Custodio applies the same format as well, with short routines on a single line. I believe whether to throw a fatal exception or silently return undef on popping a nonexistent value is ultimately an implementation decision and can go either way, as long as it’s well defined.

Roger Bell_West

Roger lays it out a little differently, but provides the same mechanisms. min is again brought over from List::Util.

    package Local::Stack;
    use List::Util;

    sub new {
      my $class=shift;
      my $self=[];
      bless $self,$class;
    }

    sub push {
      my $self=shift;
      my $op=shift;
      push @{$self},$op;
    }

    ...

    sub min {
      my $self=shift;
      if (scalar @{$self} == 0) {
        die "insufficient elements for min\n";
      }
      return List::Util::min(@{$self});
    }

This pattern shows up again and again, for example with the submission from Gustavo Chaves, which is nearly identical. However small differences arose, such as a hand-made min function rather than importing from List::Util, as in this example from

James Smith

    sub min {
      my $self = shift;
      return unless @{$self};
      my $min = $self->[0];
      foreach (@{$self}) {
        $min = $_ if $_ < $min;
      }
      return $min;
    }

James give us an appropriately named Stack.pm module to hold his object. There were no less than three modules using this name among the submissions.

Sometimes additional functions were defined to augment those required. For instance, when importing a min from List::Util, why not implement a max at the same time?

Niels van Dijke

gives us an example of how easy this can be:

    package Stack;
    use List::Util;

    sub new {
     my ($class) = @_;

      bless [], $class;
    }

    sub push {
      my ($this, $value) = @_;

      CORE::push(@$this, $value);
    }

    ...

    sub min {
      my ($this) = @_;

      return List::Util::min(@$this);
    }

    sub max {
      my ($this) = @_;

      return List::Util::max(@$this);
    }

Other added functions seen included size, is_empty and even a more assertive empty command to just reset and flush the whole thing.

Wanderdoc

The doctor demonstrates some of these new features:

    package MyStack
    {
         use strict;
         use warnings FATAL => qw(all);

         sub new
         {
              my ($class,@data) = @_;
              bless [@data], $class;
         }

         sub push { push @{$_[0]}, $_[1] }
         sub pop  { pop @{$_[0]} }
         sub top  { return ${$_[0]}[-1] }
         sub min
         {
              my $min = ${$_[0]}[0];
              for (@{$_[0]})
              {
                   $min = $_ if $_ < $min;
              }
              return $min;
         }
         sub empty { @{$_[0]} = () }
         sub size { return scalar @{$_[0]} }

         sub print_me { print join("", "<<'", join("' '", @{$_[0]}), "'<<"), $/; }
         1;
    }

Notice that warnings are set to fatal so any attempts to pop an undefined value will result in termination. Seems a bit harsh to me, but it’s not my call.

Flavio Poletti

Flavio comes to us with no less than two new packages, a Stack object to implement the desired functionality and a VerboseStack wrapper that knows how to report what’s going on below. He then remains busy, providing us with an interactive shell to play around in the sandbox.

The Stack itself uses a blessed array as a base, with a number of subroutines to provide the functionality, much as many examples we have seen today. The VerboseStack, on the other hand, is a very interesting development. A wrapper for a Stack object that can report on the status of the stack as actions are taken, the VerboseStack adds a few functions, to echo the commands given and pretty print the contents. All other commands, though, are delegated to the internal Stack object subroutines using AUTOLOAD. If an unrecognized method is called on the VerboseStack, internally $stack->can($mname) is called on the Stack within and if the method exists there a reference to the routine is returned and executed.

AUTOLOAD is a feature we don’t see too often around here, and it can lead to some messy complexity in object inheritance, but in certain situations it can be very useful.

    package VerboseStack;
    use 5.024;
    use experimental qw< postderef signatures >;
    sub AUTOLOAD ($self, @as) {
       my ($stack, $echo) = $self->@{qw< stack echo >};
       (my $mname = our $AUTOLOAD) =~ s{\A.*::}{}mxs;
       say "\n$mname @as" if $echo;
       my $method = $stack->can($mname) or die "no method '$mname'\n";
       my @r = wantarray ? $stack->$method(@as) : scalar $stack->$method(@as);
       $self->print;
       return wantarray ? @r : defined(wantarray) ? $r[0] : ();
    }
    sub DESTROY {}
    sub echo ($s) { $s->{echo} = 1 }
    sub new ($pk, @as) { bless {echo => 1, @as, stack => Stack->new}, $pk }
    sub noecho ($s) { $s->{echo} = 0 }
    sub print ($self) {
       my $stack = $self->{stack};
       my ($n, $dump, $siz_ind) = ($stack->size, '', 'empty');
       ($dump, $siz_ind) = ("$stack\n", $n == 1 ? '1 item' : "$n items") if $n;
       print {*STDOUT} "---\n$dump------- ($siz_ind)\n";
    }
    sub stack ($self) { return $self->{stack} }

Jorg Sommrey

In an unusual and seemingly unrelated twist, Jorg also brings AUTOLOAD into the picture. His approach to delegation is a little different though, defining a %method hash of anonymous subroutines to hold his functions. When an unidentified method is called on the Stack object, AUTOLOAD is called as usual, but Jorg has decided to override the UNIVERSAL:: object method can to locate the relevant key in the %method hash and return the coderef to execute.

It’s a quite indirect way to go about this, resembling a dispatch table, but as Jorg states:

Taking this task as an opportunity to practice AUTOLOADing methods and overriding “can”. Though this is highly flexible and extensible it comes with some overhead.

The code is so intertwined it becomes difficult to extract just one piece, so here’s the whole Stack object for examination:

    package Stack;

    use Carp 'croak';
    use List::Util;

    our ($verbose, @stack, $arg);

    BEGIN {
        # The %method hash maps the name of a method to a subref providing

        # its "base" functionality.  The subs will be called with the

        # variables @stack and $arg set to the current stack and the

        # optional method argument respective.

        my %method = (
            push => sub {push @stack, $arg}, # returns new stack size

            pop => sub {pop @stack},         # returns removed element

            top => sub {$stack[$#stack]},
            min => sub {List::Util::min @stack},
            max => sub {List::Util::max @stack},
            size => sub {@stack},            # returns current stack size

            clear => sub {splice @stack},    # returns top element

        );

        # Wrapping the "base" functionality in an object.  For this purpose

        # override "can" to return an existing or generated $method.  Must

        # be available at compile time.

        sub can ($self, $method) {
            # Retrieve parent methods as well as the defined methods

            # of this class.

            my $can = $self->SUPER::can($method);
            return $can if $can;

            my $call = $method{$method};
            return unless $call;

            # Generate a method to perform the configured call.

            sub ($self, $val=undef) {
                # Provide variables and call method sub.

                local $arg = $val;
                local *stack = $self; # make @$self available as @stack

                my $result = &$call();

                # Benefit from method generation: single code for every

                # method.

                no warnings 'uninitialized';
                say "$method($val): $result" if $verbose;

                $result;
            }
        }
    }

    # Autoload dynamic methods

    our $AUTOLOAD;
    sub AUTOLOAD ($self, @args) {
        my $called = $AUTOLOAD =~ s/.*:://r;
        my $method = $self->can($called);
        croak qq{Can't locate object method "$called" via package "}
            . __PACKAGE__ . '"' unless $method;

        $self->$method(@args);
    }

    # Creates an empty Stack.

    sub new ($class) {
        bless [], $class;
    }

use some OTHER OBJECT thingy…

like a HASH…

Arne Sommer, Athanasius, Cheok-Yin Fung, and Simon Green

Objects in Perl are almost always blessed hashes — keys naturally correspond to attributes and serve to hold metadata unique to the instance. Here the dynamic data, the stack model itself, is an array value with its own key.

Athanasius

The monk gives us a thorough work-up of the problem, even making an allowance with a command line switch to either fail silently or throw a fatal exception should we try certain operations on an empty stack. His Stack.pm module holds a hash object with the actual stack model an array held under the {items} key.

The implementation of top() is interesting here. Rather than simply returning the last element of the items array, an internal counter attribute is maintained, appropriately named {top}. Starting at -1, every time an element is added or removed the counter is adjusted, so that it doubly serves as an index to the last element in the the items array. Doing things this way also allows an immediate answer to the number of items in the stack, to determine when it is empty.

    use Regexp::Common qw( number );

    sub new
    {
        my ($class, $throw) = @_;

        my  %self = ( throw => $throw // 1,
                      top   => -1,
                      items => [] );

        return bless \%self, $class;
    }

    sub push
    {
        my ($self, $item) = @_;

        $item =~ / ^ $RE{num}{real} $ /x or die qq[ERROR: "$item" is not a number];
        push @{ $self->{items} }, $item;

        ++$self->{top};
    }

    sub pop
    {
        my ($self) = @_;
        my  $item;

        if ($self->{top} >= 0)
        {
            $item = pop @{ $self->{items} };
            --$self->{top};
        }
        elsif ($self->{throw})
        {
            die "ERROR: Cannot pop() an empty stack\n";
        }
        return $item;
    }

    sub top
    {
        my ($self) = @_;
        my  $item;

        if ($self->{top} >= 0)
        {
            $item = $self->{items}->[ $self->{top} ];
        }
        elsif ($self->{throw})
        {
            die "ERROR: There is no top() on an empty stack\n";
        }
        return $item;
    }

Arne Sommer

Arne gives us a proper demonstration in his submission, providing a shell prompt where the user can input commands, such as push 35, pop or min. The state of the stack can be viewed at any time by typing all, which prints an arrow-connected list from bottom to top.

The actual implementation is done within a blessed hash Stack object, in an array stored as {values}. There is one little oddity in interpretation I have to bring up, and that is over the meaning of the top operation. Defined only as “get the top element” this is, perhaps surprisingly, apparently ambiguous. In any case Arne has deliberately chosen to deliver unto us the first element added, what might commonly be considered the “bottom”, rather than the last element. Relative terms, like left and right, or physical terms applied to abstract ideas are never as well defined as we think they are and always ripe for miscommunication. A physical stack of plates intrinsically has a “top” plate, but a data structure we have skeuomorphically titled a “stack” does not. It’s an important thing to remember in communicating ideas.

With this caveat I’m going to let this one go.

    my $stack = stack->new();

    my $input;

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

      if    ($input =~ /^push\s+(.*)$/)            { $stack->push($_) 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; }
    }

or a MODULE…

Adam Russell, Lubos Kolouch and W. Luis Mochan

The cpan module object frameworks were also represented, as might be expected.

Adam Russell

Adam draws on Class::Struct, which documents itself as able to “declare struct-like datatypes as Perl classes”. The actual stack is in a field called data, with a pointer to a Perl array (a reference, of course, in Perl, but as we’re in the land of C it thought it best to use the language of the locals).

Having the module do the housekeeping makes for a compact design:

    package StackAdam {
        use boolean;
        use Class::Struct;

        struct(
            data => q/@/
        );

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

        sub pop{
            my($self, $n) = @_;
            pop @{$self->data()};
        }

        sub top{
            my($self, $n) = @_;
            @{$self->data()}[@{$self->data()} - 1];
        }

        sub min{
            my($self, $n) = @_;
            my @sorted = sort {$a <=> $b} @{$self->data()};
            return $sorted[0];
        }
        true;
    }

W. Luis Mochan

Luis delivers us a fully featured stack demonstration in an interactive shell, with a prompt for command inputs. He creates his masterpiece using the Moo framework.

The four required functions apparently proved insufficient to Luis’ liking, so he adds a veritable raft of extra functionality: exch swaps the two top items; arithmetic operations pop off the two top items, compute and and push back on the result; a “change sign” operation multiplies the top value by -1; and he even gives us an inv 1/x function.

    use Scalar::Util qw(looks_like_number);
    my $s=Stack->new;
    while(<>){
        chomp;
        $s->push($_), next if looks_like_number($_);
        $s->pop, next if lc $_ eq "pop";
        $s->top, next if lc $_ eq "top";
        $s->exch, next if lc $_ eq "exch";
        $s->min, next if lc $_ eq "min";
        $s->max, next if lc $_ eq "max";
        $s->add, next if $_ eq "+";
        $s->sub, next if lc $_ eq "-";
        $s->mul, next if lc $_ eq "*";
        $s->div, next if lc $_ eq "/";
        $s->cs, next if lc $_ eq "cs"; # change sign

        $s->inv, next if lc $_ eq "inv"; # invert

        $s->show, next if lc $_ eq "show";
        die "Unrecognized op";
    }

Here are a few example subs to get an idea of the implementation:

    sub push {
        my $self=shift;
        my $x=shift;
        say "Push:\t$x";
        my $s=$self->_stack;
        push @$s, $x;
        $self->show;
    }
    sub exch {
        my $self=shift;
        my $x=$self->pop;
        my $y=$self->pop;
        say "Exch:\t$x <-> $y";
        $self->push($x);
        $self->push($y);
    }
    sub mul {
        my $self=shift;
        my ($x, $y)=($self->pop,$self->pop);
        my $res=$x*$y;
        say "Mul:\t$x * $y -> $res";
        $self->push($res);
    }
    sub cs {
        my $self=shift;
        my $x=$self->pop;
        my $res=-$x;
        say "CS:\t$x -> $res";
        $self->push($res);
    }

It’s quite involved, so much so that I’d rather it would default to a usage directive rather than dying on finding an unrecognized input. Overall I find it just the kind of over-the-top ridiculousness that brightens my day around here.

Lubos Kolouch

Lubos brings us a full-blown Moose offering, which makes the implementation quite compact. The stack itself is held in an array under the values attribute. Of notable difference is that the actual stack data is ordered from last to first, so that new items are placed on the list using unshift and removed using shift. This keeps track of the the top of the stack, as it is always located at index 0.

When Perl creates a new array data structure, internally it uses an AV typedef, which holds metadata about the array and a pointer to a C array of scalars (SVs) that holds the data. When memory for the array of scalars is allocated, Perl always tries to add a little extra on either end of the underlying C array and notes the bounds at both the beginning and the end. Hence Perl arrays are intrinsically dynamic.

As such, unlike many languages, there is no penalty for using unshift, as it only involves assigning to the lower end of this array, into existing memory space, and moving a pointer, xav_array.

So keeping the top of the stack at index 0 is arguably not just okay, but a better way to do it, as it handles the bookkeeping.

    package Stack;
    use Moose;
    use List::Util qw/min/;
        has 'values' => (is => 'rw', isa=>'ArrayRef', default=>sub{ [] });

        sub push {
            my $self = shift;
            my $what = shift;
            unshift @{$self->values}, $what;
        }

        sub pop {
            my $self = shift;
            my $what = shift;
            return shift @{$self->values} if @{$self->values};
            return;
        }

        sub top {
            my $self = shift;
            return ${$self->values}[0] if @{$self->values};
            return;
        }

        sub stack_min {
            my $self = shift;
            return min(@{$self->values});
        }
    }

AVOID using an ARRAY at ALL

Bartosz Jarzyna and Colin Crain

Bartosz Jarzyna

First, have a look at this:

    use Scalar::Util qw(refaddr blessed);

Bartosz does something really interesting and truly different with both the task and his Stack object, which does not keep the items in the stack in an internal array. Rather, on calling push to add a new item, a new instance of the Stack object itself is created with that item at the top, essentially creating a nested list structure that operates as a linked list. The linkage is implicit to the nested structure and the values are held elsewhere in a lookup hash, keyed on a integer representation of the relevant node reference provided by Scalar::Util::refaddr(). The layers of indirection make it complex yet sublime.

Peaking inside with Data::Dumper after adding three items you can see the nested structure:

    $VAR1 = bless(
                do{\(my $o = bless(
                    do{\(my $o = bless(
                        do{\(my $o = bless(
                            do{\(my $o = undef)}, 'Stack' ))}, 'Stack' ))}, 'Stack' ))}, 'Stack' );

Here are his new and push routines, and you can see how the new instances are created.

    sub new {
        my ($self) = @_;
        my $package = blessed $self || $self;
        my $last = blessed $self ? $self : undef;

        return bless \$last, $package;
    }

    sub push {
        my ($self, $value) = @_;

        my $new = $self->new;
        addval($new, $value);
        $_[0] = $new;
        return;
    }

My Own Solution

For my own solution I chose to do things he hard way, as I often do. Given that Perl already has quite capable dynamic arrays with built-in functions to mimic a stack, there seemed little challenge in using those excellent tools to just get the job done and move on. No, that would be too easy by far — too sensible. So instead I pretended those things didn’t exist and built my stack as one might do in a lower-level language, using a linked list. On the other hand, I did draw on the Moo framework for my object model, because frankly I enjoy using it. As Emerson said: “Consistency is the hobgoblin of small minds”.

So after whipping up some simple Moo objects for a Stack and each Node within it, with each newly added item linking to the previous we can produce all of the functionality required. All we need for bookkeeping is to keep track of the top node.

push becomes adding and linking in a new top node. pop removes the top node, returning its value, and setting its link to be the new top. top returns the value of the top node.

min only requires a single traversal from top to bottom, looking at the values at each node and updating a running minimum value. The whole thing is implemented without using an array nor any of the relevant inbuilt functions.

    sub min {
        my $self = shift;
        my $node = $self->last;
        my $min  = $node->value;
        while ( defined $node->down ) {
            $min = $node->down->value if $node->down->value < $min;
            $node = $node->down;
        }
        return $min;
    }

Make no mistake this is not be best way to do this, nor even particularly Perlish. But it is likely to be reasonably efficient and serves as a good study, bringing a new and entirely unrelated meaning to the term “constraint programming”.


BLOGS


That’s it for me this week, people! Warped by the rain, driven by the snow, resolute and unbroken by the torrential influx, I somehow continue to maintain my bearings. Looking forward to next wave, the perfect wave, I am: your humble servant.

But if Your THIRST for KNOWLEDGE is not SLAKED,

then RUN (dont walk!) to the WATERING HOLE

and READ these BLOG LINKS:

( don’t think, trust your training, you’re in the zone, just do it … )

Aaron Smith

Abigail

Adam Russell

Arne Sommer

Colin Crain

Flavio Poletti

James Smith

Kang-min Liu

Laurent Rosenfeld

Luca Ferrari

Roger Bell_West

Simon Green

W. Luis Mochan

SO WHAT DO YOU THINK ?

If you have any suggestions or ideas then please do share with us.

Contact with me