首页 文章

Perl:我可以将CODE引用与包含它的HASH引用相关联吗?

提问于
浏览
8

我想创建一个哈希引用,其代码引用映射到标量(字符串)作为其成员 .

到目前为止,我有一个 Map 参考,看起来像这样:

my $object; 
$object = {
    'code1' => sub {
        print $_[0];
    },
    'code2' => sub {
        return 'Hello, World!';
    },
    'code3' => sub {
        $object->{code1}->($object->{code2}->());
    }
}; 
$object->{code3}->();

我想能够用$ object在$ object中“祝福”'code3'引用,所以我可以做类似的事情:

my $object; 
$object = {
    'code1' => sub {
        print $_[0];
    },
    'code2' => sub {
        return 'Hello, World!';
    },
    'code3' => sub {
        $self = shift;
        $self->{code1}->($self->{code2}->());
    }
}; 
$object->{code3}->();

然而,祝福只适用于包,而不是哈希表 .

在Perl 5版本22中有没有办法做到这一点?

注意:既然我想起来了,那就's better to pass $object to the method explicitly, as it solves javascript' s "this"问题了 . 我已经习惯了Java的"this",这在Java中是有意义的,因为所有方法都是一个类,因此所有方法都有"this",但在脚本中,确实知道"this"是否实际传递,或者它是否只是作为一个函数被调用真的很有帮助(并且最终意外地污染全局范围或触发严格警告)传递$ self明确表明您不是将其作为函数调用,而是作为一种方法 .

2 回答

  • 2

    您正在进行子调用(而不是方法调用),因此您只是忘记将 $self 作为参数传递 .

    my $object = {
        code1 => sub {
            print $_[0];
        },
        code2 => sub {
            return 'Hello, World!';
        },
        code3 => sub {
            my $self = shift;
            $self->{code1}->( $self, $self->{code2}->($self) );
        }
    }; 
    $object->{code3}->($object);
    

    但我认为你正在尝试创建类似JavaScript的对象 . 您可以从以下开始:

    package PrototypeObject;
    
    sub new {
       my $class = shift;
       my $self = bless({}, $class);
       %$self = @_;
       return $self;
    }
    
    sub AUTOLOAD {
       my $self = shift;
       ( my $method = our $AUTOLOAD ) =~ s/^.*:://s;
       return $self->{$method}->($self, @_);
    }
    
    1;
    
    use PrototypeObject qw( );
    
    my $object = PrototypeObject->new(
        code1 => sub {
            print $_[1];
        },
        code2 => sub {
            return 'Hello, World!';
        },
        code3 => sub {
            my $self = shift;
            $self->code1( $self->code2() );
        }
    ); 
    
    $object->code3();
    

    请注意,这会降低方法调用的速度,因为它必须在调用方法之前调用AUTOLOAD . 这可以通过重载方法调用操作符来解决 .

    检查CPAN . 有人可能已经有了更完整的实现 .

  • 5

    这不是您想要的确切语法,但Perl 5支持许多方法调用方法,包括method calls via strings . 所以你可以说:

    #!/usr/bin/perl
    
    { package Foo;
    
    use strict;
    use warnings;
    
    sub new { bless {}, shift }
    
    sub code1 { my $self = shift; print "$_[0]\n" };
    sub code2 { "Hello, World!" }
    sub code3 {
        my $self = shift;
        my $method1 = "code1";
        my $method2 = "code2";
        $self->$method1($self->$method2);
    }
    
    }
    
    use strict;
    use warnings;
    
    my $o = Foo->new;
    
    print "normal call\n";
    $o->code3;
    
    print "via string\n";
    my $method = "code3";
    $o->$method;
    

    另外,请记住package's symbol table is a hash%Foo:: ,这样你就可以自己在那里进行探险:

    #!/usr/bin/perl
    
    { package Foo;
    
    use strict;
    use warnings;
    
    sub new { bless {}, shift }
    
    sub code1 { my $self = shift; print "$_[0]\n" };
    sub code2 { "Hello, World!" }
    sub code3 {
        my $self = shift;
        my $method1 = "code1";
        my $method2 = "code2";
        $self->$method1($self->$method2);
    }
    
    }
    
    use strict;
    use warnings;
    
    print $Foo::{code2}->(), "\n";
    

    但是,我建议对这些技术有一个真正的代码原因,因为它可以使维护变成一场噩梦(例如,成像试图找到所有调用 Foo::approved 的代码,你不能只是为了"->approved" grep,因为实际的调用是 ->$state() ) .

    我刚看完评论并注意到你说了

    我对包的关注是我似乎无法在运行时创建包,但我可以在运行时创建哈希表

    Perl 5允许您在运行时创建包 . 实际上,根据您定义运行时的方式,您可以在运行时使用string eval执行任何操作,因为它在调用时会重新进行编译 . 但是还有一个使用typeglobs操作符号表的纯运行时方法:

    #!/usr/bin/perl
    
    { package Foo;
    
    use strict;
    use warnings;
    
    sub new { bless {}, shift }
    
    }
    
    use strict;
    use warnings;
    
    my $o = Foo->new;
    
    # here we add functions at runtime to the package Foo
    {
    no warnings "once";
    *Foo::code1 = sub { my $self = shift; print "$_[0]\n" };
    *Foo::code2 = sub { "Hello, World!" };
    *Foo::code3 = sub {
        my $self = shift;
        my $method1 = "code1";
        my $method2 = "code2";
        $self->$method1($self->$method2);
    };
    }
    
    $o->code3;
    

    因为Perl 5是面向对象的(而不是像JavaScript这样的基于对象),所以这些方法附加到所有Foo对象 . 如果你想个别对象有自己的符号表,那么我肯定有办法做到这一点 . 在我的头脑中,我想到了:AUTOLOAD

    #!/usr/bin/perl
    
    { package Foo;
    
    use strict;
    use Carp;
    use warnings;
    
    sub new {
        bless {
            symtab => {}
        }, shift
    }
    
    sub AUTOLOAD {
        my $self = shift;
        our $AUTOLOAD;
        my $method = $AUTOLOAD =~ s/.*:://r;
    
        my (undef, $file, $line) = caller();
    
        die "$method does not exist at $file line $line"
            unless exists $self->{symtab}{$method};
    
        $self->{symtab}{$method}->($self, @_);
    }
    
    sub DESTROY {} # prevent DESTROY method from being hijacked by AUTOLOAD
    
    }
    
    use v5.22;
    use warnings;
    
    my $o1 = Foo->new;
    my $o2 = Foo->new;
    
    $o1->{symtab}{inc} = sub { my $self = shift; $self->{i}++; };
    
    $o1->inc;
    $o1->inc;
    $o1->inc;
    
    say "inc called on o1 $o1->{i} times";
    
    $o2->inc; #dies because we haven't defined inc for $o2 yet
    

    Perl 5非常灵活,可以让你做任何你想做的事情(毕竟座右铭是TIMTOWTDI),但是你应该始终牢记未来的程序员负责维护你的代码谁可能想要追捕你并磨损你的皮肤做一些这些技巧 .

    这个问题有一个明确的感觉 . 看起来你试图像在JavaScript中解决它一样解决Perl 5中的问题 . 虽然Perl 5会让你这样做(正如我已经演示的那样),但是可能有一种更惯用的方法来实现相同的效果 . 你能否在一个不同的问题中描述你想要做的事情(而不是你想做什么),我们可以建议我们解决问题的方法 .

相关问题