【问题标题】:Assigning multiple Values to a hash Reference - Array将多个值分配给哈希引用 - 数组
【发布时间】:2012-05-23 18:07:28
【问题描述】:

我声明了以下子项(实际上,值来自数据库 - 所以我对其进行了简化):

sub get_date {
 my ($ref_last)=@_;
 $$ref_last->{duration}='24,0,4';
 ($$ref_last->{duration}->{d},
  $$ref_last->{duration}->{h},
  $$ref_last->{duration}->{m})
   = split(/\,/, $$ref_last->{duration});
}

这个 sub 是从脚本的 main-Part 调用的,像这样:

my $hashy;
get_date(\$hashy);
print $hashy->{duration}->{d};

一切都很好,就像一个魅力,直到我使用严格:

use strict;
my $hashy;
get_date(\$hashy);
print $hashy->{duration}->{d};

在这种情况下 perl 说“不能使用字符串 ("24,0,4") 作为 HASH ref 而使用“strict refs”

我已经尝试过ref($ref_last) - 但ref 是一个只读函数。

任何建议,为什么会发生这种情况 - 也许是更好的解决方案?

这是完整的(非)工作脚本:

#!/usr/bin/perl -w
use strict;
my $hashy;
get_date(\$hashy);
print $hashy->{duration}->{d};

sub get_date {
        my ($ref_last)=@_;
        $$ref_last->{duration}='24,0,4';
        ($$ref_last->{duration}->{d},
         $$ref_last->{duration}->{h},
         $$ref_last->{duration}->{m})
                = split(/\,/, $$ref_last->{duration});
}

