2012-06-01 2 views
0

Учитывая файлы:Perl: Сравнение хэш-массивов с другим массивом

А. hash.pl:

%h1 = (
    A=>['4631 4576','6646 6646',], 
    B=>['3539 4576',], 
); 

B. input.txt

4576 4631 4 
4576 3539 4 

я должен написать код Perl, который находит значения (4631 4576) в input.txt. (Порядок не важен.) Здесь «4631 4576» отображается как 4576 4631 в input.txt.

Я написал следующий код, но там, кажется, некоторые проблемы:

#!/usr/bin/perl -w 
open (FH, "input.txt") or die "can't open file: &! \n"; 
require "hash.pl"; 
foreach $amp (<FH>) 
{ 
    if ($amp=~/(\d+)\t(\d+)\t(\d+)/)   
    {  
     foreach $keys (keys %h1) 
     { 
      @tmparray= @{$h1{$keys}}; 
      foreach $tmp1 (@tmparray) 
      { 
       if ($tmp1 =~ m/($1 $2|$2 $1)/) 
       { 
        print "$keys", "$3\n"; 
       } 
      } 
     } 
    } 
} 
close (FH); 
exit; 

Что не так с этим кодом?

+0

В чем проблема? – ArjunShankar

ответ

2

Это решение использует do в предпочтении к require, как последний предназначено для inluding библиотеки исходных файлов и возвращает бесполезное скалярное значение в этом контексте. do просто возвращает значение последнего выполняемого оператора и поэтому может использоваться для инициализации локальной переменной.

Вместо того, чтобы использовать регулярное выражение, эта программа просто вызывает split для сбора небелых полей в файле. Затем он проверяет, что их было три, и что они все были числовыми.

Помещение результата в split в массив позволяет избежать потери захваченных полей регулярных выражений.

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

Выход кажется минимальным, но он содержит ту же информацию, что и исходный код.

use strict; 
use warnings; 

my %data = do 'hash.pl'; 

open my $fh, '<', 'input.txt' or die $!; 

while (<$fh>) { 

    my @values = split; 
    next if grep /\D/, @values or @values != 3; 

    my $re = qr/\A$values[0]\s+$values[1]\z|\A$values[1]\s+$values[0]\z/; 

    foreach my $key (keys %data) { 
    print "$key - $values[2]\n" if grep $_ =~ $re, @{$data{$key}}; 
    } 
} 

выход

A - 4 
B - 4 
0

Вы пытаетесь повторно использовать переменные $1, $2 и $3 внутри другого регулярного выражения, и я подозреваю, что это то, что испортило. Когда я пытаюсь код, я получаю ошибку:

Use of uninitialized value $2 in regexp compilation ... 

Таким возможным решением является копирование значения сразу после того, они захвачены, чтобы избежать т.д. переменных $1 стать перезаписаны, когда второй регулярное выражение компилируется:

