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

时间:2016-09-11 18:01:34

标签: perl hash

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

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

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

我希望能够#34;祝福" ' CODE3'使用$ object在$ object中引用,这样我就可以执行以下操作:

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中有没有办法做到这一点?

注意:现在我想到了,最好将$ object明确地传递给方法,因为它解决了javascript的"this"问题。我已经习惯了Java"这个"这在Java中是有意义的,因为所有方法都是一个类,因此所有方法都有一个"这个",但在脚本编写中,它确实有助于了解" this"实际上是传递,或者它只是作为一个函数调用(并且你最终意外地污染全局范围或触发严格警告)传递$ self明确表明你不是将它作为一个函数调用,而是作为一种方法。

2 个答案:

答案 0 :(得分:5)

您正在进行子调用(而非方法调用),因此您只是忘记将$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。有人可能已经有了更完整的实施。

答案 1 :(得分:2)

这不是您想要的确切语法,但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的代码,你不能只是为了&而烦恼#34; - >已批准"因为实际的通话是->$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),但是你应该始终牢记未来的程序员负责维护你想要的代码抓住你的身体并穿上你的皮肤做一些这些技巧。

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