【问题讨论】:

    标签: perl


    【解决方案1】:

    基于 cmets,您正在尝试更改现有哈希值的格式(从 «24,0,4» 到 «{ d=>24, h=>0, m=>4 }»)。这是我的做法。

    sub split_duration {  # Changes in-place.
        my ($duration) = @_;
        my %split;
        @split{qw( d h m )} = split(/,/, $duration);
        $_[0] = \%split;
    }
    
    my $row = $sth->fetchrow_hashref();
    split_duration( $row->{duration} );
    

    sub split_duration {
        my ($duration) = @_;
        my %split;
        @split{qw( d h m )} = split(/,/, $duration);
        return \%split;
    }
    
    my $row = $sth->fetchrow_hashref();
    $row->{duration} = split_duration( $row->{duration} );
    

    下面的问题说明和初步解决方案。


    没有严格,24,0,4 被视为哈希引用,这意味着 Perl 正在创建一个名为 $24,0,4 的变量!!!这很糟糕,这就是 use strict 'refs'; 阻止它的原因。

    根本问题是您尝试将两个值分配给$$ref_last->{duration}:一个字符串

    '24,0,4'
    

    以及对哈希的引用

     { d => 24, h => 0, m => 4 }
    

    它不能同时容纳两者。您需要重新排列数据。

    我怀疑您拆分后实际上并没有使用24,0,4,因此您可以按如下方式修复代码:

    sub get_date {
        my ($ref_last)=@_;
        my $duration = '24,0,4';
        @{ $$ref_last->{duration} }{qw( d h m )} =
            split(/,/, $duration);
    }
    

    如果你需要24,0,4,你可以重构它。或者,您可以将组合持续时间与 d、h、m 一起存储。

    sub get_date {
        my ($ref_last)=@_;
        my $duration = '24,0,4';
        $$ref_last->{duration}{full} = $duration;
        @{ $$ref_last->{duration} }{qw( d h m )} =
            split(/,/, $duration);
    }
    

    或者在一个单独的元素中上位散列。

    sub get_date {
        my ($ref_last)=@_;
        my $duration = '24,0,4';
        $$ref_last->{full_duration} = $duration;
        @{ $$ref_last->{duration} }{qw( d h m )} =
            split(/,/, $duration);
    }
    

    【讨论】:

    • 这个sub的界面对我来说也很糟糕,但不知道它的实际用途,我不知道如何修复它。
    • 谢谢。问题是,$ref_hash->{duration} 来自$sth->fetchrow_hashref() - 不想在 sql 中将其拆分为 3 个额外值。无论如何在将$ref_hash->{duration}(但不是整个哈希)分配给子变量(d,h,m)之前/之后“删除”它吗?
    • 知道了...只需将字段“duration”选择为“duration_pre”,然后用您的解决方案填充duration->h,m,d...非常感谢!!
    • @int2000, my %split; @split{qw( d h m )} = split(/,/, $$ref_last->{duration}); $$ref_last->{duration} = \%split;
    【解决方案2】:

    get_date 中,您将一个字符串分配给$ref_last->{duration},然后尝试像哈希引用一样访问它。您还有额外的美元符号试图取消引用从哈希中提取的单个值。

    我会这样写

    sub get_date {
      my($ref_last) = @_;
      my $duration = '24,0,4';
      @{ $ref_last->{duration} }{qw/ d h m /} = split /\,/, $duration;
    }
    

    最后一行是hash slice,允许您在单个列表分配中为dhm 键分配值。

    在调用者的上下文中,需要搭建一点脚手架。

    my $hashy = {};
    get_date($hashy);
    

    在没有初始化$hashy 以包含一个新的空hashref 的情况下,get_date 完成了所有的分配,然后扔掉了新建的大厦。这是因为当您从@_ 中复制参数时,您使用的是按值传递语义。

    Perl 也将支持按引用传递。 Perl 有一个称为autovivification 的功能,该语言可以根据需要为您构建必要的脚手架。要使用这种风格,你会写

    my $hashy;
    get_date($hashy);
    
    sub get_date {
      my($ref_last) = @_;
      my $duration = '24,0,4';
      @{ $_[0]->{duration} }{qw/ d h m /} = split(/\,/, $duration);
    }
    

    注意使用$_[0] 直接访问第一个参数,在这种情况下是$hashy别名。即get_date直接修改$hashy

    不管怎样,假设我们用

    打印内容
    print "[", join("][" => %{ $hashy->{duration} }), "]\n";
    

    在这种情况下,输出是一些排列

    [h][0][m][4][d][24]

    使用 Perl 构建复杂的数据结构并不难,但您必须学习规则。

    【讨论】:

      【解决方案3】:

      发生这种情况是因为您的哈希引用语法很奇怪。

      #!/usr/bin/perl -w
      use strict;
      
      my $hashref = {};
      get_date($hashref);
      
      print $hashref->{duration}->{d};
      
      sub get_date {
          my ($ref_last) = @_;
          $tmp = '24,0,4';
      
          ($ref_last->{duration}->{d},
           $ref_last->{duration}->{h},
           $ref_last->{duration}->{m})
                  = split(/,/, $tmp);
      }
      

      并且在您的子程序中使用$ref_last->{duration},而不使用$$

      【讨论】:

      • 抱歉,无法关注你。你能举一个完整的工作例子吗?我了解到:如果我必须从一个子(没有return $returnvar)返回值,我必须将它们作为参考(\$var)传递。为了取消引用它,我在子中使用$$var。这很好,直到我use strict
      • 通过进行该更改,$hashref 将不会自动激活,因此在调用 get_date 后它将保持 undef。他需要做my $hashref = {};
      • @ikegami,好像和split有什么特别的关系;即使使用my $hashref = {}; 也会出现同样的错误;如果在你的答案中使用一个简单的变量,它可以正常工作,但如果使用哈希元素,它会下降
      • @ArtM,我的my $hashref = {}; 评论是关于您所做的调用约定的更改。这并不是为了解决 OP 提出的问题。
      • 是的,我似乎度过了糟糕的一天,我完全脱离了主题,甚至在我这个例子时读错了行号:/。 ..如果我不发表废话就更好了
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-07-20
      • 1970-01-01
      • 2012-01-09
      • 2011-04-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多