在上一篇文章中用函数方式实现了一个二叉树,本篇用闭包模拟类的方式来重新实现,不同于传统意义的Perl 类,否则也不用去费力重新实现,本篇用闭包来模拟类的一些行为,以消息传递的方式调用方法。
功能同上一个版本的基本一样,是用闭包的好处:
1。 模拟类行为,开放类方法
2。 内部变量不可直接修改,只能通过方法调用获取或修改值
3。 消息类型多样,自己可以随便控制
特点:
1。 每次取得一个节点(增加,删除,修改)都是一个闭包Node 的返回,可以看作对应的类实例,可通过进一步调用来获取相应的值
2。 只有两个模拟类,Node and BNSTree,通篇都是通过这两个闭包类来进行操作
附代码以及编写过程中的一些注释:
use strict;
use warnings;
use Data::Dumper;
sub Node {
my $_node = { left => undef, right => undef, key => undef, parent => undef };
# set multiple value at same time
my %params = @_;
map { $_node->{$_} = $params{$_} if defined $params{$_} } keys %$_node;
# set or get value for each item
return sub {
my ( $msg, $value ) = @_;
if ( exists $_node->{$msg} ) {
defined $value ? $_node->{$msg} = $value : $_node->{$msg};
}
# because cannot set one of attribute of _node to undef, so add one method delete to set to undef
elsif ( $msg eq 'delete' && $value ) {
$_node->{$value} = undef;
}
else {
die "Undefined key: $msg";
}
}
}
sub BNSTree {
my $root = undef;
my $empty = sub {
return !$root;
};
#this is original version for insert, logic is clear but code is too much
my $insert_old = sub {
my ($value) = @_;
if ( $empty->() ) {
my $node = Node( key => $value );
return $root = $node;
}
my $tmp = $root;
while ( $value != $tmp->('key') ) {
if ( $value < $tmp->('key') && $tmp->('left') ) {
$tmp = $tmp->('left');
}
elsif ( $value > $tmp->('key') && $tmp->('right') ) {
$tmp = $tmp->('right');
}
else {
last;
}
}
if ( $value < $tmp->('key') ) {
my $node = Node( key => $value, parent => $tmp );
return $tmp->( left => $node );
}
elsif ( $value > $tmp->('key') ) {
my $node = Node( key => $value, parent => $tmp );
return $tmp->( right => $node );
}
else {
return $tmp;
}
};
#this is optimized version for insert, almost 1/2 less code than previous and tight
my $insert = sub {
my ($value) = @_;
return $root = Node( key => $value ) if $empty->();
my $tmp = $root;
while ( $value != $tmp->('key') ) {
if ( $value < $tmp->('key') ) {
$tmp->('left')
? $tmp = $tmp->('left')
: return $tmp->( left => Node( key => $value, parent => $tmp ) );
}
elsif ( $value > $tmp->('key') ) {
$tmp->('right')
? $tmp = $tmp->('right')
: return $tmp->( right => Node( key => $value, parent => $tmp ) );
}
#~ else{ last; }
}
return $tmp;
};
my $min_max = sub {
#~ my ($msg) = @_;
#~ return if $empty->();
#~ my $tmp = $root;
#~ while($tmp->($msg)){
#~ $tmp = $tmp->($msg);
#~ }
#~ return $tmp;
#previous version work for the whole tree, but successor need it, so modify it that can be used by sub-tree min max
my ( $msg, $tmp_root ) = @_;
return if !$tmp_root;
while ( $tmp_root->($msg) ) {
$tmp_root = $tmp_root->($msg);
}
return $tmp_root;
};
my $search = sub {
my ($value) = @_;
return if $empty->();
my $tmp = $root;
while ( $value != $tmp->('key') ) {
if ( $value < $tmp->('key') ) {
$tmp->('left') ? $tmp = $tmp->('left') : return;
}
elsif ( $value > $tmp->('key') ) {
$tmp->('right') ? $tmp = $tmp->('right') : return;
}
else { last; }
}
return $tmp;
};
my $successor = sub {
my ($value) = @_;
#~ return undef if $empty->();
my $search_item = $search->($value);
return if !$search_item;
if ( $search_item->('right') ) {
#~ my $tmp = $search_item->('right');
#~ while($tmp->('left')){
#~ $tmp = $tmp->('left');
#~ }
#~ return $tmp;
# previous version need expend min function, so modify it use subtree min
return $min_max->( 'left', $search_item->('right') );
}
my $tmp = $search_item;
while ($tmp->('parent')
&& $tmp->('parent')->('right')
&& $tmp->('parent')->('right') == $tmp )
{
$tmp = $tmp->('parent');
}
return $tmp->('parent');
};
my $predecessor = sub {
my ($value) = @_;
#~ return undef if $empty->();
my $search_item = $search->($value);
return undef if !$search_item;
if ( $search_item->('left') ) {
#~ my $tmp = $search_item->('right');
#~ while($tmp->('left')){
#~ $tmp = $tmp->('left');
#~ }
#~ return $tmp;
# previous version need expend min function, so modify it use subtree min
return $min_max->( 'right', $search_item->('left') );
}
my $tmp = $search_item;
while ($tmp->('parent')
&& $tmp->('parent')->('left')
&& $tmp->('parent')->('left') == $tmp )
{
$tmp = $tmp->('parent');
}
return $tmp->('parent');
};
#go through from upper level to lower level
my $level_order = sub {
return if $empty->();
#~ my $tmp = $root;
my @stack = ($root);
while (@stack) {
my $tmp = shift @stack;
print $tmp->('key'), ' ';
push @stack, $tmp->('left') if $tmp->('left');
push @stack, $tmp->('right') if $tmp->('right');
}
};
my $in_order = sub {
return if $empty->();
my $tmp = $root;
my @stack = ();
while ( $tmp || @stack ) {
if ($tmp) {
print $tmp->('key'), ' ';
push @stack, $tmp;
$tmp = $tmp->('left');
}
else {
$tmp = pop @stack;
#~ print $tmp->('key'),' ';
$tmp = $tmp->('right');
}
}
};
my $pre_order = sub {
return if $empty->();
my $tmp = $root;
my @stack = ();
while ( $tmp || @stack ) {
if ($tmp) {
push @stack, $tmp;
$tmp = $tmp->('left');
}
else {
$tmp = pop @stack;
print $tmp->('key'), ' ';
$tmp = $tmp->('right');
}
}
};
my $suc_order = sub {
return if $empty->();
my $tmp = $root;
my @stack = ();
while ( $tmp || @stack ) {
if ($tmp) {
push @stack, $tmp;
$tmp = $tmp->('right');
}
else {
$tmp = pop @stack;
print $tmp->('key'), ' ';
$tmp = $tmp->('left');
}
}
};
my $internal_delete = sub {
my ($node) = @_;
if ( $node->('parent') ) {
my $pchild =
$node->('parent')->('key') > $node->('key') ? 'left' : 'right';
if ( !$node->('left') && !$node->('right') ) {
$node->('parent')->( delete => $pchild );
}
elsif ( !$node->('left') || !$node->('right') ) {
my $child = $node->('left') ? 'left' : 'right';
$node->('parent')->( $pchild => $node->($child) );
$node->($child)->( parent => $node->('parent') );
}
}
else {
if ( !$node->('left') && !$node->('right') ) {
$root = undef;
}
elsif ( !$node->('left') || !$node->('right') ) {
my $child = $node->('left') ? 'left' : 'right';
$root = $node->($child);
$node->($child)->( delete => 'parent' );
}
}
};
my $delete = sub {
my ($value) = @_;
my $tmp = $search->($value);
if ( !$tmp ) {
return;
}
elsif ( !$tmp->('left') || !$tmp->('right') ) {
$internal_delete->($tmp);
}
else {
my $suc = $successor->($value);
$internal_delete->($suc);
$tmp->( 'key', $suc->('key') );
}
};
#initial binary search tree with more values
for (@_) {
$insert->($_);
}
return sub {
my ( $msg, $value ) = @_;
if ( $msg eq 'empty' ) {
$empty->();
}
elsif ( $msg eq 'insert' ) {
$insert->($value);
}
elsif ( $msg eq 'min' ) {
$min_max->( 'left', $root );
}
elsif ( $msg eq 'max' ) {
$min_max->( 'right', $root );
}
elsif ( $msg eq 'search' ) {
$search->($value);
}
elsif ( $msg eq 'successor' ) {
$successor->($value);
}
elsif ( $msg eq 'predecessor' ) {
$predecessor->($value);
}
elsif ( $msg eq 'print' && $value && $value eq 'pre' ) {
$pre_order->();
}
elsif ( $msg eq 'print' && $value && $value eq 'suc' ) {
$suc_order->();
}
elsif ( $msg eq 'print' ) {
$in_order->();
}
elsif ( $msg eq 'delete' ) {
$delete->($value);
}
}
}
my $a = BNSTree(qw(14 20 18 21 7 ));
$a->( 'delete', 14 );
$a->( parent => undef );
$a->('print');
print "\n";
$a->( 'print', 'pre' );
print "\n";
$a->( 'print', 'suc' );
print "\n";