如何在函数内使用模块的“成员”变量?

时间:2013-09-16 21:57:37

标签: perl perl-module

我有这段代码:

#!/usr/bin/perl

package Modules::TextStuff;  

use strict;  
use warnings;  
use Exporter;  
our @ISA = qw(Exporter);  
our @EXPORT = qw(get_text);  

my $author;  

my $text_tmp1 =<<'ENG';  
This is a template text  
by $author.   
ENG


sub get_text {  
        my $tmp = shift @_;  
        $author = shift @_;  
        print "In sub author= $author lang = $tmp \n";  
        my $final_str = eval('$text_'.$tmp);  
        print "$final_str \n";  
        return $final_str;  
}  
1;  

测试脚本:

#!/usr/bin/perl  

use strict;  
use warnings;  

use Modules::TextStuff;  

my $str = get_text('tmp1','jim');  
print $str;  

当我运行测试脚本时,它不起作用。我明白了:

  

在sub author = jim lang = eng

     

变量“$ text_tmp1”在(eval 1)第2行不可用。使用   uninitialized值$ final_str连接(。)或字符串

我该如何解决这个问题?

3 个答案:

答案 0 :(得分:1)

组合字符串以创建变量名称通常是个坏主意。您可以使用our $text_tmp1 = ...代替my $text_tmp1 = ...来挽救当前的程序,但我认为您应该考虑采用不同的方法,例如哈希:

my %templates = (

    tmp1 => <<ENG,
This is a template text
by \$author.
ENG

    tmp2 => <<ESP,
Esta es templata texta de \$author.
ESP
);

sub get_text {
    ...
    my $final_str = eval( $templates{$tmp} );
    ...
}

答案 1 :(得分:1)

eval EXPR尝试获取确实存在但不再存在的变量的值时,会生成您询问的错误。

>perl -wE"{ my $x = 123; sub f { eval '$x' } } say '<'.f().'>';"
Variable "$x" is not available at (eval 1) line 2.
Use of uninitialized value in concatenation (.) or string at -e line 1.
<>

请记住,执行文件(例如脚本或模块)是在其自己的词法范围内完成的,就像上面的curlies创建的那样。


可以通过不让它超出范围

来保持变量存活来修复它
>perl -wE"my $x = 123; sub f { eval '$x' } say '<'.f().'>';"
<123>

但那对你来说不是一个选择。

其他选项包括使变量成为全局变量。

>perl -wE"{ our $x = 123; sub f { eval '$x' } } say '<'.f().'>';"
<123>

或者强制子捕获它以使它不会停止存在。

>perl -wE"{ my $x = 123; sub f { $x if 0; eval '$x' } } say '<'.f().'>';"
<123>

if 0使“void context”警告无效。)


那就是说,看起来你正试图重新发明wheel。不要发明另一个半假的模板系统。

答案 2 :(得分:0)

我正在看几件事:

  • 首先,$text_tmp1不是包变量。它是词法范围的,因为你用my声明了它。如果您需要它作为包变量并且在所有子例程中都可见,则需要使用our声明它。
  • 您的模块未按编写编译。您正尝试使用$author来源,但它未定义。
  • 你在eval做什么?这在很多层面都是错误的。

我将如何做到这一点:

#! /usr/bin/env perl

package Modules::TextStuff;  

use strict;  
use warnings;  
use Exporter qw(import);
use Carp;
our @EXPORT_OK = qw(get_text);

our %templates;                # This is now a package variable

#
# TEMPLATES
#

$templates{tmp1}=<<TEMPLATE;   # We'll use `%s` for replacements
This is a template text  
by %s.
TEMPLATE

$templates{tmp2}=<<TEMPLATE;
This is another template and we will substitute 
in %s in this one too.
TEMPLATE

sub get_text {  
    my $template = shift;  
    my $author   = shift;
    if ( not exists $templates{$template} ) {
        croak qq(Invalid template name "$template");
    }
    return sprintf $templates{$template}, $author;
}
1;

我会将这些模板中的每一个都作为%templates哈希中的条目。无需eval计算模板的变量名称。另请注意,我现在可以实际测试用户是否使用exists传入了有效模板。

另请注意,%template声明为our而非my。这使它在整个包中可用,包括我的包中的任何子程序。

我还使用@EXPORT_OK代替@EXPORT。这被认为更有礼貌。您正在请求污染用户命名空间的权限。这就像打开某人的门,问你是否可以喝啤酒而不是闯进冰箱里翻找啤酒。

请注意我如何使用sprintf来处理可替换参数。这再次消除了对eval的需求。

我也更喜欢在我的程序标题上使用#! /usr/bin/env perl,因为它与Perlbrew等内容更兼容。您正在使用/usr/bin/env来查找用户路径中的可执行Perl程序。这样,您无需知道它是/bin/perl/usr/bin/perl/usr/local/bin/perl还是$HOME/perl5/perlbrew/perls/perl-5.18.0/bin/perl

要使用你的模块,我会这样做:

#! /usr/bin/env perl

use strict;
use warnings;
use feature qw(say);

use Modules::TextStuff qw(get_text);

say get_text('tmp1','jim');

你做的几乎一样的电话。打印出来:

This is a template text  
by jim.