LoginSignup
2
0

More than 3 years have passed since last update.

【'20/5/23追記】配列から特定条件「以降」の要素をgrepで取り出す

Last updated at Posted at 2018-04-11

grepは本来、条件に合う要素だけを配列から取り出す。
でも頑張ればそれだけじゃないこともできるっぽい。

ということで。

@arr1 = qw/pikachu kairyu yadoran pijon/;
@arr2 = qw/kodakku koratta zubatto gyaroppu/;
@arr3 = qw/sandasu menokurage/;

このような文字列配列から、"k"で始まる要素以降の要素をgrepで取り出してみる。

grep_after.pl
#!/usr/local/bin/perl
use strict;
use warnings;

my @arr1 = qw/pikachu kairyu yadoran pijon/;
my @arr2 = qw/kodakku koratta zubatto gyaroppu/;
my @arr3 = qw/sandasu menokurage/;

sub after_k
{
    print $_[0], "\n";
    @_ = eval $_[0];    # 文字列を変数名として使用(printで変数名を表示したかっただけ)
    print "@_\n";

    my $flg = 0;
    @_ = grep { $flg = /^k/ ? 1 : $flg } @_;    # kで始まる要素「以降」の要素を取り出す
    # 【2020/5/23】更に簡潔になる追記あり(上記コードは追記未適用)
    print "@_\n";
    print 'flg: ', $flg, "\n\n";
}

after_k('@arr1');
after_k('@arr2');
after_k('@arr3');

実行結果:

@arr1
pikachu kairyu yadoran pijon
kairyu yadoran pijon
flg: 1

@arr2
kodakku koratta zubatto gyaroppu
kodakku koratta zubatto gyaroppu
flg: 1

@arr3
sandasu menokurage

flg: 0


もちろんここがミソ。

my $flg = 0;
@_ = grep { $flg = /^k/ ? 1 : $flg } @_;    # kで始まる要素名「以降」の要素を取り出す

"k"で始まる要素が来た時にだけフラグを立て、それ以外ではフラグを維持する、という動きを三項演算子でやった。


ちなみに最初はこうしていた。

grep { $flg = /^k/ ? 1 : $flg; $flg > 0 } @_;

しかし、Perlでは0は偽/1は真になるので、この事例ならあえて> 0と明示せずともこれで良いのでは? と思い…

grep { $flg = /^k/ ? 1 : $flg; $flg } @_;

しかし、そんな風に変数をそのまま真偽として渡すなら、わざわざ別途真偽の形にせずともその前で既に$flg =の形で取り沙汰してるんだからそれで代えられるのでは? と思い…

grep { $flg = /^k/ ? 1 : $flg } @_;

というわけで結局こうなったのでした。


他にもmapと組み合わせたりハッシュの何たらを利用したりみたいな方法もありそうだけど、ひとまずこれで。
(ハッシュのことがよくわかってないなんて言えない)


【2020/5/23 追記】

三項演算子の分岐先が単なる真偽なら、もう短絡評価 || で十分だった…。

grep { $flg = /^k/ || $flg } @_;

なお、Perlに真偽値型みたいなものは存在せず、比較演算子とかが生み出す「真」は具体的には 1 を返す。
これはパターンマッチ判定でも同様で、一致した際に返ってくるのは単なる 1 であり、決して文字列の一致部分とか $n$ 文字目を指す数値とかではない。

my $v0 = 'a' eq 'a';        # 1
my $v1 = 'abc' =~ /b|c/;    # 1
my $v2 = 'abc' =~ /(b|c)/;  # 1
my $v3 = 'abc' =~ /b|c/g;   # 1
print "$v0, $v1, $v2, $v3";
# -> 1, 1, 1, 1

※ ただし上記はスカラコンテキストの話であり、リストコンテキストでは異なる。今回は無関係なので畳んどくけど…。
  • Perl の演算子と優先順位 > 正規表現のクォート風の演算子 - perldoc.jp
    • /g オプションが使われなかった場合、リストコンテキストでの m// はパターンの中の括弧で括られた部分列にマッチしたもので構成されるリストを返します。 …中略… パターンに括弧がない場合は、返り値は成功時はリスト (1) です。
    • /g 修飾子は …中略… リストコンテキストでは、正規表現内の括弧付けされたものにマッチした部分文字列のリストが返されます。括弧がなければ、パターン全体を括弧で括っていたかのように、すべてのマッチした文字列のリストが返されます。

つまりこう。

my @a1 = 'abc' =~ /b|c/;    # (1)
my @a2 = 'abc' =~ /(b|c)/;  # ('b')
my @a3 = 'abc' =~ /b|c/g;   # ('b', 'c')
sub j { return join ', ', @_ }
print join(' / ', j(@a1), j(@a2), j(@a3));
# -> 1 / b / b, c

そして今回は $flg = /^k/ || $flg というスカラ変数への代入なので無関係。

故に、追記後のコードによる「実行結果:」は、print 'flg: ', $flg の部分も含めて追記前と全く変わらない。

【追記ここまで】


余談。
配列をprintする際、以下が全部違う結果になったのでビビった。

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

my @arr1 = qw/pikachu kairyu yadoran pijon/;

print "@arr1\n";
print @arr1, "\n";
print @arr1."\n";

実行結果:

pikachu kairyu yadoran pijon
pikachukairyuyadoranpijon
4
2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0