如何在Perl中正确传递引用和嵌套引用

时间:2018-01-21 23:56:34

标签: arrays perl hash reference

我是Perl的新手,我正在努力更好地了解何时以及如何将引用传递给嵌套数据结构。以下是我正在做的事情的概述:

  1. 将字符串列表传递给sub1
  2. 让sub1返回对散列内的列表的引用
  3. 将sub1的返回值传递给sub2
  4. 让sub2返回对散列内的列表的引用
  5. 迭代从sub2返回的列表并放入新的哈希
  6. 将对新哈希的引用传递给sub3
  7. 让sub3返回对新哈希的引用
  8. 我正在尝试传递对子例程的引用并让它们返回引用。这是推荐的做事方式吗?

    以下是代码以及我对 认为 所做的事情的评论。

    # create normal list
    my @list1 = qw(1 2 3); 
    
    # this passes a reference of list1 to sub1 and $result1 is 
      a reference to return value of sub1
    my $result1 = sub1(\@list1) 
    
    # passes reference of result1 to sub2 and result2 is a reference 
      to return value of sub2
    my $result2 = sub2($result1) 
    
    my %hash1;
    
    # dereferences list1 to the largest index to iterate until 
      and creates a key value pair in hash1 using the current 
      index of the loop. Also dereference result2 to get the 
      hash at the index.
    for my $i (0 .. $#list1) {
      $hash1{$list1[$i]} = @{$result2}[$i];  
    }
    
    my $result3 = sub3(\%hash1);
    
    sub sub1 {
      # dereference list passed in?
      my ($list1) = @_; 
    
      my $result = {hash containing a list} from service call using $list1;
    
      # dereferences result to return list2, 
        is this returning the list or a reference?
      return $result->{list2}; 
    }
    
    sub sub2 {
      # dereference list passed in?
      my ($list2) = @_; 
    
      my $result = {hash containing a list} from service call using $list2;
    
      # dereferences result to return list3, is 
        this returning the list or a reference?
      return $result->{list3} 
    }
    
    sub sub3 {
      # dereference hash1 ?
      my ($hash1) = @_ 
    
      my %result3;
    
      # loop over key value pairs of dereferenced hash1
      while (my ($key, $value) = each %$hash1) { 
        my %hash2;
    
        # loop over item list inside hash that is 
          the value of the current key in the outer loop
        foreach my $item (@{ $value->{items} }) {
          if( !(defined $hash2{flag1}) ) {
            $hash2{flag1} = $item->{flags}->{flag1};
          }
    
          switch ($item->{costName}) {
            case 'name1' {
              $hash2{name1} += $item->{costName}->{value};
            }
    
            case 'name2' {
              $hash2{name2} += $item->{costName}->{value};
            }
    
            # I want to also add a case where I compare the current value to two strings to see if it is not equal to both. Can this be done in a Perl switch.
    
    
          }
    
          # set the value for this key in result3 to reference of hash2
          $result3{$key} = \%hash2; 
        }
    
    # return reference to result3
    return \%result3
    }
    

    如果我正确引用和解除引用,请告诉我。到目前为止,我有我期望的输出,但我不能告诉我是否以非常低效的方式处理这个问题,或者只是使用引用错误。感谢。

    编辑:正在发生的一件事我无法理解当我何时 在for循环中运行此行:$hash1{$list1[$i]} = @{$result2}[$i];,第二个键(i = 1)接收用于第一个键(i = 0)的值。我验证了正确的数据位于result2列表中的正确索引处。这里发生了什么?

1 个答案:

答案 0 :(得分:3)

我将使用我的评论来注释您的代码,即我将重写# inline annotations并在下面的纯文本中添加更多说明。

首先,你错过了

use strict;
use warnings;

您编写的每个Perl文件都应以此开头(或等效文件;例如use Moose为您启用此功能。)

# create normal array
my @list1 = qw(1 2 3); 

@list1是一个数组。右侧的qw(1 2 3)部分是一个列表。

# pass a reference to @list1 to sub1 and assign the return value of sub1 to $result1
my $result1 = sub1(\@list1);

您在本声明末尾遗漏了;$result1sub1的返回值(的副本),而不是对它的引用。

# pass $result1 to sub2; $result2 is the return value of sub2
my $result2 = sub2($result1);

(见上文。)

my %hash1;

# iterate over the indices of @list1. $#list1 is the last index of @list1.
# Initialize %hash1 with keys taken from @list1 and corresponding values from @{$result2}.

for my $i (0 .. $#list1) {
  $hash1{$list1[$i]} = @{$result2}[$i];  
}

$#list1不会取消引用任何内容; @list1不是参考。

@{$result2}[$i]在技术上是一个列表切片。正如您使用$list1[$i]从数组中获取单个元素一样,您应该使用${$result2}[$i]从数组引用中获取单个元素。 @恰好在这里工作(列表切片只包含一个元素),但最好是请求标量,如果这是你想要的。

此外,惯用Perl将$result2->[$i]从数组引用中获取单个元素。

根本不需要这个循环:

@hash1{@list1} = @{$result2};

这是一个哈希切片。我们使用@list1的元素作为键,使用@{$result2}作为相应的值。

my $result3 = sub3(\%hash1);

sub sub1 {
  # Parameter list (done manually). The first argument goes in $list1.
  my ($list1) = @_; 

这里没有解除引用。这只是列表赋值:我们将@_(我们的参数)的元素列表分配给($list1),即我们的第一个参数获取本地名称$list1

  my $result = {hash containing a list} from service call using $list1;

我不知道你在这做什么。这是语法错误。

  # return the value stored under key 'list2' in the hash %{$result}
  return $result->{list2};

返回标量值。如果这是存储在%{$result}哈希中的内容,那么它可能是一个参考。

}

sub sub2 {

我正在跳过这个。请参阅上面的sub1

}

sub sub3 {
  # put first argument in local variable called $hash1
  my ($hash1) = @_;

您再次错过了;

  my %result3;

  # loop over key value pairs of dereferenced hash1
  while (my ($key, $value) = each %$hash1) {

请注意,建议不要使用each。它使用/修改您使用它的哈希中的隐藏状态,因此如果循环中的任何代码在您迭代的哈希上调用eachkeysvalues,它将会中断

    my %hash2;

    # loop over item list inside hash that is 
    # the value of the current key in the outer loop
    foreach my $item (@{ $value->{items} }) {

具体来说,此代码假定$value%{$hash1}中的当前值)是对另一个哈希的引用,该哈希包含对密钥'items'下的数组的引用。这个数组是我们正在迭代的。

      if( !(defined $hash2{flag1}) ) {
        $hash2{flag1} = $item->{flags}->{flag1};
      }

      switch ($item->{costName}) {

Perl没有switch声明。您必须使用与Perl语法相混淆的模块。如果是use Switch,请停止:此模块有几个问题。它会在Perl解析器看到之前重写程序的代码,但并不总是正确。这意味着Perl可能会执行与您编写的代码不同的代码。它也有相当复杂的行为,即使代码重写部分成功,也可能导致意外行为。

        case 'name1' {
          $hash2{name1} += $item->{costName}->{value};
        }

        case 'name2' {
          $hash2{name2} += $item->{costName}->{value};
        }

        # I want to also add a case where I compare the current value to two strings to see if it is not equal to both.
        # Can this be done in a Perl switch.

正如我所说,没有Perl开关这样的东西。 (从技术上讲,有given / when,但那些遭受同样复杂/难以预测的行为问题。他们现在正式“实验性”(行为可能会在未来版本中发生变化)。

      }

      # set the value for this key in result3 to reference of hash2
      $result3{$key} = \%hash2; 
    }

# return reference to result3
return \%result3
}

这就是代码注释。如果您对程序的实际行为有疑问,则需要发布Minimal, Complete, and Verifiable Example