2016-12-11 39 views
4

Perlで別の配列を使って配列の配列をフィルタリングしようとしています。私はOS X上でPerl 5.18.2を持っていますが、もし私がuse 5.010なら振る舞いは同じです。Perl:配列項目の削除と配列のサイズ変更

#!/usr/bin/perl 
#use strict; 
my @terms = ('alpha','beta test','gamma','delta quadrant','epsilon', 
      'zeta','eta','theta chi','one iota','kappa'); 
my @filters = ('beta','gamma','epsilon','iota'); 
foreach $filter (@filters) { 
    for my $ind (0 .. $#terms) { 
     if (grep { /$filter/ } $terms[$ind]) { 
      splice @terms,$ind,1; 
     } 
    } 
} 

これは、さまざまな検索条件に一致する行を引き出すために機能しますが、配列の長さは変更されません。私は結果の@terms配列を書き出す場合は、私が取得:

[alpha] 
[delta quadrant] 
[zeta] 
[eta] 
[theta chi] 
[kappa] 
[] 
[] 
[] 
[] 

あなたがそれから予想されるように、scalar(@terms)を印刷する10の結果を取得します。

私が望むのは、長さ6の結果の配列で、最後に4つの空白の項目はありません。その結果をどうやって得るのですか? perldoc page about spliceに「配列は必要に応じて拡大または縮小します」という意味で、配列が縮小しないのはなぜですか?

(私はPerlにはあまり慣れていないので、「なぜあなただ​​けではないの?」と思っているなら、私はそれを知らない、または理解していない

+1

'grep'は配列に対して動作し、一致する要素を返します。たぶん、$ terms [$ ind] =〜/ $ filter/'を1つのものにマッチさせることを意味するでしょうか? – tadman

+0

うん、それは意図したとおりに動作するように見える - ありがとう!私はまだ配列が私が以前にやっていたもので収縮しなかった理由について混乱しています。 –

+0

積極的に反復処理している配列から要素を削除するのは、常に難しいことです。何かをスプライスするたびにオフセットが1ずつシフトします。 – tadman

答えて

7

あなたは、不要なものを除いて、いつでも配列を再生成することができます。あなたが欲しいどの要素を決定するとどのそうでない可能フィルタとしてgrep行為:あなたが手に%filter_exclusionのような単純な構造を持っている場合

#!/usr/bin/perl 

use strict; 

my @terms = ('alpha','beta test','gamma','delta quadrant','epsilon', 
      'zeta','eta','theta chi','one iota','kappa'); 
my @filters = ('beta','gamma','epsilon','iota'); 

my %filter_exclusion = map { $_ => 1 } @filters; 

my @filtered = grep { !$filter_exclusion{$_} } @terms; 

print join(',', @filtered) . "\n"; 

それは非常に簡単です。

更新:各ステップで、配列の内容を印刷し、何が起こっているかを確認するには

my $filter_exclusion = join '|', map quotemeta, @filters; 

my @filtered = grep { !/$filter_exclusion/ } @terms; 
+0

これは部分的にしか動作しません。つまり、 'gamma'と' epsilon'は除外されますが、 'beta test'や' one iota'は除外されます。しかし、将来のプロジェクトに手を携えておくと便利です! –

+0

任意の部分文字列をテストするバージョンを追加しました。これは、正規表現を再度使用しますが、N回のテストではなく、エントリごとに1回だけテストします。 – tadman

+0

クール、ありがとう!それは確かに動作します。どのように、なぜ、どのように動作するのか全く分かりません。 –

0

:あなたは、任意の部分文字列の一致を許可したい場合は、配列をスプライスするとき、それは縮小したが、あなたのループは0 .. $#項を反復するので、ループの終わりに$ indは配列の最後を指します。 grep { ... } $array[ $too_large ]を使用する場合、Perlは、存在しない要素をgrepブロック内の$_にエイリアスする必要があるため、配列にundef要素を作成します。

#!/usr/bin/perl 
use warnings; 
use strict; 
use feature qw{ say }; 

my @terms = ('alpha', 'beta test', 'gamma', 'delta quadrant', 'epsilon', 
      'zeta', 'eta', 'theta chi', 'one iota', 'kappa'); 
my @filters = qw(beta gamma epsilon iota); 

for my $filter (@filters) { 
    say $filter; 
    for my $ind (0 .. $#terms) { 
     if (grep { do { 
      no warnings 'uninitialized'; 
      /$filter/ 
     } } $terms[$ind] 
     ) { 
      splice @terms, $ind, 1; 
     } 
     say "\t$ind\t", join ' ', map $_ || '-', @terms; 
    } 
} 

あなたが$terms[$ind] =~ /$filter/代わりのgrepを使用した場合、あなたはまだ初期化されていない警告を取得したいが、要素を別名する必要はありませんように、それは作成されません。

+0

@ikegami:出力に「ガンマ」が表示されません。さらに、これは「修正」ではなく、後続の要素が作成されるWHYおよびWHENのみをデモストレートする必要があります。したがって、まだ存在します。 – choroba

+0

@ikegami:@terms "'を ''印刷 ''していると、私は ''アルファデルタ四分円ゼータηシータカイカッパ 'を見ます。 – choroba

+0

ああ、申し訳ありませんが、あなたが '@terms = qw(gamma gamma kappa);'で始めるとバグが起こります。 2番目のガンマは '$ terms [0]'に移動しますが、これは再訪されません。 – ikegami

関連する問題