2

Perl では、同じ配列の別の要素がその要素の空でない部分文字列である配列からすべての要素を削除したいと思います。

私は配列を持っているとしましょう

@itemlist = ("abcde", "ab", "khi", "jklm");

この例では、 は の部分文字列である"abcde"ため、要素を削除したいと思います。"ab""abcde"

配列のコピーを (おそらくハッシュとして) 作成し、それを反復処理して、元の配列のすべての要素にインデックスを付けて削除することもできますが、もっと洗練された方法が必要ですよね?

ご協力いただきありがとうございます!

わかりやすくするために少し編集しました。

4

6 に答える 6

3

すべてのアイテムから正規表現を構築し、一致するものをすべて捨てることができます:

$alternation = join('|', map(quotemeta, @itemlist));
@itemlist = grep !/($alternation).|.($alternation)/, @itemlist;

これ().|.()は、アイテムがそれ自体と一致しないことを保証するだけです。

于 2013-01-17T15:53:29.237 に答える
1

まあ、これはエレガントとは言えませんが、次のようになります。

#!usr/bin/perl
use strict;
use warnings;

my @itemlist = ("abcde", "ab", "khi", "jklm");

@itemlist = grep { 
    @itemlist ~~ sub {$_ !~ /(?:.\Q$_[0]\E|\Q$_[0]\E.)/} 
} @itemlist;

print "@itemlist";

これは、スマート マッチのあいまいな動作に依存しています。左の引数が配列で、右の引数がサブルーチンである場合、各要素に対してサブルーチンが呼び出され、サブルーチンが各要素に対して true を返す場合にのみ、最終結果が true になります。

説明: 配列の各要素について、他の要素がその要素の部分文字列でないことを確認します (要素がそれ自体と一致しないように、少なくとも 1 つの追加文字が必要です)。

注: wdebeum の答えは、おそらく現実の世界で私が好むものです。それでも、スマート マッチでできる奇妙なことは興味深いものです。

于 2013-01-17T15:47:37.763 に答える
0

wdebeaumの答えは、以下のものではなく、使用するソリューションですが、私はそれを行うことで何かを学びました。おそらく他の誰かもそうするでしょう。私が自分のものを書いた後、数千の要素のリストでテストすることにしました。

b.pl:

#!/usr/bin/perl

use strict;
use warnings;

my @itemlist = <>;
for(@itemlist) { chomp; }
my $regex;

if(defined $ENV{wdebeaum}) {
    # wdebeaum's solution
    my $alternation = join('|', map(quotemeta, @itemlist));
    $regex = qr/(?:$alternation).|.(?:$alternation)/;
} else {
    # my solution
    $regex = join "|", map {qq{(?:\Q$_\E.)|(?:.\Q$_\E)}} @itemlist;
}

my @result = grep !/$regex/, @itemlist;
print scalar @itemlist, "\t", scalar @result, "\n";

5000 のランダムな単語のリストを生成しました。

sort -R /usr/share/dict/american-english|head -5000 > some-words

小さなリストの場合、両方のソリューションがうまくいくようです。

$ time head -200 some-words | wdebeaum=1 ./b.pl
200 198

real    0m0.012s
user    0m0.004s
sys     0m0.004s

$ time head -200 some-words | ./b.pl
200 198

real    0m0.068s
user    0m0.060s
sys     0m0.004s

しかし、より大きなリストの場合は、wdebeum の方が明らかに優れています。

$ time cat some-words | wdebeaum=1 ./b.pl 
5000    1947

real    0m0.068s
user    0m0.064s
sys     0m0.000s

$ time cat some-words | ./b.pl 
5000    1947

real    0m8.305s
user    0m8.277s
sys     0m0.012s

違いの理由は、両方の正規表現に同じ数の可能なパスがあるにもかかわらず、.wdebebaum には 2 つしかないのに対し、正規表現にはパスと同じ数の sがあるため、試行する必要があるより多くのパスがあるためだと思います.

于 2013-01-17T17:15:23.727 に答える
0

ハッシュを使用して、すべての単語の部分文字列をカウントできます。カウントが 1 より大きいリスト内の単語は、別の単語の部分文字列です。この例では、部分文字列の最小の長さは 2 です。

use strict;
use warnings;
use feature 'say';

my @list = qw(abcde ab foo foobar de oba cd xs);

my %count;

for my $word (@list) {
    my $len = length $word;
    $count{$word}++;
    for my $start (0 .. $len - 2) {
        for my $long (2 .. $len - 2) {
            my $sub = substr($word, $start, $long);
            $count{$sub}++;
        }
    }
}
say for grep $count{$_} == 1, @list;

出力:

abcde
foobar
xs
于 2013-01-17T17:19:03.047 に答える
0

以下は、配列から部分文字列を削除します。

#!/usr/bin/perl
use strict;
use warnings;

my @ar=("asl","pwe","jsl","nxu","sl","baks","ak");
foreach my $i (@ar){
  my $p = grep /$i/, @ar;
  if ( $p == 1 ){
    print "$i" , "\n";
  }
} 
于 2013-01-30T12:19:59.387 に答える