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



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

到目前为止,我有一个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}->();

然而,bless只对包起作用,而不是哈希表。

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

注意:现在我想到了,最好显式地将$object传递给方法,因为它解决了JavaScript的"this"问题。我只是太习惯Java的"this"了。这在Java中是有意义的,因为所有的东西都是一个类,因此所有的方法都有一个&;this&;,但在脚本中,知道&;this&;是实际传递的,或者它只是作为一个函数被调用(你最终会意外地污染全局作用域或触发严格的警告)通过$self显式地表明你不是作为一个函数调用它,而是作为一个方法。

您正在进行子调用(而不是方法调用),因此您只是忘记将$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;

& # x20的;

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();

注意这将减慢你的方法调用,因为它必须在调用你的方法之前调用autolload。这可以通过重载方法调用操作符来解决。

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

这不是您想要的语法,但是Perl 5支持许多方法调用,包括通过字符串调用方法。所以你可以说:

#!/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 calln";
$o->code3;
print "via stringn";
my $method = "code3";
$o->$method;

另外,记住一个包的符号表是一个散列:%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的代码,你不能只是grep"->批准",因为实际调用是->$state())。

我刚看了评论,注意到你说

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

Perl 5允许在运行时创建包。实际上,取决于您如何定义运行时,您可以在运行时对字符串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对象上。如果你想让单个对象有自己的符号表,那么我肯定有办法做到这一点。我的脑海里浮现出autolload:

#!/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问题感。看起来您正在尝试在Perl 5中解决问题,就像您在JavaScript中解决问题一样。虽然Perl 5将允许您这样做(正如我所演示的),但是可能有一种更惯用的方法来实现相同的效果。你能在另一个问题中描述你正在做什么(而不是你想怎么做)吗?我们可以建议我们解决你问题的方法。

最新更新