プロが教えるわが家の防犯対策術!

下記のようにx(\d+)y(\d+)z(\d)形式で構成されている値をもつリストがあった時に、期待値のように
z
x
y
の優先順でsortしたいのですが、よろしくお願いします。


@list = qw(
x2048y2z3
x1024y2z5
x1024y4z2
x1024y4z3
x1024y2z2
) ;

期待値の順
x1024y2z2
x1024y4z2
x1024y4z3
x2048y2z3
x1024y2z5

A 回答 (6件)

No4hrm_mmmです。

間違いがありました、済みませんm(_ _)m。
split(/[xyz]/)では、xの前に何もないので先頭に、'' が一個入り、
さらにその先頭に元データを保持させているので、
ということは、sort文の配列添え字番号は1ずつずれますね。

sort{ $a->[4] <=> $b->[4] or $a->[2] <=> b->[2] or $a->[3] <=> $b->[3] }
    • good
    • 0
この回答へのお礼

わざわざご丁寧にありがとうございます。

お礼日時:2005/12/18 22:54

ややトリッキーになりますが、シュワルツ変換とハッシュを使った方法も回答しておきます。



print map { "x$$_{x}y$$_{y}z$$_{z}\n" }
sort { $$a{z} <=> $$b{z} or $$a{x} <=> $$b{x} or $$a{y} <=> $$b{y} }
map { { /([xyz])(\d+)/g } } @list;

解説:
/([xyz])(\d+)/g
これはxかyかzと、値の2項からなるリストを
(gオプションによって)可能な限り展開します。
/^(x)(\d+)(y)(\d+)(z)(\d+)$/
でも構いません。
それを内側のブレース(無名ハッシュコンストラクタ)で受け、
{ x => 1024, y => 2, z => 2 }
こういった無名ハッシュを作成します。
こんな展開を@list回だけで済ませるのがシュワルツ変換です。
    • good
    • 0
この回答へのお礼

ありがとうございます。
こんなに短いコードでも書けるのですね。
自分ではなかなかこういうコードが書けませんが勉強になります。

お礼日時:2005/12/18 22:53

http://www.din.or.jp/~ohzaki/perl.htm#SortMulti
[ 複数の項目でソートする ]
を参考にすると、こんな感じにもできます。

@list = qw(
x2048y2z3
x1024y2z5
x1024y4z2
x1024y4z3
x1024y2z2
) ;

print "x:y:z\n" . join( "\n",
map{ $_->[0] }
sort{ $a->[3] <=> $b->[3] or $a->[1] <=> b->[1] or $a->[2] <=> $b->[2] }
map{ [$_, split(/[xyz]/) ] }
@list
) . "\n";

まあ、無理に1行につっこむ必要もないのだけど、perlだから出来る技ですね。
    • good
    • 0
この回答へのお礼

ありがとうごあざいます。
mapとsortの合わせ技なのですね。
勉強になります。

お礼日時:2005/12/18 22:50

サブルーチンを持つsort関数を使うと複雑なソートも表現できます。



@newlist = sort{&compare($a,$b)} @list;

sub compare
{
my($v1,v2) = @_;
my($v1x,$v1y,$v1z) = split(/[x-z]/,$v1);
my($v2x,$v2y,$v2z) = split(/[x-z]/,$v2);

return $v1z <=> $v2z or $v1x <=> $v2x or $v1y <=> $v2y;
}

それぞれx,y,zの数字を分解して、z,x,yの順番に比較していきます。
    • good
    • 0
この回答へのお礼

sortの{}内にsubファンクションもいけるのですね。
この処理は多用するので、この記述はかなりうれしいです。
さっそくコピーして、モジュール化してしまおうと思ってます。

お礼日時:2005/12/18 22:49

つまりこういうことかと


-----
my @list = qw(
x2048y2z3
x1024y2z5
x1024y4z2
x1024y4z3
x1024y2z2
) ;
@list = sort {
my @a = split(/[xyz]/, $a);
my @b = split(/[xyz]/, $b);
$a[3] <=> $b[3] ? $a[3] <=> $b[3]
: $a[1] <=> $b[1] ? $a[1] <=> $b[1]
: $a[2] <=> $b[2] ? $a[2] <=> $b[2]
: 0;
# 桁が解るなら
# $a[3]*100000000 + $a[1]*100 + $a[2] <=> $b[3]*100000000 + $b[1]*100 + $b[2];
} @list;
e @list;
    • good
    • 0
この回答へのお礼

ありがとうございます。

お礼日時:2005/12/18 22:47

算術比較演算子<=>は、同値のとき0を返し、Perlでは0はfalseなので、1つめの<=>で判定がつかない場合は、2つ目の<=>で大小を判定するよう、||でつないでいく形にすれば、ソートキーが複数の値からなる場合でも単一の式として表現できます。


これをシュワルツ変換にかければ、大抵ソートできます。

値の桁が事前に限定できるなら、単一の値を生成してキーにできます。こんな感じ:
$_ = 'x1024y2z2';
my @k = split( /[xyz]/, $_ );
[ $_, $k[3]*100000000 + $k[1]*100 + $k[2] ];
    • good
    • 0
この回答へのお礼

ありがとうございます。
値の桁数は限定できるので使えそうです。
複雑なsortの具体的な書き方が分からなかったので勉強になりました。

お礼日時:2005/12/18 22:45

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!