【问题标题】:Using a variable name as the key name in a hash (Perl) [duplicate]使用变量名作为哈希中的键名(Perl)[重复]
【发布时间】:2013-08-26 20:02:04
【问题描述】:

我有这个要简化的 Perl 代码块。它是子例程的一部分,其中将一系列参数添加到哈希值的哈希值中。

$user{$account_num}{'item0'} += $item0;
$user{$account_num}{'item1'} += $item1;
$user{$account_num}{'item2'} += $item2;
$user{$account_num}{'item3'} += $item3;
$user{$account_num}{'item4'} += $item4;
$user{$account_num}{'item5'} += $item5;
$user{$account_num}{'item6'} += $item6;

您可以看到变量名称与键名称相同。我似乎记得曾经看过一个朋友在一行中做这种事情。比如:

[looping through input arguments]:
$user{$account_num}{'variable_name'} += $variable_name;

有人知道怎么做吗?

【问题讨论】:

标签: perl


【解决方案1】:

上面代码的问题是你想使用变量名作为键,它可能看起来像这样:

for my $item ( @items ) {
   $user{$account_num}{$item} = $item
}

问题在于 $items 值将用作键,而不是它的名称。

可能的解决方案:

如果您还有一个键名列表,这将大大简化:

my @keys = qw(item0 item1 item2 item3 item4 item5 item6);

现在假设您实际上将所有这些项目都放入您的子程序中,您可以轻松循环

# 1
sub update_items {
   my @items = @_;

   for my $k ( @keys ) { 
      my $item = shift @items;          # take an item from the front
      $user{$account_num}{$k} += $item; # update
   }
}

或者您可以使用一种称为hash-slicing 的技术,该技术在列表上下文中获取哈希并将值列表分配给键列表:

# 2
sub update_items {
   my @items = @_;

   # update all keys of @keys with all values of @items
   @user{$account_num}{@keys} += @items;
}

【讨论】:

  • 复合赋值运算符是否可以处理这样的切片?在我的快速测试中,@h{@keys} += @values 似乎只是在@values 上放置一个标量上下文并将该数字添加到切片的最后一个元素(即$h{ $key[-1] } += scalar(@values))。
  • 奇怪,我假设任何赋值运算符都可以使用,因为无论如何都必须先计算左侧。我想不是,我的错误。
【解决方案2】:

下面是 Perl 调试器中的一个简单示例:

main::(-e:1):   0
  DB<1> @a=('v1', 'v2', 'v3', 'v4')

  DB<2> x @a
0  'v1'
1  'v2'
2  'v3'
3  'v4'
  DB<3> $v1=10
  DB<4> $v2=20
  DB<5> $v3=30
  DB<6> $v4=40

  DB<7> for (@a) { $hash{$_} += $$_ }

  DB<8> x %hash
0  'v4'
1  40
2  'v1'
3  10
4  'v2'
5  20
6  'v3'
7  30
  DB<10>

键是$$_,它使用变量间接法来获取名称包含在变量中的变量的值,在本例中为$_

【讨论】:

  • +1 我忘了你可以这样做。
  • 如果你抽象地考虑一下,变量间接与 C/C++ 指针没有什么不同,而额外的$ 是取消引用运算符。并不是说我会使用这种模式,只是回答 OP 的问题。
  • @Jim Garrison,不是这样。虽然引用是类似的 C 和 C++ 指针,虽然通过引用进行间接引用非常好,但我们也不是在谈论。手头的主题是符号引用,在 C 和 C++ 中没有对等的东西,因为它们没有运行时可访问的符号表。
【解决方案3】:
#!/usr/bin/env perl

use strict;
use warnings;

use Data::Dumper;
$Data::Dumper::Indent = 0;

my %user = (
    '0022334401' => [ map int(1000 * rand), 1 .. 7 ],
    '1177459393' => [ map int(1000 * rand), 1 .. 7 ],
);

my $account_id = '0022334401';

print "Before:\n", Dumper($user{$account_id}) ,"\n";