if ($amp=~/(\d+)\t(\d+)\t(\d+)/) {  
    my @args = ($1,$2,$3); 

и тогда, конечно, заменить $1 с $args[0] и так далее.

Вы также должны знать, что запуск сценария без use warnings - не очень хорошая идея. Время, которое, по вашему мнению, вы сэкономите, будучи ленивым, будет потеряно 10 раз над отладкой простых ошибок. Why use strict and warnings?

+1

или просто 'if (my @args = $ amp = ~/(\ d +) \ t (\ d +) \ t (\ d +) /)' – ysth

+0

Если у вас нет проблем с качеством данных в файле, может также быть написано 'my @args = split '', $ amp'. – Borodin

1

Проблемы довольно прост: Вы используете $1, $2 и $3 в вашей программе, но к тому времени, когда вы используете их, вы потеряли свою ценность. Это глобальные символы, и они заменяются всякий раз, когда вы используете оператор регулярного выражения. После вашего первого регулярного выражения просто сохраните их в другой переменной:

$first = $1; 
$second = $2; 
$third = $3; 

Вы также должны быть осторожны с регулярными выражениями. Ваши регулярные выражения работают, но они очень, очень узкие. Я пропустил это в первый раз, когда у вас были вкладки в ваших файлах. Мне нравится использовать \s+ для пробелов любого типа. Это будет охватывать несколько вкладок или пробелов или комбинацию разных.

Я также настоятельно рекомендую вам узнать еще modern Perl. Вы бы сразу поднял эту проблему, если бы вы использовали эти две строки в программе:

use strict; 
use warnings; 

strict бы убедиться, что вы определили переменные через my или our. Это гарантирует, что вы не скажете $Foo в одном месте и $foo еще и задаетесь вопросом, что произошло со значением, которое вы сохранили в $foo.

warnings было бы сразу подсвечено, что $1 и $2 не имеют значений, когда вы выполняете свое второе регулярное выражение.

Из-за require вещи немного липкие в описании переменных, когда вы используете strict. A my переменная является строго локальной переменной с ограниченным объемом. Вот почему он используется в 99% случаев.

A my Переменная существует только в области, которую она объявила. Например, если вы объявляете переменную внутри цикла, она не существует вне цикла:

if ($a > $b) { 
    my $highest = $a; 
} 
else { 
    my $highest = $b; 
} 
print "The highest value is $highest\n"; 

Это не будет работать, потому что $highest определяется внутри, если заявление. Вы должны объявить $highest вне заявления для его работы:

my $highest; 
if ($a > $b) { 
    $highest = $a; 
} 
else { 
    $highest = $b; 
} 
print "The highest value is $highest\n"; 

our объявлена ​​переменная доступна по всему миру для всего пакета . Вы определяете его где угодно - внутри цикла, внутри оператора if, где угодно - и он будет доступен позже.

Пакет - просто пространство имен. Если вы не заявили об обратном, вы всегда находитесь в пакете main. Полезно, чтобы переменные модуля не влияли на переменные в вашем коде. Таким образом, ваш включенный модуль может использовать переменную $foo, и вы можете использовать переменную $foo, не мешая друг другу.

Причина, по которой я должен был войти в это из-за вашего require. Переменная A my доступна только в ее объеме. То есть цикл for, оператор if или весь файл. Обратите внимание, что последний: Весь файл. Это означает, что если я сделаю my %h1, он не будет существовать вне файла. Таким образом, я должен объявить его с our.

Кроме того, когда вы используете strict, это довольно чертовски строго. Он генерирует ошибку времени компиляции, когда видит переменную, которая не была объявлена. Таким образом, я должен объявить %h1 внутри основной программы, поэтому компилятор знает об этом.

Я также использую заявление say, которое я получаю от своего use feature qw(say);. Это как print, за исключением того, что он всегда печатает символ NL. Это не похоже на много, но во многих случаях это может быть намного менее беспорядочно.

Настоятельно рекомендуется использовать объявленный скаляр для открытия файла, а не только дескриптор файла. Файловые дескрипторы являются глобальными и могут вызвать проблемы. Кроме того, трудно использовать дескриптор файла в подпрограмме. Кроме того, рекомендуется использовать трехпозиционный открытый оператор. Это предотвращает проблемы, когда имена файлов начинаются с > или |.

Вот программа, переписанная с немного более современным чувством Perl. Я сохранил ваш стандартный алгоритм, но добавил новые прагмы, объявленный %h1 до require ing, и использовал более стандартный open. В противном случае, это в значительной степени то, что у вас было.

#! /usr/bin/env perl 
# 

use strict; 
use warnings; 
use feature qw(say); 

our %h1; 
require "hash.pl"; 


open (my $input_fh, "<", "input.txt") 
    or die "can't open file: $! \n"; 

foreach my $amp (<$input_fh>) { 
    chomp $amp; 
    if ($amp =~ /(\d+)\s+(\d+)\s+(\d+)/) { 
     # Got to save the $1, $2, and $3 for later 
     my $first = $1; 
     my $second = $2; 
     my $third = $3; 
     foreach my $key (keys %h1) { 
      foreach my $tmp1 (@{$h1{$key}}) { 
       if ($tmp1 =~ /($first\s+$second|$second\s+$first)/) { 
        say qq("$key": "$third"); 
       } 
      } 
     } 
    } 
} 
close $input_fh; 
+0

'&!' Не является сообщением об ошибке, '$!' Is. – TLP

+0

или просто 'if (my ($ first, $ second, $ third) = $ amp = ~/(\ d +) \ t (\ d +) \ t (\ d +) /)' – ysth

+1

привязать совпадение, или вы можете ошибочно соответствуют частичным номерам: '/ \ A (?: $ first \ s + $ second | $ second \ s + $ first) \ b /' – ysth

Смежные вопросы