在使用其他人的Perl API时,避免使用$ _是一种约定吗?

时间:2010-10-29 18:05:17

标签: perl api calling-convention

当我将其他人的API与默认变量$ _

结合使用时,我才被抓获
foreach (@rps_server_details) {
    @server_data = ();
    @server_data = split(/,/);
    @$esp_hosts = ();
    $filters{server_name} = $server_data[0];
    print "--->$_<--\n";
    $esp_hosts = $esp->get_hosts(fields => $fields, %filters) || die "$@";
    print "--->$_<--\n";

这个输出是:

--->igrid8873.someone.com,app_10<--
Use of uninitialized value in concatenation (.) or string at ./rps_inv_lookup.pl line 120.
---><--

指定我自己的循环变量而不是依赖$ _修复问题。

通过将$ _与其他人编写的API结合使用,我是不是很天真?或者这是API模块中的错误吗?

3 个答案:

答案 0 :(得分:9)

这是API中的一个错误。如果在函数中使用$_,则添加

非常重要
local($_);

在函数内部,以避免破坏调用者的$_,或者避免在库函数中使用$_来被其他人调用。

如果您可以将yoursel限制为Perl版本&gt; 5.9.1那么你也可以制作$ _ lexical,这比使用<{p>} local更容易理解

my $_;

但是这会在早期版本的Perl上破解。

来自man perlvar:

  

由于$_是一个全局变量,在某些情况下可能导致这种情况                  不必要的副作用。从perl 5.9.1开始,你现在可以使用了                  $_的词汇版本,通过在文件或块中声明它                  和我的”。此外,声明“our $_”会恢复全局$_                  在目前的范围内。

答案 1 :(得分:6)

我会说:

  1. 违反了您的最佳做法(始终使用尽可能本地的变量范围,并且由于您遇到的问题而避免使用$_

  2. 加上 API 中的错误导致同样违反最佳做法,以及未将local $_特定变量本地化为{{1}} {3}}

  3. 除了perldoc之外,API违反了Perl最佳实践(如Conway的书中规则):

      

    第5.6节。本地化标点符号变量

         

    如果您被迫修改标点符号变量,请对其进行本地化。

         

    前面描述的“本地化”中的问题也可能在您被迫更改标点符号变量中的值时出现(通常在I / O操作中)。所有标点符号变量都是全局的。它们提供了明确的控制权在大多数其他语言中,它将完全是隐式行为:输出缓冲,输入行编号,输入和输出行结束,数组索引等等。

         

    在没有首先对其进行本地化的情况下更改标点符号变量通常是一个严重的错误。未本地化的分配可能会改变系统中完全不相关的部分中的代码行为,即使是在您自己编写但仅仅使用的模块中。

         

    使用local是临时更改全局变量值的最干净,最稳健的方法。它应该始终应用在尽可能小的范围内,以便最小化变量可能控制的任何“环境行为”的影响:

    这里还有完整的perldoc perlvar文档 - 在网页中搜索“nasty_break”一词(我找不到直接的页内链接,但它接近页面的开头)

      

    你什么时候应该非常小心   修改大多数的默认值   这里描述的特殊变量   文献。在大多数情况下,你想   之前将这些变量本地化   改变它们,因为如果你不这样做,那么   改变可能影响其他模块   依赖于默认值   你有的特殊变量   改变。这是正确的   一次读取整个文件的方法:

         
        
    1. 打开我的$ fh,“&lt;”,“foo”或者死!$ ;;
    2.   
    3. local $ /; #enable localized slurp mode
    4.   
    5. my $ content =;
    6.   
    7. 关闭$ fh;
    8.         

      但以下代码非常糟糕:

           
          
      1. 打开我的$ fh,“&lt;”,“foo”或者死!$ ;;
      2.   
      3. undef $ /; #enable slurp mode
      4.   
      5. my $ content =;
      6.   
      7. 关闭$ fh;
      8.         由于其他一些模块,可能要   从中的某个文件中读取数据   默认“行模式”,所以如果代码我们   刚刚提出已经执行,   $ /的全球价值现在已经改变   对于在其中运行的任何其他代码   同样的Perl翻译。

             

        通常在变量本地化时   你想确保这个改变   影响可能的最短范围。   所以,除非你已经在里面   短{}块,你应该创建一个   你自己。例如:

             
            
        1. my $ content ='';
        2.   
        3. 打开我的$ fh,“&lt;”,“foo”或者死!$ ;;
        4.   
        5. {
        6.   
        7. local $ /;
        8.   
        9. $ content =;
        10.   
        11. }
        12.   
        13. 关闭$ fh;
        14.         

          以下是您自己的例子   代码可能会崩溃:

               
              
          1. for(1..5){
          2.   
          3. nasty_break();
          4.   
          5. 打印“$ _”;
          6.   
          7. }
          8.   
          9. sub nasty_break {
          10.   
          11. $ _ = 5;
          12.   
          13. #用$ _
          14. 做点什么   
          15. }
          16.         

            你可能期望这段代码   印刷:

                 
                
            1. 1 2 3 4 5
            2.         

              但你会得到:

                   
                  
              1. 5 5 5 5 5
              2.         

                为什么呢?因为nasty_break()修改$ _   没有首先本地化它。修复   是添加local():

                     
                    
                1. local $ _ = 5;
                2.   

答案 2 :(得分:2)

foreach (@rps_server_details) {
    @server_data = ();
    @server_data = split(/,/);
    @$esp_hosts = ();
    $filters{server_name} = $server_data[0];
    print "--->$_<--\n";
    {
        local *_;  # disconnects the remaining scope from the implicit 
                   # variables so you can clean up after the dirty api.
                   # NOTE: Submit a bug report against the offending module.
                   #       If you notice this across multiple api features
                   #       consider finding a different module for this task.
        $esp_hosts = $esp->get_hosts(fields => $fields, %filters) || die "$@";
    }
    print "--->$_<--\n";