Hatena::ブログ(Diary)

ダウンロードたけし(寅年)の日記 このページをアンテナに追加 RSSフィード Twitter

2010-06-10

知ってそうで意外と知られていないperlの小技 10選

意外と知られていないperlテクってのが、意外とあるもんですね。

最近身の回りでいくつか話題に上がったものがあったので、ちょっと書いてみます。

どれも最新のモダパ的なモノではないけども、知っておくと地味に便利かもしれないノウハウです。

中級レベル以上のperlユーザの人たちでも「お、こんなの知らなかった」というのもあるかもね。


複数項目でのソート

よくエクセルなんかで「A列を降順、B列を昇順にして並び替え」みたいなことしますよね?

perlで複数項目のsortではどうすればできるでしょうか?

じつはとっても簡単。sortの次に続くブロックの中でorするだけです。

例えば以下のような4人の子供たちのデータを年齢順、体重順でソートしてみます。

use strict;

my @data = (
    {   name   => '太郎',
        age    => 10,
        weight => 25,
    },
    {   name   => '花子',
        age    => 9,
        weight => 23,
    },
    {   name   => '次郎',
        age    => 10,
        weight => 27,
    },
    {   name   => 'よし子',
        age    => 9,
        weight => 21,
    },
);

my @sorted
    = sort { $b->{age} <=> $a->{age} || $b->{weight} <=> $a->{weight} } @data;

for (@sorted) {
    print $_->{name}, "\t", $_->{age}, "歳\t", $_->{weight}, "キロ\n";
}

次郎 10歳 27キロ

太郎 10歳 25キロ

花子 9歳 23キロ

よし子 9歳 21キロ

おお、簡単ですね。

これを知らないとうっかり自分で関数を書いてしまいそうになります。ひゃー。

ランダムシャッフル

続いてこれもソートねた。

配列をランダムにシャッフルします。

use Data::Dumper;

my @data = qw( 0 1 2 3 4 5 6 7 8 9 );

my @shuffle = sort { rand() <=> 0.5 } @data;

print Dumper \@shuffle;  #結果はランダムにシャッフルされている

え、これだけ?なんで?って思うかもしれませんが、よーく考えてみてください。

ふーむ。よく考えたもんだ!

GUIアプリをつくる

がらっと志向を変えてみます。

実はperlwindowsアプリを作れるって知ってましたか?

Win32::GUIWin32::GUI::Loftを使えば簡単にGUIアプリケーションを作れてしまいます。

もしくはGtkとGtk2::GladeXMLを使えばwindowsだけでなくmaclinuxな環境でも動作するGUIアプリを作れれます。

これらのツールで書いたスクリプトをPARで実行可能なバイナリ形式にパックすれば、perlインタプリタが入っていない環境にも配布できるよ!

具体的に書いているとすごい量になってしまうので参考サイトを書いておきます。

http://blog.remora.cx/2010/03/gui-programming-with-win32-gui-loft.html

http://perl-mongers.org/2008/06/perl-and-gtk.html

あとついでにWin32::GuiTestを紹介しておきます。

これのSendKeysという関数をうまく使えば、フォームなどへのデータの流し込みとかができて面白いです。

周りの人に見せると、たぶんびっくりされます。

http://perldoc.jp/docs/modules/Win32-GuiTest-1.3/GuiTest.pod


ハッシュ配列の相互変換

perlの中級者ぐらいの人でも以外と知らない事実。

実はハッシュアレコンテキストで評価すると配列になります。

逆もしかりです。

my %hash = (
    'ブレンド'     => 540,
    'アメリカン'   => 540,
    'カフェオーレ' => 650
);
my @array = %hash;    # @arrayの中身は qw( 'ブレンド' 540  'アメリカン' 540  'カフェオーレ' 650 )

my %hash2 = @array;  #%hash2の中身はは%hashと同じ

この性質はクラスのコンストラクタでよく使われているテクだったりします。

sub new {
	my $class = shift;
	my %args  = @_;
	return bless \%args, $class;
}

デフォルト引数を設定する場合なんか、こういう風に書かれたりもします。

sub new {
	my $class = shift;
	my %args  = (
		foo    => 'bar',
		hoge => 'fuga',
		@_
	);
	return bless \%args, $class;
}

なるほどね。って感じではないでしょうか。

evalのあぶない使い方

evalと言えば例外処理、という固定観念はありませんか?

evalはもっとエキサイティングな用途でも使えます。

evalは渡されブロックをperlスクリプトとして実行します。

なので動的に文字列としてプログラムコードを生成して、それを実行させることができるわけです。

わざとらしい例を1つあげます。

ログを集計する場合に「店舗別の月別の商品別の販売個数を集計したい」というようなタスクがあったとします。

でもこの「○別の△別の〜」といった集計項目が何個あるか、プログラムを実行するまでわからないような条件であったとしましょう。

(現実的にはそんな条件ってないと思うけど)

use strict;
use Data::Dumper;

my $data;

while (<DATA>) {
    chomp $_;

    # 一番最後のカラムに数値が入っているつもり
    # それ以外のカラムは集計軸として使うという前提
    my @f = split( "\t", $_ );
    my $value = pop @f;

    # 動的にデータ構造を作る
    my $string = '$data';
    for (@f) {
        $string .= '->{\'' . $_ . '\'}';
    }
    $string .= '+=' . $value . ';';

    # エバる
    eval $string;
}

print Dumper $data;

__DATA__
横浜店  2010年05月      商品A   割引なし        2
横浜店  2010年06月      商品A   割引あり        7
田町店  2010年06月      商品A   割引なし        2
田町店  2010年05月      商品B   割引なし        4
横浜店  2010年06月      商品C   割引なし        8
横浜店  2010年06月      商品C   割引あり        12

