Проблемы с порядком вывода после возврата нескольких значений через подпрограмму в Perl - PullRequest
1 голос
/ 26 сентября 2019

У меня возникла небольшая проблема с выяснением того, как правильно синтаксис подпрограммы, которая возвращает несколько значений.Скажем, у нас есть файл в следующем формате, из которого я хочу проанализировать значения:

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.

Ответы [ 2 ]

4 голосов
/ 26 сентября 2019

Моя команда, однако, полностью сбивает порядок вывода.Любые идеи о том, какая ошибка в моем коде может быть причиной этого?

Это не ошибка в вашем коде (хотя прислушайтесь к советам Дэйва Кросса).Это ошибка в вашем понимании типа хеш-переменной 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.

3 голосов
/ 26 сентября 2019

Боюсь, это все довольно запутанно.Я думаю, что основная проблема заключается в использовании глобальных переменных.Подпрограмма должна всегда использовать переменные, которые либо передаются в нее в качестве параметров, либо определяются внутри нее.Кроме того, хранение данных о животном в трех разных хэшах - это путь к катастрофе.Как вы видели, вы не можете гарантировать, что различные хеши остаются в шаге.

Давайте начнем с написания подпрограммы, которая берет один из ваших «абзацев» данных об одном животном и превращает его вхэш.

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'
        };

Итак, мы можем видеть, что а) мы правильно анализируем данные и б) данные для каждого животного хранятся вместе в одном хеше.

что на самом деле с данными, которые мы получаем обратно?Ну, есть (по крайней мере!) Пара вариантов.И то, что выбрать, зависит от того, что вы хотите, чтобы ваша программа делала.

Если вы хотите обрабатывать информацию о животных в том порядке, в котором они появляются во входном файле, то имеет смысл хранить их в массиве..

my @animals;

while (<$fh>) {
  push @animals, parse_animal($_);
}

for (@animals) {
  say "The $_->{id} is found in $_->{loc} and is $_->{simil} similar";
}

Другой вариант может заключаться в том, чтобы хранить их в хэше (возможно, по идентификатору):

my %animals;

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

say Dumper \%animals;

Но, надеюсь, это дало вам достаточно информации, чтобы получить васотключился.

...