配列の削除と splice
配列を操作してて、「ある条件のばあい、その要素を削除」みたいなことをしたい場合。
良くやるのが、
sub remove_item_in_some_condition { my (@input) = @_; my @result = (); for my $item ( @input ) { push @result, $item if( !some_condition($item) ); } return @result; }
みたいなやつです。(まあ map とか grep 使うほうが多いかも)
ただ、引数に配列リファレンスを渡して、そのリファレンスに対して操作したい(渡したリファレンスの指す先の配列の中身を削除したい)場合、この方法は使えません。
# 使えなくも無いのかもしれませんが、リファレンスに対して向き先を付け替えるようなコーディングが必要になり、これはバグの元なのでやりたくありません。
(リファレンスにこの操作をしたい、というのが間違いのような気もするので)何が正解なのか良く分かりませんが、僕はこの場合は splice を使います。
sub remove_item_in_some_condition2 { my ($input_aref) = @_; for ( my $i=0; $i < @{ $input_aref} ; $i++ ) { if ( some_condition($input_aref->[$i]) ) { splice @{ $input_aref }, $i--, 1; } } }
っていうか、splice を使いたいとき、ぐぐってみても
splice @array, $i--, 1;
のサンプルがなかなか見つからなくて、いっつも $i のデクリメントを忘れて、「あれれ?」となるので、メモを残してみたのでした。
splice @array, 2, 3;
みたいなサンプル見せられても、「そんなの使う局面ねーだろ!」と思うんですけどね。
以下、動作する完全なサンプルです。
#!/usr/bin/perl use strict; use warnings; use feature qw(say); my @aaa = (1, 2, 3); my @bbb = remove_item_in_some_condition(@aaa); say @bbb;#=> (1, 3) my @xxx = (1, 2, 3); remove_item_in_some_condition2(\@xxx); say @xxx;#=> (1, 3) sub some_condition { my ($item) = @_; # 条件はなんでもいいのだけど、とりあえず # 「中身が2の場合消す」、とする return 1 if ( $item == 2 ); return 0; } sub remove_item_in_some_condition { my (@input) = @_; my @result = (); for my $item ( @input ) { push @result, $item if( !some_condition($item) ); } return @result; } sub remove_item_in_some_condition2 { my ($input_aref) = @_; for ( my $i=0; $i < @{ $input_aref} ; $i++ ) { if ( some_condition($input_aref->[$i]) ) { splice @{ $input_aref }, $i--, 1; } } }