Hatena::Diary

Perl入門〜サンプルコードによるPerl入門〜 このページをアンテナに追加 RSSフィード

YouTubeプレイヤー YouSukeをリリースしました。 紹介記事(2009/1/4)
Twitterをはじめました。(2008/12/14), Webミニ英和・和英辞典「翻訳の小窓」をリリースしました。(2008/11/22)
サンプルコード中心のPerl入門です。 (ビュアー目次 ,Perlインストール, コマンドプロンプト )
サンプルコードをコピーすれば、そのままPerlで実行して、試すことができます。(旧詳細目次,旧カテゴリ別目次)
デバッガで、サンプルコードをたどれば理解が深まります。 (デバッガの使い方)

2009-02-21

Test::More 真偽値、数値や文字列の比較を行う関数

 前回はTest::Moreの役割や関数について少し述べました。

 今回は実際に自動試験をおこうなための関数について解説します。

真偽値、数値や文字列の試験のための関数一覧

 真偽値、数値、文字列、オブジェクトの中身などを試験するには、以下の関数を使用します。

関数解説
okTest::Simpleと同じもの。真偽値の確認に使用
iseqを使っての文字列が等しいことを確認。整数の数値比較はこれでもOK
isntneを使って文字列が等しくないことを確認。整数の数値比較はこれでもOK
like文字列が正規表現にマッチすることを確認
unlike文字列が正規表現にマッチしないことを確認
cmp_ok==, <, >, <=, >=, gt, lt, などPerl演算子を指定して比較
is_deeply配列の配列など複雑なデータ構造の比較に用いる

関数の解説

1. ok

 okは、Test::Simpleのokとまったく同じです。


2009-02-20

Test::More 自動試験をさらに便利にする

 入門的な自動試験のためのモジュールであるTest::Simpleについては解説しました。今回は、Perlの自動試験を行うためのスタンダードにあたるTest::Moreというモジュールについて解説します。

Test::Moreの便利なところ

 Test::Simpleは、ok関数という自動試験のための関数がありますが、これの不便なところは、どんな理由で試験が失敗したかということがわからないことです。

 Test::Moreを使えば、どのような理由で試験に失敗したのかということまでわかります。

 また配列の配列やハッシュの配列などの複雑なデータ構造を試験することもできます。

Test::Moreの関数

 Test::Moreの関数の一覧です。

1. 真偽値、数値や文字列の試験のための関数
関数解説
okTest::Simpleと同じもの。真偽値の確認に使用
iseqを使っての文字列が等しいことを確認。整数の数値比較はこれでもOK
isntneを使って文字列が等しくないことを確認。整数の数値比較はこれでもOK
like文字列が正規表現にマッチすることを確認
unlike文字列が正規表現にマッチしないことを確認
cmp_ok==, <, >, <=, >=, gt, lt, などPerlの演算子を指定して比較
is_deeply配列の配列など複雑なデータ構造の比較に用いる
2.モジュールの読み込み、クラスの定義などを試験するための関数
関数解説
can_okクラスがあるメソッドを持つかどうかの確認
isa_okオブジェクト継承しているクラスの確認、リファレンスの種類の確認
use_okモジュールがuseできることを確認
require_okモジュールがrequireできることを確認
3.自動試験の内容の補足するための関数
関数解説
diag試験が失敗した内容を詳しく伝えたいときに使う
noteデバッグ用に値を確認したい場合に使う
explain配列の配列など複雑なデータ構造の内容を表示
SKIP:(ラベル)環境依存するコードをスキップするために使用
TODO:(ラベル)しなければならない試験を記述するために使用

Test::Moreの記述

 Test::Moreを使用するには、Test::Simpleと同じようにテストの数を指定する必要があります。'no_plan'としてテストの数を指定しないこともできます。

use Test::More tests => 23;

あるいは

use Test::More 'no_plan';

次回は各関数について解説します。

no_planについて

no_planは、「テストを弱くするのでなるべく避けてください」とドキュメントにあるのですが、何が弱くなるのかよくわからない。

