Hatena::Groupperl

Press::Alt_R

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