なんと、事前に定義することなく動的に多段のハッシュなデータ構造ができてしまいました。

でも、ある意味でこれはとても危険な行為です。ログの文字列にへんなコードが混入してたらアウト。1行ごとのevalするので実行速度も遅いしね。

くわしくはこちらを見てください。

http://blog.livedoor.jp/dankogai/archives/51175261.html

しかしリスクを十分わかった上で、使いどころを間違えなければ、evalは十分に便利な道具です。私はevalが以外とすきです。


perltidy

どんなにいいコードを書いてもインデントの幅がまちまちだったり、きたなーい感じのソースだとイマイチですよね。

そんなときはPerl::Tidyでソースコードを整形してあげましょう。一発でプロっぽい見栄えに変身します。

さぁ、今すぐ cpan -i Perl::Tidy しましょう。perltidyというコマンドが使えるようになります。

use strict;
use   warnings;


for(1.. 10){
print $_;
		print "\n";
        }

こんな悲しくなるほどきたないコードが

use strict;
use warnings;

for ( 1 .. 10 ) {
    print $_;
    print "\n";
}

おお、まっとうな見栄えになりました。

vimつかってるならば :%!perltidy とするだけどOK!

とても便利です。

perlとだけ叩いたら

perlはコードを書いてファイルに保存して実行させるか、-e オプションワンライナーとして実行させるか、どちらかで動かすものと考えていませんか?

そんなあなたは、まずコンソールに向かって「perl」とだけ打ってリターンしてみてください。

続いて print "hello world\n"; と叩いてみてください。

最後にCtrl-D。

おお!こんな動かしたもあったのか!

って知ってましたか?常識だったかな?

モジュールの格納場所をしる

@INCはとても有名。perlユーザならだれもが知ってる特殊変数ですね。

インクルードパスが格納されています。

一方、なぜか %INC は知名度が低いようです。

%INCにはそのプログラムでロードしているモジュールのパスがハッシュとして格納されています。

use LWP::Simple;

while ( my ( $key, $value ) = each %INC ) {
    print $key, "\t", $value, "\n";
}

これをじっこするとこうなります。

Godzilla:Hacks miki$ perl test.pl

warnings.pm /System/Library/Perl/5.10.0/warnings.pm

warnings/register.pm /System/Library/Perl/5.10.0/warnings/register.pm

LWP/Simple.pm /System/Library/Perl/Extras/5.10.0/LWP/Simple.pm

Exporter/Heavy.pm /System/Library/Perl/5.10.0/Exporter/Heavy.pm

Exporter.pm /System/Library/Perl/5.10.0/Exporter.pm

HTTP/Status.pm /System/Library/Perl/Extras/5.10.0/HTTP/Status.pm

vars.pm /System/Library/Perl/5.10.0/vars.pm

strict.pm /System/Library/Perl/5.10.0/strict.pm

モジュールを書いていて、そのモジュール自身がインストールされている場所を知りたい、なんて時にはこのハッシュから自分自身のパッケージ名を探せばパスがわかります。

知っておくとちょっと便利です。

モジュールのメソッドを上書きする

CPANモジュールを使っていて、「ここのメソッドのこの処理、どうしてもちょっとだけ変えたいんだよなぁ」というようなケースってありますよね?

わざわざ継承するのも大げさっていうような場合は思い切ってそのクラスの当該メソッドを再定義してしまいましょう!

use strict;
use warnings;

package Foo;

sub do_something {
    print "なにか処理します", "\n";
}

package main;

# オリジナルのメソッドのコードリファレンスを保持しておく
my $origin = Foo->can('do_something');

# 型グロブで上書き
no warnings 'redefine';
*Foo::do_something = sub {
    print "このメソッドはノットッタゼ!", "\n";
};

# 実行してみる
Foo->do_something();    #ノットタレタゼ!

# もとに戻す

no warnings 'redefine';
*Foo::do_something = sub {
    $origin->();
};

# もう1回実行してみる
Foo->do_something();    # なにか処理します

実行結果はこうなります。

このメソッドはノットッタゼ!

なにか処理します

ちょっとお行儀わるいかもしれませんが、知っておくと便利なテクですね。


perldocをもう少し

perldocって便利ですよね。わざわざブラウザCPANサイトに行かなくてもPODのドキュメントをコンソールで閲覧できるわけです。

そんな便利なperldocをもう少しだけ便利に使うオプションです。

モジュールの格納されているパスを知るためには -l オプションをつけます。

perldoc -l

Godzilla:Hacks miki$ perldoc -l JSON

/Library/Perl/5.10.0/JSON.pm

さらにソースを直接みたい場合は -m オプションでソースを開くことができます。

perldocは便利だなぁ。




以上、なんの脈絡もなく10個の「意外と知られていないテクニック」を紹介してみました。

enjoy!

aa 2010/06/13 10:05 shuffleはList::Utilにあるよ!

あのにますあのにます 2010/06/14 13:02 >ランダムシャッフル
一見、シャッフルされているように見えるんですけど……

「ランダム ソート」でググると一番目に出る
ランダムソート(笑)とは http://d.hatena.ne.jp/nishiohirokazu/20071121
>誰が「ソートするときに比較関数に『ランダムに1か-1を返す関数』を与えたらシャッフルできる」って言い出したのかしらないけど、真に受ける方も真に受ける方だと思う。

download_takeshidownload_takeshi 2010/06/14 21:20 あいたたた。。。
自分でも検証してみましたが、ご指摘のとおり、この方法ではランダムシャッフルにならないですね。やっぱりList::Utilのshuffleがいいです。

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証