2010-08-02
Perlわかば (アルパカ 2.1.1 grep)
grep EXPR, LIST grep BLOCK LIST EXPR/BLOCKは、LISTからの要素を $_ で受ける。
# 桁の合計が奇数になる数字だけを取り出す my @input_numbers = (3 4 6 12 15 21 24 100 311); my @odd_digit_sum = grep{ # 最終的に真偽値を返す処理 my $input = $_; # このブロックのLISTからの受け皿は $_ (サブルーチンとの違いに注意) my @digits = split //, $input # 数字を空の正規表現で分割=1文字ごとに分割 my $sum; $sum += $_ for @digits; # foreach my $keta(@digits){$sum += $keta;} $sum % 2; # 奇数→2による剰余は1→真、偶数→偽 } @input_numbers
Perlわかば (アルパカ本 2.1.1 リストのフィルタリング)
リストとgrep
grep BLOCK LIST
grep EXPR, LIST
BLOCK/EXPRが真になったもののみをリストにして返す
BLOCK: {}で囲まれたブロック。通常の文を書いていくことができる
EXPR : 式(演算子や関数など、1命令で実行できるもの)。
perldoc -f grep すればわかります。
コメントを書く
2010-07-20
Perlわかば (サブルーチンのリファレンス)
例:ファイルを再帰的にたどる
#!perl use strict; # -- 自力で決め打ち関数を書くと my_filefind("./"); # -- ファイルを再帰的に探索してprintする sub my_filefind{ my $filename = shift; # $filename maybe: # a) file # b) directory if ( -f $filename ){print $filename , "\n"; return;} return unless ( -d $filename); print "----opening dir: $filename\n"; opendir my $DH , $filename || die("cannot open directory"); my @filelist = readdir($DH); $filename =~ s/\/$//; #remove slash foreach my $f(@filelist){ next if ($f =~ /^\./); my_filefind($filename . "/" . $f); # join with slash } print "----closing dir: $filename\n"; return; }
File::Findとcoderefを使う
#!perl use strict; use File::Find; find(\&print_filename , "./"); sub print_filename{ print $_ , "\n"; }
補足 - 深さのぶん先頭にspaceをとる
# -- 行頭に深さのぶんスペースをつける sub my_filefind_with_depth{ my $filename = shift; my $depth = shift; # $filename maybe: # a) file # b) directory if ( -f $filename ){print " " x $depth , $filename , "\n"; return;} return unless ( -d $filename); print " " x $depth , "--dir: $filename\n"; $depth++; opendir my $DH , $filename || die("cannot open directory"); my @filelist = readdir($DH); $filename =~ s/\/$//; #remove slash foreach my $f(@filelist){ next if ($f =~ /^\./); my_filefind_with_depth( $filename . "/" . $f ,$depth ); # join with slash } return; }
補足2: File::Findではディレクトリ探索スタックが使えず……?
各ディレクトリのファイルの数を数える。
counterをスタックにローカル変数として確保できれば、counterを足すだけでよいが、やりかたがわからないのでハッシュに入れている。
#!perl use strict; use File::Find; our %counter = (); find( { wanted=>\&print_filename, postprocess=>\&aggr_directory, preprocess=>\&prepare_directory, } , "./"); sub print_filename{ print $_ , "\n"; $counter{$File::Find::dir}++; } sub prepare_directory{ print "-- dir is:" , $File::Find::dir , "\n"; return @_; #given list of files } sub aggr_directory{ print "-- " , $counter{$File::Find::dir} ,"\n"; }
2010-07-16
Perlわかば(シュウォーツ変換)
#!perl use strict; # Schwartzian transform my @DATA = map{chomp;$_}<DATA>; # remove CRLF and store in ARRAY my @sorted_data = map{$_->[0]} sort{$a->[1] <=> $b->[1]} map{[$_, (split /:/, $_)[1] ]} @DATA; ; print map{$_ . "\n"} @sorted_data; __DATA__ Yamada:10:1 Sato:2:1 Kato:5:2 Furukawa:1:1 Suzuki:4:2 Ito:7:3 Yamaoka:3:3 Masuno:6:1 Mizuhara:8:3 Akatsuka:9:1
2010-07-14
Perlわかば (シュウォーツ変換のまえに 1)
- 配列と配列のリファレンス
- push ARRAY, LIST
- ARRAY <==== LIST を追加
#!perl use strict; use Data::Dumper; my @DATA = map{chomp;$_}<DATA>; # remove CRLF and store in ARRAY # 1. 行データ → 行データ, 比較用数値 のセット my @id_data=(); foreach my $line (@DATA){ my @list = split ( ":" ,$line ); push @id_data,[ ($line,$list[1]) ]; } print Dumper @id_data; # 2. 比較用数値でデータをソート # 3. データセットから行データのみとりだし __DATA__ Yamada:10:1 Sato:2:1 Kato:5:2 Furukawa:1:1 Suzuki:4:2 Ito:7:3 Yamaoka:3:3 Masuno:6:1 Mizuhara:8:3 Akatsuka:9:1
ポイント:構造的なデータをつくる
pushの説明が少し違っていたので補足します。
今回、@id_dataにそのまま「行データ」と「切り出したID」を入れると、このようになります。
('Yamada:10:1' , 10 , 'Sato:2:1' , 2 , 'Kato:5:2' , 5 .... )
これを後からソートすることは難しそうですね(偶数番目の要素だけを拾ってソートするとか……?)。
ここは、「行データ」と「切り出したID」を、ひとくくりにして、@id_dataに入れておきたいわけです。
( ('Yamada:10:1' , 10) , ('Sato:2:1' , 2) , ('Kato:5:2' , 5) .... )
しかし、これだけだと、そのまま入れるのと同じです。リストの中にリストを入れようとしても、Perlは最終的には、リストを単なる値の羅列と見ていますので、カッコをほどいてしまいます。
そこで、構造的にデータを表現する「データのまとまり」として、リファレンスが必要になるのです。
( ['Yamada:10:1' , 10] , ['Sato:2:1' , 2] , ['Kato:5:2' , 5] .... )
@id_data の中に、このように、「行データ」と「切り出したID」を含む配列のリファレンスを、一つのデータセットにして入れていくことになります
説明不足だった点
[ ] は、リストのリファレンスではなく、配列のリファレンスです。正確には「無名の配列」と呼ばれます(もっと正確には、無名配列コンストラクタ)。
my @arr = (10,20,30); my $aref = \@arr;
配列を作ってリファレンスをとる、というのを、いきなり
my $aref = [10,20,30];
と書けるようにしてある、というところから、「無名配列」という語感を掴んでいただければと思います。[10,20,30]は、「(10,20,30)という値を含む配列へのリファレンス」ということになりますが、ここで言っている配列には、名前を与えていませんね。
メモ
2010-07-13
Perlわかば (ソート結果のキャッシュ)
#!perl use strict; use utf8; # parse passwd-like textfile and sort lines my @DATA = map{chomp;$_}<DATA>; # remove CRLF and store in ARRAY # sort via field 1 # 「行データを : で分割したリストのうち、[1]番目の要素を比較して行をソート」 #my @sorted_data = sort{(split /:/, $a)[1] <=> (split /:/, $b)[1]} @DATA; my @sorted_data = sort compare_field_2 @DATA; print map{$_ . "\n"} @sorted_data; printf ("total:%d times\n" , counter()); # ================ subs # キャッシュを用いる場合、用いない場合 # (行切り出しは、別関数 get_item() を呼び出し) sub compare_field_1{ #(split /:/, $a)[1] <=> (split /:/, $b)[1]; get_item($a) <=> get_item($b) } {my %cache; sub compare_field_2{ ($cache{$a} ||= get_item($a)) <=> ($cache{$b} ||= get_item($b)); } } # ================ subs (parse) # 行切り出し { my $counter = 0; sub get_item{ $counter++; # 回数をカウント my $line = shift; return ((split /:/, $line)[1]); } sub counter{ $counter = $_ || $counter; return $counter; } } __DATA__ Yamada:10:1 Sato:2:1 Kato:5:2 Furukawa:1:1 Suzuki:4:2 Ito:7:3 Yamaoka:3:3 Masuno:6:1 Mizuhara:8:3 Akatsuka:9:1
メモ
or/||を使った評価
$foobar ||= 100; # $foobarが真 → $foobar; # $fooberが偽 → $foobar = 100;
orのキャッシュを使ったソート
@sorted = sort {($cache{$a} ||= -M $a) <=> ($cache{$b} ||= -M $b)} @files; # (....) <=> (...) で、通常のsortブロックの評価 # カッコ内の値の評価 # $cache{$a} が真(値が既にある) → ($cache{$a}) # $cache{$b} が偽(初めて出てきた)→ ($cache{$a} = -M $a) # 代入式は代入された値をそのまま返すので、 # カッコの中からは結局 -M $a した値(か、そのキャッシュ)が返る # 機能としては以下と全く同じ @sorted = sort {-M $a <=> -M $b} @files;
todo
- こんなことやらずに一次変数を使ったほうがいいんじゃない?→手を動かしてやってみる
- Schwartzian transform