「no_planはTest::Harnessの新しい機能なので、Test::Harnessが古い場合は、すべてのテストが失敗します」と書いてありますが、そういう状況は存在するのかなぁ? よくわからない。

 (テスト数を指定すると、新しいテストを追加したときに、テスト数の記述を増やすのを忘れて、テスト成功なんてことがよくあるので、できればno_planで済ませたい。)

サイト案内

自動試験の目次へ

目次へ

ビュアーへ

トップページへ

2009-02-19

「メソッドチェーンはわかりにくい」といった話の続き

 メソッドチェーンはややこしいと思うという記事を書いたらcharsbarさんが メソッドチェーンの話という記事を書いてくださったので、紹介しておきます。

 もしからしたら私が慣れていないだけで、メソッドチェーンは読みやすいという人のほうが多いのかもしれません。

でも今の私が思っていること

 前回のサンプルの

my $loader = Loader->new;
my $book = $loader->load( 'Book' )->build

このコード。

 どのように書いてほしいかというと、

my $loader = Loader->new;
$loader->load( 'Book' );
my $book = $loader->build

と書いてほしいです。

 こう書けば、loadが何をしているのかが、コードから推測できます。buildが$loaderから呼ばれたことがコードを読んでわかります。

 でも、一番素直に書けるのが良いと思ってます。loadとbuildを順番に呼び出すメソッドがあるなら、、load_build を使うのが一番良いと思います。

my $book = Loader->new->load_build( 'Book' );

 こういう風に記述できる選択肢があるのに、

my $book =  Loader->new->load( 'Book' )->build

 という記述をあえてする必要はないのじゃないでしょうか?

 newというメソッドは自分自身を返すことが自明です。だから、メソッドチェーンでつなげても誤解は起きません。けれど、loadが自分自身を返すというのは、自明ではないです。

ゲッターのチェーンとセッターのチェーン

 ゲッターのチェーンは使ってもよいと思っています。

my $query_string = $page->url->query_string

 は、「ページのURLクエリ文字列を取り出す。」と読めます。

 でもセッターのチェーンは避けたほうが良いと思います。

$book->author( 'a' )->name( 'b' )->to_string;

 以下と比較して、記述は非常に良く似ているのに、意味はまったく違います。

$book->author->name->to_string;

 めんどうでも、

$book->author( 'a' )
$book->name( 'b' )
$book->to_string;

と書いたほうが、可読性が高いと思います。

 個人的には英語として自然に読めるかよりも、プログラムとしてわかりやすいかどうかのほうが大事だと思っています。

追記

 もう一度charsbarさんから返答がありました。

charsbarさんのこの記事への返答

 気分を害したのでしたらすみませんでした。Mojoのコードをもう少し読んでみます。

2009-02-18

メタプログラミングとは

 最新版のSimo( バージョン 0.0601 )では、アクセッサをメタプログラミングという手法を使って記述しました。( Mojo::Base(ソースコード) が参考になりました。 )

 メタプログラミングという手法を覚えたので、メタプログラミングとは何かということを解説しておきます。

メタプログラミングとは

 メタプログラミングとはソースコードを生成するプログラミングのことです。メタプログラミングによって生成したソースコードは、eval関数で実行することができます。

 メタプログラミングとは、プログラムの実行時にソースコードを記述し、記述したソースコードを実行する手法」といえます。

簡単なメタプログラミング

 「print $num」というソースコードを作成して、evalで実行しています。1と表示されます。

use strict;
use warnings;

my $num = 1;

my $src = 'print $num'; # メタプログラミング( ソースコードの記述 )

eval $src; # eval で記述したソースコードを実行

__END__

条件分岐の伴うメタプログラミング

 OSWindowsの場合は、

sub func{
    return 'Windows';
}

 Windowsでない場合は、

sub func{
    return 'Not Windowss';
}

という関数を定義するメタプログラミングをしてみます。

 することは、関数の定義を文字列で作成して、evalで実行するだけです。

 qq//はダブルクォート演算子で、ダブルクォートが使用できること以外はダブルクォートと同じです。.= は、現在の文字列にさらに文字列を結合する場合に使います。

 

use strict;
use warnings;

my $func;

