微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Perl 闭包模拟类实现二叉树

上一篇文章中用函数方式实现了一个二叉树,本篇用闭包模拟类的方式来重新实现,不同于传统意义的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";

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