如何在没有eval的情况下本地化一些传统的全局变量?

Axe*_*man 4 perl scope global-variables

我问这个问题是因为我终于解决了一个问题,我一直试图在很多情况下找到一种技术.我觉得它非常整洁所以我正在做这个Q-and-A.

看,如果我可以使用eval,我会这样做:

eval join( "\n"
         , map { 
             my $v = $valcashe{$_}; 
             sprintf( '$Text::Wrap::%s = %s', $_
                    , ( looks_like_number( $v ) ? $v : "'$v'" )
                    )
          }
        );
Text::Wrap::wrap( '', '', $text );
Run Code Online (Sandbox Code Playgroud)

我甚至尝试过很棘手,但似乎local将符号本地化为虚拟块,而不是物理块.所以这不起作用:

ATTR_NAME: while ( @attr_names ) {
    no strict 'refs';
    my $attr_name       = shift;
    my $attr_name       = shift @attr_names;
    my $attr_value      = $wrapped_attributes{$attr_name};
    my $symb_path       = "Text\::Wrap\::$attr_name";
    local ${$symb_path} = $attr_value;
    next ATTR_NAME if @attr_names;

    Text::Wrap::wrap( '', '', $text );

}
Run Code Online (Sandbox Code Playgroud)

相同的物理块,我在设置之前和之后测试了包变量,它们甚至在循环中显示了它们的时间值.但是测试显示只有传递的最后一个变量保留了它的调用值wrap.所以值只保持本地化,直到循环结束.

我认为解决方案很简洁 - 即使是神秘的perl magick.但最终结果是好的,因为这意味着我可以包装依赖于包范围变量的遗留代码,并确保设置的值尽可能短.

Axe*_*man 6

绅士,你这个白痴!包藏匿也是哈希!这有效:

local @Text::Wrap::{ keys %wrapped_attributes } 
    = \( values %wrapped_attributes )
    ;
Run Code Online (Sandbox Code Playgroud)

这样做如下:

  1. 它访问Text::Wrap符号表的数组切片并返回命名符号.
  2. 因为它是一个符号而不是一个标量,为了让perl知道正确的槽,你必须分配引用.对于数组和散列也是如此,但我们都知道它们的引用更常见.
  3. \( ... )语法传递回列表中的每个项目的引用.

因此,我们为每个键定位每个变量%wrapped_attributes,并为其分配每个值的引用.

这是丑陋和神秘的,每次我需要时都很难将它移开 - 但有了它,我可以预先指定并延迟本地化以选择我可以放置Damien Conway建议的铁丝网和危险的地方迹象.

如果我要使用它,它必须是我使用它的丑陋.