代码之家  ›  专栏  ›  技术社区  ›  Rob Wells

begin块在Perl中的角色是什么?

  •  34
  • Rob Wells  · 技术社区  · 14 年前

    我知道begin块是在Perl程序的主体之前编译和执行的。如果您不确定,请尝试在上面运行命令perl-cw:

    #!/ms/dist/perl5/bin/perl5.8
    
    use strict;
    use warnings;
    
    BEGIN {
        print "Hello from the BEGIN block\n";
    }
    
    END {
        print "Hello from the END block\n";
    }
    

    我被教导,早期编译和执行一个begin块可以让程序员在执行主程序之前确保任何需要的资源都是可用的。

    所以我一直在使用begin块来确保诸如db连接之类的东西已经建立并且可以被主程序使用。类似地,我使用结束块来确保在程序终止之前关闭、删除、终止等所有资源。

    在今天上午的讨论之后,我想知道这是否是看待开始和结束块的错误方法。

    在Perl中,begin块的预期角色是什么?

    更新1: 刚刚发现DBI连接不工作的原因。在得到这个小Perl程序之后:

    use strict;
    use warnings;
    
    my $x = 12;
    
    BEGIN {
        $x = 14;
    }
    
    print "$x\n";
    

    执行时打印12张。

    更新2: 由于埃里克·斯特罗姆在下面的评论,这个新版本更加清晰:

    use strict;
    use warnings;
    
    my $x = 12;
    my $y;
    
    BEGIN {
        $x = 14;
        print "x => $x\n";
        $y = 16;
        print "y => $y\n";
    }
    
    print "x => $x\n";
    print "y => $y\n";
    

    输出是

    x => 14
    y => 16
    x => 12
    y => 16
    

    再次感谢埃里克!

    3 回复  |  直到 10 年前
        1
  •  22
  •   tchrist    14 年前

    你试过换掉 BEGIN{} 阻止 INIT{} 封锁?这是modperl等工具的标准方法,它使用“编译一次,运行多个”模型,因为您需要在每次单独运行时重新初始化,而不仅仅是在编译期间初始化一次。

    但我还是要问为什么这一切都在特别的街区。你为什么不做点什么呢 prepare_db_connection() 函数,然后在程序启动时根据需要调用它?

    在一个 开始 如果模块文件中的主行代码 use D.这是使用 init {} 块。

    我还看到了致命的相互递归问题,这些问题必须使用类似于 require 而不是 使用 init {} 而不是 开始 . 但这很罕见。

    考虑这个程序:

    % cat sto-INIT-eg
    #!/usr/bin/perl -l
    print               "    PRINT: main running";
    die                 "    DIE:   main dying\n";
    die                 "DIE XXX /* NOTREACHED */";
    END         { print "1st END:   done running"    }
    CHECK       { print "1st CHECK: done compiling"  }
    INIT        { print "1st INIT:  started running" }
    END         { print "2nd END:   done running"    }
    BEGIN       { print "1st BEGIN: still compiling" }
    INIT        { print "2nd INIT:  started running" }
    BEGIN       { print "2nd BEGIN: still compiling" }
    CHECK       { print "2nd CHECK: done compiling"  }
    END         { print "3rd END:   done running"    }
    

    仅在编译时,它会生成:

    % perl -c sto-INIT-eg 
    1st BEGIN: still compiling
    2nd BEGIN: still compiling
    2nd CHECK: done compiling
    1st CHECK: done compiling
    sto-INIT-eg syntax OK
    

    编译时 执行后,它生成:

    % perl sto-INIT-eg 
    1st BEGIN: still compiling
    2nd BEGIN: still compiling
    2nd CHECK: done compiling
    1st CHECK: done compiling
    1st INIT:  started running
    2nd INIT:  started running
        PRINT: main running
        DIE:   main dying
    3rd END:   done running
    2nd END:   done running
    1st END:   done running
    

    壳牌报告了255个出口, die .

    您应该能够安排在需要时进行连接,即使 开始 证明太早了。

    嗯,只是记得。你不可能在做什么 DATA 在一个 开始 ,有吗?在解释器运行之前不会设置;它不会向编译器打开。

        2
  •  31
  •   Eric Strom    14 年前

    同时 BEGIN END 块可以如您所描述的那样使用,典型的用法是进行影响后续编译的更改。

    例如, use Module qw/a b c/; 陈述实际上意味着:

    BEGIN {
       require Module;
       Module->import(qw/a b c/);
    }
    

    类似地,子例程声明 sub name {...} 实际上是:

    BEGIN {
       *name = sub {...};
    }
    

    由于这些块在编译时运行,因此在块运行后编译的所有行都将使用begin块所做的新定义。这就是如何调用不带圆括号的子例程,或者各种模块如何“改变世界的工作方式”。

    结束 块可用于清除 开始 块已生成,但使用对象 DESTROY 方法。

    如果要清除的状态是DBI连接,则在 结束 布洛克没事。我不会在 开始 但由于几个原因,请阻止。通常在编译时不需要连接可用。执行诸如在编译时连接到数据库之类的操作将大大降低您使用的任何具有语法检查的编辑器的速度(因为它运行 perl -c )

        3
  •  4
  •   oscfri    10 年前

    虽然其他答案都是正确的,但我觉得值得一提的是 BEGIN END 使用时阻止 -n -p 切换到Perl。

    http://perldoc.perl.org/perlmod.html

    当您使用-n和-p切换到Perl时,开始和结束工作就像在awk中一样,就像退化的情况一样。

    对于那些不熟悉 -N 开关,它告诉Perl用以下代码包装程序:

    while (<>) {
        ...  # your program goes here
    }
    

    http://perldoc.perl.org/perlrun.html#Command-Switches 如果您对Perl交换机的更具体信息感兴趣的话。

    举例说明 开始 -N switch,此perl-one行程序枚举 ls 命令:

    ls | perl -ne 'BEGIN{$i = 1} print "$i: $_"; $i += 1;'
    

    在这种情况下, 开始 -块用于启动变量 $i 在处理以下行之前将其设置为1 LS . 此示例将输出如下内容:

    1: foo.txt
    2: bar.txt
    3: program.pl
    4: config.xml