在perl中,我可以在子例程中动态创建变量吗?

时间:2015-12-01 11:50:24

标签: perl

背景

在我编写的代码中,我使用hash-ref将数据传递给方法(参见注释[1])。

遗憾的是,这导致了许多重复的代码:

sub thing {
  my ($self, $params) = @_;

  my ($foo, $bar, $baz, $biff,);
  if ( exists $params->{foo} && $params->{foo} ) {
     $foo = $params->{foo};
  }
  # repeat for `bar`, `baz`, `biff`

  ## rest of function ##
}

(并在每个带参数的函数中重复)

我想做什么

更容易定义参数列表,然后 迭代该列表,创建两个变量并在需要时将它们设置为值。

为了测试这个,我试过了:

my $params = { x => 1, y => 2};
my @params = qw(x y z a b c);

gno strict 'refs';
rep( ${$_}, @params );
use strict 'refs';

foreach my $p (@params) {
  if ( exists $params->{$p} && $params->{$p} ) {
    ${$p} = $params->{$p};
  }
}
print "x:$x, y:$y, z:$z, a:$a, b:$b, c:$c\n"

这给了我以下错误:

Global symbol "$x" requires explicit package name at ./test.pl line 20.
Global symbol "$y" requires explicit package name at ./test.pl line 20.
Global symbol "$z" requires explicit package name at ./test.pl line 20.
Global symbol "$c" requires explicit package name at ./test.pl line 20.

我可以做这个动态变量创建吗? (如果是的话,怎么样?)

[1]通过使用哈希来传递数据,我在很多方面获益:

  1. 清楚地表明每项数据是什么
  2. 数据的ORDER不再重要
  3. 我可能会遗漏一个或多个数据,而且我不需要添加随机undef
  4. 我传递的数据较少:1个标量(参考)而不是多个标量
  5. (我接受功能可以改变父母数据的危险,而不是用它的副本捣乱......)

5 个答案:

答案 0 :(得分:5)

是的,您可以在Perl中执行此操作。但对于Mark Dominus在these three articles中解释的所有原因,这是一个可怕的想法。

将这些值存储在哈希中是一个好主意。

#!/usr/bin/perl

use strict;
use warnings;

my $params = { x => 1, y => 2};
my @params = qw(x y z a b c);

my %var;

foreach my $p (@params) {

  # You need to take care exactly what you want in this
  # logical statement. The options are:
  # 1/ $p exists in the hash
  #    exists $params->{$p}
  # 2/ $p exists in the hash and has a defined value
  #    defined $params->{$p}
  # 3/ $p exists in the hash and has a true value
  #    $params->{$p}
  # I think the first option is most likely. The last one has
  # good chance of introducing subtle bugs.

  if ( exists $params->{$p} ) {
    $var{$p} = $params->{$p};
  }
}

print join ', ', map { "$_: " . ($var{$_} // 'undef') } @params;
print "\n";

答案 1 :(得分:1)

使用像这样的符号引用是一个非常糟糕的主意...哈希很好地完全消除了对此的需求。

use warnings;
use strict;

my $params = { x => 1, y => 2, foo => 3, };

thing($params);

sub thing {
    my $params = shift;
    my $foo;
    if (defined $params->{foo}){
        $foo = $params->{foo};
    }
    print $foo;
}

您也可以直接传入哈希本身(无论是预先创建的,还是内联传递给子。如果预先创建,则子将在副本上运行。)

thing(foo => 1, x => 2);

sub thing {
    my %params = @_;
    print $params{foo} if defined $params{foo};
}

答案 2 :(得分:1)

感谢Dave Cross&其他 - 以下测试工作:

#!/usr/bin/perl 
use strict;
use warnings;
use English qw( -no_match_vars ) ;
use Carp;
use Data::Dumper;

my $params = { x => 1, y => 2, z => 0};
my @params = qw(x y z a b c);

my %var;

foreach my $p (@params) {
  if ( exists $params->{$p} ) {
    $var{$p} = $params->{$p};
  } else {
    $var{$p} = undef;
  }
}

print Dumper \%var;

这为我%var提供了所有需要的参数(如@params中所列,其中未传入的参数(即,不在$params hashref中)创建了{ {1}}值。

因此,我可以放心地测试真理,而不必担心存在

谢谢大家。

答案 3 :(得分:0)

我使用软参考做了这个:

#!perl

no strict "refs";

my %vars = ( x => 1, y => 2 );

for my $k ( keys %vars ) {
  $$k = $vars{$k};
}

print $x, $y;

但推荐设置(使用严格;使用警告;)可以防止出现这种模式。用它来拍脚很容易。

答案 4 :(得分:0)

perl -Mstrict -MData::Dumper -wE'
  {package Data::Dumper;our($Indent,$Sortkeys,$Terse,$Useqq)=(1)x4}
  my @aok = qw( x  y  z  a  b  c );
  my %dft = ( a => -1 );
  say "- - - -";
  my $arg = { x => 1, y => 2, foo => 42 };
  $arg = { %dft, %$arg };
  say "arg: ", Dumper($arg);

  my %var;                     
  @var{ @aok } = @$arg{ @aok };
  say "var: ", Dumper(\%var);

  my %aok = map { $_ => 1 } @aok;
  my @huh = grep !$aok{$_}, sort keys %$arg;
  @huh and say "huh: ", Dumper(\@huh);
'
- - - -
arg: {
  "a" => -1,
  "foo" => 42,
  "x" => 1,
  "y" => 2
}

var: {
  "a" => -1,
  "b" => undef,
  "c" => undef,
  "x" => 1,
  "y" => 2,
  "z" => undef
}

huh: [
  "foo"
]