2012-06-05 40 views
5

Cho một biến có chứa một chuỗi đại diện cho tên của một gói, làm cách nào để gọi một chương trình con cụ thể của gói?Làm thế nào để bạn gọi một chương trình con từ một gói, được đặt tên gói trong Perl?

Dưới đây là điều gần nhất tôi đã tìm ra:

package MyPackage; 

sub echo { 
    print shift; 
} 

my $package_name = 'MyPackage'; 
$package_name->echo('Hello World'); 

1; 

Vấn đề với mã này là chương trình con được gọi là một phương pháp học; tên gói được chuyển vào làm đối số đầu tiên. Tôi muốn gọi chương trình con từ tên gói mà không có một đối số đầu tiên đặc biệt được chuyển hoàn toàn.

+1

Nếu bạn không muốn truyền một kẻ xâm lược, thì những gì bạn muốn không phải là gọi phương thức *. – hobbs

+0

@hobbs: Cảm ơn. Tôi đã cập nhật câu hỏi để sử dụng thuật ngữ chính xác hơn. – Sam

Trả lời

4

Perl phương pháp gọi là chương trình con chỉ thông thường, có được invocant như giá trị đầu tiên.

use strict; 
use warnings; 
use 5.10.1; 

{ 
    package MyPackage; 
    sub new{ bless {}, shift } # overly simplistic constructor (DO NOT REUSE) 
    sub echo{ say @_ } 
} 

my $package_name = 'MyPackage'; 
$package_name->echo; 

my $object = $package_name->new(); 
$object->echo; # effectively the same as MyPackage::echo($object) 
MyPackage 
MyPackage=HASH(0x1e2a070) 

Nếu bạn muốn gọi một chương trình con mà không có một invocant, bạn sẽ cần phải gọi nó là khác nhau.

{ 
    no strict 'refs'; 
    ${$package_name.'::'}{echo}->('Hello World'); 
    &{$package_name.'::echo'}('Hello World'); 
} 

# only works for packages without :: in the name 
$::{$package_name.'::'}{echo}->('Hello World'); 

$package_name->can('echo')->('Hello World'); 
  • Phương pháp can trả về một tham chiếu đến các chương trình con đó sẽ được gọi nếu nó đã được kêu gọi invocant. Các coderef sau đó có thể được sử dụng một cách riêng biệt.

    my $code_ref = $package_name->can('echo'); 
    $code_ref->('Hello World'); 
    

    Có một số hãy cẩn thận khi sử dụng can:

    • can có thể được ghi đè bởi các gói, hoặc bất kỳ lớp mà từ đó nó được thừa hưởng.
    • Gói định nghĩa phương thức có thể khác với trình kích hoạt.


    Đây thực sự là hành vi bạn đang tìm kiếm.

  • Cách tiếp cận khác là sử dụng thứ được gọi là symbolic reference.

    { 
        no strict 'refs'; 
        &{ $package_name.'::echo' }('Hello World'); 
    } 
    

    Không nên sử dụng tham chiếu tượng trưng. Một phần của vấn đề là có thể vô tình sử dụng một tham chiếu tượng trưng mà bạn không có ý định sử dụng nó. Đây là lý do tại sao bạn không thể có hiệu lực use strict 'refs';.

    Đây có thể là cách đơn giản nhất để thực hiện những gì bạn muốn làm.

  • Nếu bạn không muốn sử dụng tham chiếu tượng trưng, ​​bạn có thể sử dụng Stash.

    $MyPackage::{echo}->('Hello World'); 
    $::{'MyPackage::'}{echo}->('Hello World'); 
    
    $main::{'MyPackage::'}{echo}->('Hello World'); 
    $main::{'main::'}{'MyPackage::'}{echo}->('Hello World'); 
    $main::{'main::'}{'main::'}{'main::'}{'MyPackage::'}{echo}->('Hello World'); 
    

    Vấn đề duy nhất với điều này là bạn sẽ phải chia $package_name trên ::

    *Some::Long::Package::Name::echo = \&MyPackage::echo; 
    
    $::{'Some::'}{'Long::'}{'Package::'}{'Name::'}{echo}('Hello World'); 
    
    sub get_package_stash{ 
        my $package = shift.'::'; 
        my @package = split /(?<=::)/, $package; 
        my $stash = \%:: ; 
        $stash = $stash->{$_} for @package; 
        return $stash; 
    } 
    get_package_stash('Some::Long::Package::Name')->{echo}('Hello World'); 
    

    Đây không phải là lớn của một vấn đề mặc dù. Sau khi xem nhanh trên CPAN bạn tìm thấy Package::Stash.

    use Package::Stash; 
    my $stash = Package::Stash->new($package_name); 
    my $coderef = $stash->get_symbol('&echo'); 
    $coderef->('Hello World'); 
    

    (Phiên bản Pure Perl của Package::Stash sử dụng tài liệu tham khảo mang tính biểu tượng, không phải là Stash)


Nó thậm chí có thể làm cho một bí danh của chương trình con/phương pháp, như thể đã được nhập khẩu từ một mô-đun đang sử dụng Exporter:

*echo = \&{$package_name.'::echo'}; 
echo('Hello World'); 

Tôi sẽ ecommend giới hạn phạm vi của bí danh mặc dù:

{ 
    local *echo = \&{$package_name.'::echo'}; 
    echo('Hello World'); 
} 

Đây là một ngoại lệ, nơi bạn có thể sử dụng một tài liệu tham khảo mang tính biểu tượng với strict 'refs' kích hoạt.

+0

Cảm ơn câu trả lời toàn diện. Tôi đã chấp nhận nó bởi vì bạn đã cung cấp nhiều cách để làm những gì tôi hỏi. Tôi đặc biệt thích cách tiếp cận 'có thể'. – Sam

+1

@Sam, 'can' là sai. Nó kiểm tra cây thừa kế. 'can' là cho các phương thức. – ikegami

8

Có vẻ như bạn không muốn gọi nó là phương thức, nhưng là một chương trình con thông thường. Trong trường hợp đó, bạn có thể sử dụng một tài liệu tham khảo mang tính biểu tượng:

my $package_name = 'MyPackage'; 
{ 
    no strict 'refs'; 
    &{ $package_name . '::echo' }('Hello World'); 
} 
+0

+1 cho "không nghiêm ngặt" refs '; " chính xác – DVK

+2

Nếu bạn sử dụng '$ :: {$ package_name. '::'} {echo}' bạn không cần phải sử dụng 'không nghiêm ngặt' refs '; ' –

1

Sử dụng cú pháp &{ <EXPRESSION> }() gọi một phụ có tên là sự biểu hiện, như đã thảo luận trong perldoc perlref khi liệt kê các nhà khai thác dereferencing:

Phải thừa nhận rằng, đó là một chút ngớ ngẩn để sử dụng các curlies trong trường hợp này, nhưng BLOCK có thể chứa bất kỳ biểu thức tùy ý, cụ thể, biểu thức chỉ số:

&{ $dispatch{$index} }(1,2,3); # call correct routine 

ngẫu nhiên ví dụ thực tế:

# Note no "use strict"! 
use File::Slurp; 
my $p="File::Slurp"; 
@a=&{"${p}::read_file"}(".profile"); 
print $a[0]; 
Các vấn đề liên quan