Singly: Perl Challenge #71 Redux
Thank you, Walt Mankowski! In response to my first pass, he wrote:
Nice writeup! In part 1, I missed the part where the random numbers were supposed to be unique. In part 2, it looks like you missed the part about it being a singly linked list. π
Oh, smell.
A doubly-linked list looks like:
ββββββββ ββββββββ ββββββββ
β node β --next--> β node β --next--> β node β
β β <--prev-->β β <--prev-->β β
ββββββββ ββββββββ ββββββββ
There are links going both forward to the next node and back to the previous. With a singly-linked list, like they said in the movie Gumball Rally, βWhatβs behind us? Not important!β
ββββββββ ββββββββ ββββββββ
β node β --next--> β node β --next--> β node β
ββββββββ ββββββββ ββββββββ
Thereβs not much I was doing with the previous. The think you could do with prev links is to go to the start and count backwards, which I donβt do.
Most of the changes to my Node class are simple, just comment out anything mentioning prev
or parent
.
package Node;
sub new ( $class, $value = 0 ) {
my $self = {};
$self->{value} = $value;
$self->{next} = undef;
# $self->{parent} = undef;
return bless $self, $class;
}
sub value ( $self ) {
return $self->{value};
}
# sub is_root ( $self ) {
# return defined $self->{parent} ? 0 : 1;
# }
sub is_leaf ( $self ) {
return ( !defined $self->{left} && !defined $self->{right} )
? 1
: 0;
}
sub next ( $self, $node = undef ) {
if ( defined $node ) {
$self->{next} = $node;
# $node->{parent} = $self;
}
else {
return $self->{next};
}
}
# sub parent ($self ) {
# return $self->{parent};
# }
Thereβs still the pesky matter of removal, which Iβll get to later.
Walt also suggested an explicit start
node, so that we go start -> 1 -> 2 -> 3 -> 4 -> 5
, which makes the list creation a little simpler, and I expect will make the removal code simpler.
my $start = Node->new('0');
for my $i ( 1 .. 5 ) {
my $last = get_last($start);
$last->next( Node->new($i) );
}
So, letβs start trimming this.
trim_list( $start, $n );
sub trim_list ( $link, $n = 1 ) {
# how big is the linked list?
my $i = 0;
my $s = $link;
while ( defined $s ) {
$i++;
$s = $s->{next};
}
my $stop = $i - $n;
$stop = $stop < 1 ? 1 : $stop;
my $k = 1;
$s = $start;
while ( $s->next ) {
my $v = $s->next->value;
if ( $stop == $k ) {
$s->remove_next;
last;
}
$s = $s->next;
$k++;
}
show_list( $start->next );
}
Very similar to the original. Itβs that last while loop where things change. Iβm looking at and removing $s->next
rather than $s
, and using both last
(because why go through the rest of the list if weβre done?) and using $s->remove_next
rather than $s->remove
. Letβs look at that.
sub remove ( $self ) {
my $parent = $self->{parent};
my $next = $self->{next};
if ( defined $parent && defined $next ) {
$parent->{next} = $next;
$next->{parent} = $parent;
}
elsif ( defined $parent ) {
$parent->{next} = undef;
}
elsif ( defined $next ) {
$self->{value} = $next->{value};
$next->remove;
}
}
sub remove_next( $self ) {
my $next = $self->{next};
if ( defined $next ) {
$self->{next} = $next->{next};
}
}
When weβre 1) using a start node and 2) not having to look back, that allows us to have a much simpler remove function.
So, there you have it; a singly-linked list solution.
#!/usr/bin/env perl
use strict;
use warnings;
use feature qw{ say signatures state };
no warnings qw{ experimental };
use Getopt::Long;
my $n = 1;
GetOptions( 'number=i' => \$n, );
my $start = Node->new('0');
for my $i ( 1 .. 5 ) {
my $last = get_last($start);
$last->next( Node->new($i) );
}
# show_list($start->next); #so we don't get the start node
trim_list( $start, $n );
exit;
sub trim_list ( $link, $n = 1 ) {
# how big is the linked list?
my $i = 0;
my $s = $link;
while ( defined $s ) {
$i++;
$s = $s->{next};
}
my $stop = $i - $n;
$stop = $stop < 1 ? 1 : $stop;
my $k = 1;
$s = $link;
while ( $s->next ) {
if ( $stop == $k ) {
$s->remove_next;
last;
}
$s = $s->next;
$k++;
}
show_list( $start->next );
}
sub show_list( $link ) {
while ( defined $link ) {
print $link->{value} || '';
if ( defined $link->{next} ) {
print ' -> '
if defined $link->{next};
}
else { print "\n" if !defined $link->{next}; }
$link = $link->{next};
}
}
sub get_last( $node ) {
return get_last( $node->next ) if $node->next;
return $node;
}
# copied and pasted from my Challenge #59 code
######### ######### ######### ######### ######### ######### #########
# The same old Node code, but instead of left and right,
# it just has next
######### ######### ######### ######### ######### ######### #########
# Now a singly-linked list, meaning no pointing back to the start
package Node;
sub new ( $class, $value = 0 ) {
my $self = {};
$self->{value} = $value;
$self->{next} = undef;
# $self->{parent} = undef;
return bless $self, $class;
}
sub value ( $self ) {
return $self->{value};
}
# sub is_root ( $self ) {
# return defined $self->{parent} ? 0 : 1;
# }
sub is_leaf ( $self ) {
return ( !defined $self->{left} && !defined $self->{right} )
? 1
: 0;
}
sub next ( $self, $node = undef ) {
if ( defined $node ) {
$self->{next} = $node;
# $node->{parent} = $self;
}
else {
return $self->{next};
}
}
# sub parent ($self ) {
# return $self->{parent};
# }
sub remove_next( $self ) {
my $next = $self->{next};
if ( defined $next ) {
$self->{next} = $next->{next};
}
}