March 07, 2010
■ [perl][dark]\(hoge) -> ?

print \(a..z); # SCALAR(0x182fe2c)SCALAR(0x182fe38)...SCALAR(0x182ff4c)SCALAR(0x182ff58)
なにこのミラクル構文。リスト(配列じゃなくてリスト構文)のリファレンス取ろうとすると中身のリファレンスのリスト返すのか。すげー
追記
@a = 0..9; @b = A..Z; @c = a..z; print \(@a); # SCALAR(0x182fd8c)SCALAR(0x182fd98)...SCALAR(0x182fec4)SCALAR(0x182fed0) print \(@a,@b,@c); # ARRAY(0x182f984)ARRAY(0x182fb04)ARRAY(0x182fde0)
リストの中身が複数に分かれると、それぞれのリファレンスを返すみたい。
ちなみに、複数の配列のすべての中身のリファレンスが欲しい時は
print \(@_=(@a,@b,@c)); # SCALAR(0x2861ac)SCALAR(0x286284)...(many)...SCALAR(0x1830368)SCALAR(0x1830374)
のように、一つの配列に代入してやるといいみたい。いいシュガー発見した気がする。
追記2
print \(@{[@a,@b,@c]});
こっちのほうがスマートですね。
■ [perl][func][memo]配列をshuffle(ランダムに並べ替えたい)/take potluck(ランダムに抜き出したい) - 爆速編

前回の更新ではベンチ取らないでやってたので今回は高速化に挑戦。
まずはベンチマーク
使ったコードはこちら。
#!perl use Benchmark qw(timethese cmpthese); use List::Util; my @a = 'Aa'..'Zz'; # 0 .. 675 cmpthese(timethese(10000, { 'L::U::shuffle' => sub { List::Util::shuffle(@a) }, shuffle_simple => sub { shuffle_simple(@a) }, shuffle => sub { shuffle(@a) }, L_U_shuffle => sub { L_U_shuffle(@a) }, })); sub shuffle_simple { return sort { int(rand 3) -1 } @_ } sub shuffle { my @old = @_; local $_; my ($i, $new) = ($#old+1, 0); map { $_ = $old[$new = rand $i--]; $old[$new] = $old[$i]; $_; } 0 .. $#old } sub L_U_shuffle { my @old = \(@_); my $n; my $i = @_; map { (${$old[$n = rand $i--]}, $old[$n] = $old[$i])[0]; } @_ }
L_U_shuffleはList::Util::shuffleのpure perl版です。
で、結果が以下。
Rate shuffle L_U_shuffle L::U::shuffle shuffle_simple
shuffle 215/s -- -73% -97% -100%
L_U_shuffle 794/s 270% -- -90% -99%
L::U::shuffle 7622/s 3453% 859% -- -94%
shuffle_simple 125000/s 58171% 15635% 1540% --
…(shuffle_simpleはともかく)勝負にならんな!あとXSのshuffle速すぎ。
なんでこんなに差がつくかというと、L_U_shuffleは実体をコピーするのではなくさっきの記事の方法を使ってリファレンスのリストを作ってそいつを回しているのが原因だと思う。余計な代入も一切発生しない。あと僕の作ったshuffleはmap回すのに0..$#oldみたいないらん配列作ってるしさらに遅い。で、3倍近く差がつく。この点を改良して最速shuffle/take potluckを作ります。
並べ替え
sub shuffle_fast(@) { my @old = \(@_); my $n; my $i = @_; map { (${$old[$n = rand $i--]}, $old[$n] = $old[$i])[0]; } @_; }
ええパクリです。でも僕の環境だとpure perlのList::Util::shuffleの倍は出ます。何故か。mapの中を1行にしただけなのに。
参考ベンチ(試行回数1000)
Rate shuffle L_U_shuffle shuffle_fast
shuffle 216/s -- -46% -72%
L_U_shuffle 396/s 84% -- -49%
shuffle_fast 774/s 259% 95% --
適当に抜き出す
sub potluck_fast($;$) { my $i = my @a = \(@{+shift}); my $n = shift || int rand $i; $n = $i if $n > $i; map { (${$a[$n = rand $i--]}, $a[$n] = $a[$i])[0]; } 0 .. $n-1; } warn potluck_fast([0..9,A..Z,a..z],20); # XK7kgQq6Dn1RLVa8ozOW
ぱくぱく。使い方は前回同様、第1引数に配列のリファレンスを、第2引数に抜き出したい数を渡します。
参考ベンチ(試行回数1000)
Rate potluck potluck_fast
potluck 248/s -- -73%
potluck_fast 925/s 273% --
おしまい
爆速って程でもなかったかな。しかし既存のshuffleよりはずっと速くなったはず。そのへんのよりは3倍速い。
List::Util使えるんだったらshuffleはList::Util使いましょう。爆速です。potluckみたいなことしたいときも
(List::Util::shuffle('Aa'..'Zz'))[0..rand(676)]
とかすれば爆速間違いなし。XSにはどう頑張っても勝てんわ。