my @vals = (11, 22, 33, 44, 55, 66, 77);

# done setting up. now, update each value for $user{$account_id}
# by adding the value at the corresponding index in @vals

$user{$account_id}[$_] += $vals[$_] for 0 .. $#vals;

print "After:\n", Dumper($user{$account_id}), "\n";

样本输出:

之前:
$VAR1 = [645,553,864,165,463,455,906];
后:
$VAR1 = [656,575,897,209,518,521,983];

【讨论】:

    【解决方案4】:

    语法是

    for my $i (0..6) {
       my $variable_name = "item$i";
       $user{$account_num}{$variable_name} += $$variable_name;
    }
    

    但它假定 $item0 等是包变量,它的使用 goes against 是软件开发的基本原则。


    您应该使用数组。

    $user{$account_num}{'item0'} += $items[0];
    $user{$account_num}{'item1'} += $items[1];
    $user{$account_num}{'item2'} += $items[2];
    $user{$account_num}{'item3'} += $items[3];
    $user{$account_num}{'item4'} += $items[4];
    $user{$account_num}{'item5'} += $items[5];
    $user{$account_num}{'item6'} += $items[6];
    

    允许你使用

    for my $i (0..$#items) {
        $user{$account_num}{"item$i"} += $items[$i];
    }
    

    【讨论】:

      【解决方案5】:

      你想多了。

      我知道,因为我一直都这样做:

      :如果我制造出一种能够以正确的频率振动插座的设备,我就可以让灯泡拧上和拧下。然后我可以将设备连接到插座...

      我的儿子:爸爸,你为什么不用电灯开关?

      如果您有一堆名为item1item2item3 等的项目。为什么不将其设为一个项目数组呢?然后你会有$item[0]$item[1]等。这将大大简化你的代码:

      for item ( @items ) {
          $user{$account_name}->[$item] += $items[item];
      }
      

      也许您使用item1item2 作为实际变量名的替代品。也许他们是$widget$left_handed_smoke_shifter。在这种情况下,您可以将其设为项目的散列,因此您将拥有 $item{left_handed_smoke_shifter}$item{widget}。然后,您的循环将如下所示:

      for my $item ( keys %items ) {
          $user{$account_name}->{$item} += $items{$item};
      }
      

      简单、易于理解和维护。

      但是,如果您真的非常坚持需要能够按照您坚持的方式使用变量名,请使用eval

      $user{$account_name}->{$varible_name} = eval '$' . "$variable_name";
      

      我只出售足够长的绳索,以便有人用它上吊。他们实际上用它做什么与我无关。

      【讨论】:

      • 是的,变量名确实是替身。我应该有创意,选择不那么像数组的替身。我想我将创建一个键名数组,因为它们在整个代码中都使用过,然后依次循环输入参数,因为它们已经传递给数组中的子例程。谢谢!
      • 我完全赞成尽可能地减少代码,但如果它需要使用不可持续的编程技术,我就不会。所以,我不认为我会追求 EVAL。
      • @JohnG。感谢您没有选择eval。哈希的想法虽然有效。看看这个Perl tutorial on Object Oriented programming。当您开始使用比简单哈希和数组更复杂的结构时,您应该开始考虑 OO 编程技术。这真的很简单,可以让你免受很多伤害。
      • 你实际上不需要使用 eval,$$variable_name 也可以,假设 $variable_name 的值是一个简单的字符串。你需要一个no strict 'refs' 在它之前。 (您也不需要将变量放在引号中 . 会为您字符串化它)
      【解决方案6】:

      最整洁的方法是使用eval。像这样

      $user{$account_num}{$_} += eval "\$$_" for 'item0' .. 'item6'
      

      但是记住别人说过的话,你不应该做这种事情。尽快重构代码。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-07-16
        • 1970-01-01
        • 2014-05-21
        • 1970-01-01
        • 1970-01-01
        • 2016-01-28
        • 1970-01-01
        相关资源
        最近更新 更多