perl のワンライナーに関しての、適当な解説 #1/#2/#3/#4
GRT
例えば、ここの TABLE A1 の一番右のセルみたいの。
データからの生物分類は、全て、つまり種に至るまできっちり類推されてくれれば良いのですが、「網まではわかるが後は判らん!」とか、「分類でけんかった」とか、そーいうのがざらに出ます。で、それらをソートするののメモ。
一言で言えば、「guttman rosler transform を使う。」だけなんですが。
で、文字列だけのソートだと、有難味が薄いので、数値と文字列とが混った可変長セルのソートを
まずは、例用のテーブル作り。
$ perl -le 'my %h = map { $_, 1 } <{a,b,c,,},{1,2,3,10,7,,},{a,b,c,,},{1,2,3,10,20,,}> ; print for keys %h' > example.txt
hash 出力に頼って、ランダム擬きの表を作成しました。例なので CSV です。
c,7,,1
a,2,a,3
c,2,a,20
,,a,3
a,10,a,10
b,10,c,2
b,7,,3
c,10,b,2
c,,b,20
a,2,,
コンセプト
では、これをソート
* 優先順位は、左のカラムから
* 昇順
* 大文字小文字は区別
* 数字は当然、数値ソート
* 今回は小数点に関しては考慮してない
* 空欄は最後に
* 文字列は、可変長想定
コード
#!/usr/bin/env perl
use warnings;
use strict;
my @arr ;
LINE:
while ( <> ){
chomp;
my @col = split /,/, $_ , 4 ; #1
my $sort_str = q{} ;
for my $i ( 0 .. $#col ){ #2
$sort_str
.= $i % 2 == 0
? pack q{A30}, $col[$i] || '|' #3
: pack q{L*}, $col[$i] || 100 #4
;
}
push @arr, [ $_, $sort_str ];
}
printf "%s\n", $_->[0] for sort { $a->[1] cmp $b->[1] } @arr; #5
これを普通にコマンドラインから実行するだけ
$ perl GRT.pl example.txt
#1 CSV ですので当然、/,/
で split
- LIMIT は重要。コレを付け忘れただけでドツボに嵌る
- LIMIT 無しだと、後ろの空カラムを無視する
「split
前に chomp
しなきゃいいじゃん」
「アアソウデスネ」
#2 各カラムの内容を舐めます。
- index で回す事にしたのは、今回の例の条件分岐の為
#3 ソート文字列の作成。
- 空白は最後にもって来たいので
|
(ascii 124)を代替文字として pack- 例えば、"Unclassified" を最後から二番目にソートしたいなら、
{
(ascii 123)を代替文字に
- 例えば、"Unclassified" を最後から二番目にソートしたいなら、
-
A*
ではなくA30
にしたのは以下のコード参照- 運用時には文字列長に留意が必要
$ perl -le 'my @a = ( [qw(a z)], [qw(ab a)] ) ; print $_->[0] for sort { $a->[1] cmp $b->[1] } map{ [ ( join "\t", @{$_} ), ( join "", map { pack q{A*}, $_ } @{$_} )] } @a ;'
ab a
a z
#4 ソート文字列の作成(数値)
Q
の方が良いのかも。
整数 pack コード sSlLqQ のそれぞれは、どこでプログラムが 実行されたとしても固定長のバイト列となります
引用元
右詰め 0 でフィルされた固定長数値ならば、#3 のコードに渡してしまえば良いが、往々にしてソートの事を考えて数値入力はされていない。ファイル名とかファイル名とか、ファイル名とか、、、
#5 ソートの実行
二次元配列の '[X][1]' に入れた、ソート文字列を、文字列比較するだけ。
sort
ブロックに or
を重ねるのを想像したら、どれだけ楽だか。
生物の階級で。
それでは、最初のサイトで見た様な、生物の階級による表のソートをした場合
- 中間的分類を除いて domain から 7 階級。文字列のみを期待。
- 'Unclassified' は一回のみ、 domain に表示
のケースを想定してコードを書く。
なお最初に書いたコードは確実にトラブるコードだった、、、
ので、ついでに、より GRT っぽくリライト
$ perl -F';\s*' -Mvars='@a' -lane 'push @a, join q{}, (( map{ pack q{A30}, /^$/ ? q{|} : /^Unclassified$/ ? q{\{} : $_ } (@F, (q{}) x 6)[0 .. 6 ]), $_ )}{ print substr $_, 210 for sort @a ' SOMETHING.txt
-
'; '
は空白が入ってる。入ってないものがあるかも知れないので';\s*'
の方が適切 -F'; ' -a
ではsplit
のリミット指定が出来ないから、(@F, (q{}) x 6)[0 .. 6 ]
で補わなきゃならない- 最初は、
-a
使わずsplit /; /, $_, 7
で書いていたがsplit
失敗時(区切り文字が出現しない)に、要素数 1 のリストになるので、(@F, (q{}) x 6)[0 .. 6 ]
の方にせざるを得ない