# メタプログラミング( OSに応じた func関数の定義を作成 )
$func .=
    qq/sub func{\n/;

if( $^O eq 'MSWin32' ){
    $func .=
    qq/    return 'Windows';\n/;
}
else{
    $func .=
    qq/    return 'Not Windows';\n/;
}

$func .=
    qq/}\n/;

eval $func; # 関数が定義される。

print func(), "\n"; # 実行してみる

__END__

 このようにメタプログラミングを使用すると関数自体を実行時に定義することもできます。

この記事への指摘

その1

 弾さんから指摘を受けたのでポイントを掲載しておきます。

perl,javascript and more - evalは最後の武器

evalだけがメタプログラミングの技法ではないし、またevalはその威力ゆえ最後の選択肢とすべきだ。

→ これはそのとおりで、ほかにC言語プリプロセッサの処理なども一種のメタプログラミングといえます。

( evalの評価は、無名サブルーチンへのリファレンスと比べて )30倍の速度差が出ている。

→これもそのとおりで、文字列をコンパイルして実行するため、evalは遅いです。

その2

 宮川さんが弾さん意見にはだいたい同意はするけれど、evalを毎回ループしてベンチマークとるのはおかしいという指摘。

メタプログラミングとevalのベンチマーク

実際のメタプログラミングの手法を考えると、まずこういう風に subref を eval をつかったほうほうとそうでない純粋な無名関数とでbenchmarkの外側で定義して実行時コストを比較すべき。

 eval自体は遅いけれど、サブルーチンを定義する最初の一回が遅いだけで、その後は速度的には同じになるということを示すベンチマークテスト。

その3

 ひがさんが、メタプログラミングの良いところと悪いところについて語る。

 メタプログラミングの光と影

高位ロジックが、高度であればあるほど、最終的に何が起こるのかが、わからなくなってしまうことです。黒魔術的といいましょうか。

だから、メタプログラミングは、結果が予想できる範囲にとどめておいたほうが良い。強力なため、乱用は厳禁ということです。

 メタプログラミングは複雑になると、デバッグをするのもコードを読むのも大変。

サイト案内

目次へ

ビュアーへ

トップページへ

2009-02-17

メソッドチェーンは非常にややこしいと思う。

 以下のようなメソッドチェーンは読むのが困難だ。

my $loader = Loader->new;
my $book = $loader->load( 'Book' )->build

 loadメソッドが自分自身( $loader )を返却して、そこからbuildが呼ばれたのか、他のオブジェクトが返却され、そこからbuildがよばれたのかがわからない。

 だから、あんまりメソッドチェーンを多用しないでほしい。Mojoソースコードを読んでいてそう思った。

話の続き

2009-02-16

ファイル名を実行環境のOSでのファイル名に変換する。

 OSに依存しないファイル名を作成するには、File::Spec->catfile を使えばよいのでした。

 今回は、あるファイル表現を実行環境のOSのファイル名に変換する方法を解説します。

 設定ファイルなどでファイル名を指定するときは、Unixでのファイル表現を使う場合が多いです。

# Unixでのファイル表現
dir1/dir2/file.txt

 Windows上でこのUnixのファイル表現ををWindowsのファイル表現に変換したいとします。以下のようにするのが簡単です。

 File::Specの、splitdirメソッドで、$unix_pathを個別の部分に分解します。それを、catfileメソッドでつなげます。catfileは、分解されたファイル名を\で結合してくれます。

use File::Spec;
my $unix_path = 'dir1/dir2/file.txt';
my $windows_path = File::Spec->catfile( File::Spec->splitdir( $unix_path ) );

 

実行可能なサンプル

use strict;
use warnings;

use File::Spec;
my $unix_path = 'dir1/dir2/file.txt';
my $windows_path = File::Spec->catfile( File::Spec->splitdir( $unix_path ) );

print "1. ウインドウズのファイルパスに変換\n";
print $windows_path, "\n";

__END__

Windowsでの出力結果

1. ウインドウズのファイルパスに変換
dir1\dir2\file.txt

サイト案内

ファイルとディレクトリの目次へ

目次へ

ビュアーへ

トップページへ