0

ファイルを考えると:

A. hash.pl:

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

B.input.txt

4576    4631    4
4576    3539    4

input.txt で値 (4631 4576) を見つける Perl コードを作成する必要があります。(順序は重要ではありません。) ここで、「4631 4576」は、input.txt では 4576 4631 と表示されます。

次のコードを書きましたが、問題があるようです。

#!/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;

このコードの何が問題になっていますか?

4

3 に答える 3

2

後者はライブラリ ソース ファイルを含めることを目的としており、このコンテキストでは役に立たないスカラー値を返すため、このソリューションではdoを優先して使用します。最後に実行されたステートメントの値を返すだけなので、ローカル変数の初期化に使用できます。requiredo

splitこのプログラムは、正規表現を使用するのではなく、ファイル内の空白以外のフィールドを収集するために呼び出すだけです。次に、3 つ存在し、それらがすべて数値であることを確認します。

の結果をsplit配列に入れると、キャプチャされた正規表現フィールドが失われるという問題が回避されます。

正規表現$reが作成され、最初の 2 つのフィールドがどちらの順序でも表示されるようになり、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
于 2012-06-01T16:27:58.727 に答える
1

問題は非常に単純です。プログラムで$1$2、および$3を使用していますが、それらを使用するまでにそれらの価値を失っています。これらはグローバル シンボルであり、正規表現演算子を使用するたびに置き換えられます。最初の正規表現の一致後、それらを別の変数に保存するだけです。

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

正規表現にも注意が必要です。あなたの正規表現は機能しますが、非常に狭いです。ファイルにタブがあったのを初めて見逃しました。私\s+はあらゆる種類の空白に使用するのが好きです。これにより、複数のタブまたはスペース、または異なるものの組み合わせがカバーされます。

また、最新の Perlを学ぶことを強くお勧めします。プログラムで次の 2 行を使用していれば、すぐに問題を解決できたはずです。

use strict;
use warnings;

は、またはstrictを介し​​て変数を定義したことを確認します。これにより、ある場所と別の場所を言って、 に保存した値に何が起こったのか疑問に思うことがなくなります。myour$Foo$foo$foo

warningsすぐにそれを強調表示し、2 番目の正規表現の一致を行うときに値が$1ありません。$2

のせいで、requireを使うと変数宣言がちょっとベタベタしますstrictmy変数は、スコープが限定された厳密にローカルな変数です。そのため、99% の確率で使用されています。

変数は、宣言されたmyスコープ内にのみ存在します。たとえば、ループ内で変数を宣言すると、ループの外には存在しません。

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

$highestif ステートメント内で定義されているため、これは機能しません。$highest機能させるには、ステートメントの外側で宣言する必要があります。

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

宣言された変数は、パッケージour全体でグローバルに使用できます。ループ内、if ステートメント内など、どこでも定義でき、後で使用できるようになります。

パッケージは単なる名前空間です。別の方法で宣言しない限り、あなたは常にmainパッケージの中にいます。モジュール変数がコード内の変数に影響を与えないようにするのに役立ちます。このようにして、含まれているモジュールは変数を使用でき、相互に干渉することなく$foo変数を使用できます。$foo

私がこれに踏み込まなければならなかった理由は、あなたのrequire. 変数はそのmyスコープ内でのみ使用できます。つまり、for ループ、if ステートメント、またはファイル全体です。最後の 1 つに注意してください:ファイル全体. これは、そうするとmy %h1、ファイルの外には存在しないことを意味します。したがって、 . で宣言する必要がありourます。

また、 を使用する場合strictは、かなり厳格です。宣言されていない変数を検出すると、コンパイル時エラーが発生します。したがって、メインプログラム内で宣言する必要が%h1あるため、コンパイラーはそれを認識します。

sayから得たステートメントも使用しuse feature qw(say);ます。print常にNL文字を出力する以外は似ています。たいしたことではないように思えますが、多くの状況ではそれほど面倒ではありません。

ファイルを開くには、ファイル ハンドルだけでなく、宣言されたスカラーを使用することを強くお勧めします。ファイル ハンドルはグローバルであり、問​​題を引き起こす可能性があります。さらに、サブルーチンでファイル ハンドルを使用するのは困難です。また、3 部構成のオープン ステートメントを使用することをお勧めします。>これにより、ファイル名がまたはで始まる場合の問題を回避できます|

これは、もう少し現代的な Perl 風に書き直されたプログラムです。標準のアルゴリズムを維持しましたが、新しいプラグマを追加し、それを ing する%h1前に宣言し、より標準的な を使用しました。そうでなければ、それはあなたが持っていたものとほとんど同じです。requireopen

#! /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;
于 2012-06-01T15:22:11.460 に答える
0

あなたは変数を再利用しようとしており$1$2$3の正規表現の中で、それが混乱していると思います。コードを試すと、次のエラーが表示されます。

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

したがって、可能な解決策は、値がキャプチャされた直後に値をコピーして$1、2 番目の正規表現がコンパイルされたときに etc 変数が上書きされないようにすることです。

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

そしてもちろん、などに置き換え$1ます$args[0]

なしでスクリプトを実行することuse warningsはお勧めできません。怠惰で節約できると思っている時間は、単純なエラーのデバッグに比べて 10 倍失われます。strict と warnings を使用する理由

于 2012-06-01T15:05:55.763 に答える