【问题标题】:Problems with output order after returning multiple values through a subroutine in Perl通过 Perl 中的子例程返回多个值后的输出顺序问题
【发布时间】:2020-01-26 17:40:40
【问题描述】:

我在弄清楚如何正确编写返回多个值的子例程时遇到了一些问题。假设我们有一个具有以下格式的文件,我想从中解析值:

Id: Animal1
Loc: Area1
Similarity: 15/20

Id: Animal2
Loc: Area2
Similarity: 19/20

Id: Animal3
Loc: Area3
Similarity: 13/20

因为我想在同一个脚本中运行几个文件,所以我想以子程序的形式读取它们。但是,我的命令完全打乱了输出顺序。关于我的代码中的什么错误可能导致此问题的任何想法?

这是在我打开文件后开始的非子例程格式的代码:

open($fh, "<", $animal1) || die "Could not open file $animal1/n $!";

while (<$fh>) {
    chomp;
    if($_ =~ /Id:\s+(\S+)/){
        $id= $1;

    }
    if($_ =~ /Loc:\s+(\S+)/){
        $loc{$id}= $1;
    }
    if($_ =~ /Similarity:\s+(\S+)/){
        $simil{$id}= $1;
    }
}

foreach $id(keys %loc){
    print "The $animal is found in $loc{$animal} and is $simil{$animal} similar\n";
}
close $fh;

有问题的代码:

open($fh, "<", $animal1) || die "Could not open file $animal1/n $!\n";

while (<$fh>) {
    chomp;
    ($animal, $loc{$animal}, $simil{$animal})= parse_key_file($_);
}

foreach $animal(keys %loc){
    print "The $animal is found in $loc{$animal} and is $simil{$animal} similar\n";
}

sub parse_key_file {
    if($_ =~ /Id:\s+(\S+)/){
        $id= $1;
        next;
    }
    if($_ =~ /Loc:\s+(\S+)/){
        $loc{$id}= $1;
        next;
    }
    if($_ =~ /Similarity:\s+(\S+)/){
        $simil{$id}= $1;
        next;
    }
    return ($id, $loc{$id}, $simil{$id});
}

谢谢! A.

【问题讨论】:

    标签: perl parsing hash subroutine


    【解决方案1】:

    然而,我的命令完全弄乱了输出顺序。关于我的代码中的什么错误可能导致此问题的任何想法?

    这不是您的代码中的错误(尽管请注意 Dave Cross 的建议)。您对 Perl 的散列变量类型和 keys 函数的理解是错误的。哈希类型是无序的,并且无法保证在keys 函数中将哈希键返回给您的顺序。要以特定顺序返回它们(例如,它们在输入文件中遇到的顺序),您必须自己跟踪它。

    my (%seen,@order);
    sub parse_key_file {
        if($_ =~ /Id:\s+(\S+)/){
            $id= $1;
            if (!$seen{$id}++) {
                push @order, $id;
            }
            next;
        }
        ...
    }
    
    foreach $animal (@order) {
        print "The $animal is found in $loc{$animal} and is $simil{$animal} similar\n";
    }
    

    还有一个名为 Tie::IxHash 的模块,当您准备好使用 Perl 模块时,它提供了保留其键的顺序输入的哈希变量。

    【讨论】:

      【解决方案2】:

      恐怕这一切都相当混乱。我认为主要问题是您对全局变量的使用。子例程应该只使用作为参数传递给它或在其中定义的变量。此外,将动物的数据存储在三个不同的散列中是灾难的根源。如您所见,您不能保证不同的哈希值保持一致。

      让我们从编写一个子例程开始,该例程获取关于单个动物的数据“段落”之一并将其转换为哈希。

      sub parse_animal {
        my ($input) = @_;
      
        my %animal;
      
        for (split /\n/, $input) {
          next unless /\S/;
      
          if (/Id:\s+(\S+)/) {
            $animal{id} = $1;
            next;
          }
          if (/Loc:\s+(\S+)/) {
            $animal{loc} = $1;
            next;
          }
          if (/Similarity:\s+(\S+)/) {
            $animal{simil} = $1;
            next;
          }
          warn "Unknown data line: $_";
        }
      
        return \%animal;
      }
      

      这看起来很像您的代码,只是它只使用作为参数传递的数据,并且它构建并返回信息哈希。

      我们可以通过这样的程序进行测试:

      #!/usr/bin/perl
      
      use strict;
      use warnings;
      use feature 'say';
      use Data::Dumper;
      
      my $animal1 = 'animals.dat';
      
      open(my $fh, "<", $animal1) || die "Could not open file $animal1: $!\n";
      
      local $/ = '';
      
      while (<$fh>) {
        my $animal = parse_animal($_);
        say Dumper $animal;
      }
      
      sub parse_animal {
        ...
      }
      

      我们在这里使用Data::Dumper 来查看您从子例程返回的内容。我明白了:

      $VAR1 = {
                'loc' => 'Area1',
                'id' => 'Animal1',
                'simil' => '15/20'
              };
      
      $VAR1 = {
                'simil' => '19/20',
                'id' => 'Animal2',
                'loc' => 'Area2'
              };
      
      $VAR1 = {
                'simil' => '13/20',
                'id' => 'Animal3',
                'loc' => 'Area3'
              };
      

      所以我们可以看到,a) 我们正确解析了数据,b) 每只动物的数据都保存在一个哈希值中。

      那么,我们返回的数据到底该怎么做呢?好吧,有(至少!)几个选项。选择哪一个取决于你想让你的程序做什么。

      如果您想按照动物在输入文件中出现的顺序处理有关动物的信息,那么将它们存储在数组中是有意义的。

      my @animals;
      
      while (<$fh>) {
        push @animals, parse_animal($_);
      }
      
      for (@animals) {
        say "The $_->{id} is found in $_->{loc} and is $_->{simil} similar";
      }
      

      另一种选择可能是将它们存储在哈希中(可能是通过 ID 键入):

      my %animals;
      
      while (<$fh>) {
        my $animal = parse_animal($_);
        $animals{$animal->{id}} = $animal;
      }
      
      say Dumper \%animals;
      

      但希望这可以为您提供足够的信息,让您摆脱困境。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-07-04
        • 1970-01-01
        • 1970-01-01
        • 2012-06-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多