Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

implement a node list in Perl

I wrote the following module but am not sure how to refer to the "last" and "head" nodes. As well as storing the address of the next node in "{nextNode}" in the previous node.

I am trying to save the reference of the class when storing it but later it's complaining: "Not a HASH reference at List.pm"; which I understand why but am not sure how the syntax would be.

If I de-reference $head and $last ($$last->{nextNode} = \$class) then I think it's using the actual name of my class; List and not the previous object like I want to.

package List;

my $head = undef;
my $last = undef;

sub new {
    my $class = shift;

    # init the head of the list
    if ($head == undef) {
    $head = \$class;
    print "updated head to:$head", "\n";
    }

    $last = \$class;
    $last->{nextNode} = \$class; # update previous node to point on this new one    

    print "updated last to:$last", "\n";
    my $self = {};
    $self->{value} = shift;    
    $self->{nextNode} = ""; # reset next to nothing since this node is last

    return bless $self, $class;
}

Thanks guys

like image 292
Yaniv Ben David Avatar asked Jan 16 '23 05:01

Yaniv Ben David


2 Answers

You should be storing $self everywhere instead of \$class. Storing $class is simply storing the name of the class, not the object itself.

Also, for $self->{nextNode} I'd store an undef instead of a blank string. Or better yet, simply don't create it at all and use exists when checking if it is there.

like image 87
Wes Hardaker Avatar answered Jan 23 '23 01:01

Wes Hardaker


You're over thinking it. If you use an array for your list instead of a hash, you don't need to worry about the head and last. The head of an array is $array[0] and the last member is $array[-1]. Simple and easy to do.

Here's a quick standard class definition for defining a list. I've only defined a constructor (the new subroutine) and one method (the list).

package Local::List;

sub new {
   my $class = shift;

   my $self = {};
   bless $self, $class;
   $self->list([]);
}

sub list {
   my $self = shift;
   my $list_ref = shift;

   if (ref $list_ref ne "ARRAY) {
       return;
   }
   if (defined $list_ref) {
       $self->{LIST} = $list_ref;
   }
   if wantarray {
      return $self->{LIST};
   }
}

The first thing: Use the same standard names everyone else uses. Use new for the constructor. When I try to look at the documentation on how to use your class, I can search for the word new and know that's how I create a class object. Also, use the variable names $class and $self. That's what everyone else does, so it's easy to know what's going on.

Notice in my new subroutine, the first item passed is the name of the class while the first item passed to my other subroutines is a reference to my class object (i.e. $self). That's probably the hardest thing to understand about classes.

Notice in new, I immediately create my $self and bless it. That way, I can call my other subroutines (my methods) to do the setting for me. This way, my constructor doesn't know how my class is structured. This has a lot of advantages:

  • When (not if) I modify my class, I don't have to modify the constructor.
  • My constructor is always in sync with all of my methods.
  • I don't have to know how my class object is structured when I start defining the class. I can start writing my class without worrying about all those dirty details on how it'll work.

Notice that the list subroutine (or method) can either set a list or return a list. It's much easier if you use the same subroutine to set or get the value. Also in your method subroutines, use a blank return when your method function returns an error. Otherwise, always return something. That makes it easy to test to see if a method failed or not.

Let's look at some of the other methods you probably want to have. Let's have all the four standard list functions:

  • push
  • pop
  • shift
  • unshift

Here's an example:

sub push {
    my $self = shift;
    my $member = shift;

    if (not defined $member) {
        return;
    }

    my $list_ref = $self->list;
    my $return = push @{ $list_ref }, $member;
    $self->list($list_ref);

    return $return;
}

Wow, that's simple. Notice that the pop doesn't know what my class looks like. It used the list method to retrieve a list reference. Then it used the builtin push method to push a member onto the list. I save that return value, and that's what I'll return. I'm not even sure what push returns. All I know is that push returns something if it succeeds. (Yes, I know it returns the number of items in the list).

The other three functions are more or less the same. Here's a few more:

  • current
  • splice
  • next
  • previous
  • head
  • last

All you need to do for current is to store the current value. Use the same function to set and get the value. Notice that my list method or my push method, or my new constructor knows or care how you store it. Nor, do our next and previous methods. All they need to do is increment or decrement the value of current and store it back using the current method subroutine:

sub next {
   my $self = shift

   my @list = $self->list;  #Returns a list;
   my $current = $self->current;
   my $list_size = $#list;

   if ($current eq $list_size) {
      return;   #Can't return a value after the end of the list!
   }

   $current++;  #Increment the value;
   my $value = $list[$current];  #I'll return this
   $self->current($current) #Store the new current
   return $value;
}

And, now to the basis of your question: Getting the last and head values of the list. Here's last

sub last {
   my $self = shift;

   my $list_ref = $self->list;
   return ${ $list_ref }[-1];
}

And a quick copy and paste will give me head:

sub head {
   my $self = shift;

   my $list_ref = $self->list;
   return ${ $list_ref }[0];
}

That's it! All that worrying you were doing was for naught.

Sorry for the long post. I just wanted to emphasize that object oriented programming in Perl isn't that tricky as long as you follow a few simple guide lines.

(Simple? What about use Moose; No, I said simple!). ;-)

like image 26
David W. Avatar answered Jan 23 '23 02:01

David W.