ROUTE 3390

備忘録的な用途で書いていますが、どなたかの役に立つ事があれば嬉しいです。

Perl Beginners #6 に参加しました

2013/01/25 Perl Beginnersに参加してきました。

この勉強会への参加は今回が2度目。

前回はBeginnersセッションというコーナーでガチガチに緊張しながらも登壇し、

「RestAPIを提供するために」という内容で発表と質問をしました。

ひどい発表と質問にも関わらず、皆さん温かい雰囲気で汲み取って下さって、色んなアドバイスを頂けてとてもためになったので、次はLTやってみたいな〜って思ってました。

 

今回のテーマは「配列」という事だったので、

自分なりに色々考えて、「配列で遊んでみた」というタイトルでLTをやらせて頂きました。

 前回の発表でちゃんと練習しなくちゃダメだなって反省して、社内LTとかで場数を踏んだりしたのに、資料作成もギリギリになって結局ガチガチに緊張してしまった。

話したい内容の10%も伝えられなかったけど、PerlBeginnersに参加する皆さんホント優しい雰囲気で聞いてくれました。ありがとうございます。m(_ _)m

 (YAPCのような場所でも堂々と話せるようになるために、同じ失敗はもう繰り返さないようにしよう)

 

尚、この日はBeginnersセッションは発表者がいなかったために中止で、

代わりに「モダンPerl入門」の著者でYAPCの運営をして下さってる牧さんに色々聞けるQ&Aセッションになりました。

 

(下記、言葉使いとか実際と違います)

質問:英語が堪能ですが、コメントは日本語と英語どちらで?

回答:githubにあげるのは英語。仕事でみんなが見るようなのは日本語。

 

質問:CPANモジュール作る時はひな形とか使うのか?

回答:使わないでゴリゴリ書く。XS書くときはおまじないを沢山書く必要があるから、自分のをコピーすることはある。

じゃあmakamakaをnakanakaにするモジュールでも作ってみよっか。(って解説しながら7分くらいでCPANに上げる直前まで作成してた。タイピングも早すぎ!!)

 

質問:XSモジュールについて書かれた書籍とか

回答:「モダンPerl入門」って本があってね 笑

あと数百冊売れないと印税が、、

 

質問:最近あった「これやっちゃいけないよ」みたいのは?

回答:定数たたみこみを使ってほしい。

毎回評価する必要のないものは定数で!(実際にコードを書いて説明してくれた)

 

質問:requireを使うときって

回答:遅延評価を行いたいときか、外部ファイルを読み込むとき。

(これも実際にコードを書いて説明してくれた)

delete @INC (モジュール名)ってやればもう一回requireできるよー

 

質問:perlで設定ファイルを読み込む時のベストプラクティスって

回答:jsonとか書くぐらいならrequireするかな〜

なにが良いって、同じような情報をたくさん書く時とかにコードが書ける。

 

質問:プログラム初心者にはどの言語?

回答:Perlはやめといたほうが、、w Cがいいじゃないかな。

全部自分でやらなきゃいけないから。便利な言語から始めると、つまった時に原因の見当がつかない。

 

質問:ちょろっと書くモジュールでDBの扱いはどうするか

回答:基本的には生DBI。おれおれキャッシュとか。

 

他にもいっぱい質問が出て、あっという間の50分でした。

 

LTは僕の他に

主催の@ytnobodyさんによる「配列をそこそこもっともらしく使う」では

配列とリストの違いに触れたり、grep,map,sort関数の紹介がありました。

「sortで引数に指定した配列変数に評価結果を格納できるのか」という質問は興味深かったです。(できるらしいので、自分でも試してみよう。)

 

最後に@i47_rozaryさんによる「引数に配列を使う」と「Data::Section::Simpleの紹介」がありました。

(__DATA__で複数のデータ構造を定義できるのは便利ですね!今度使ってみよう)

 

懇親会には初めて参加させて頂きましたが、こちらもとても楽しかったです。

YAPCで発表されたりする方々と直接話せた事が何より嬉しかったです。

Perl Beginners++

 

プログラムでの小数計算で誤差を含んでしまう原因と解決方法

Perlに限らずだいたいのプログラムでは小数点の扱いが苦手。

なぜなら浮動小数点方式というデータの持ち方をしているから。


10進数の0.1を浮動小数点方式では正確に表現出来ない(循環小数となり、途中で丸められてしまう)
そのため近似値で扱うことになり、計算結果に誤差を含むケースが出てくる。
コチラの記事が分かり易かった


では誤差を少なく計算するにはどうするか、、
これも完璧ではないようだけど、桁を上げて(揃えて)整数として計算して
最後に桁を戻すような「仮想小数点方式」で計算するのが良いらしい。


0.1 + 0.7

という計算をしたかったら


( (0.1 * 10) + (0.7 * 10) ) / 10

こうするのが「仮想小数点方式」


■ジャストアイデアだけど
仮想小数点方式でも誤差を含むっていうのは
結局0.1のような小数を使って「計算」をするから。

 

それなら
小数値を文字として扱って桁上げしたらいいんじゃないかと。
正規表現とかで頑張って、、、
=> 実際JavaBigDecimalとかはそれっぽいことしてるらしい。気が向いたら調べよう。


COBOLは小数点の計算に誤差を含まない
金融系のシステムに採用されていたりするCOBOLは小数の計算に誤差を含まない。
そんな話を昔聞いたなーっと思ったので、どうやっているか調べてみた。

BCD(Binary-coded decimal) というデータの持ち方を採用しているらしい。
詳しくはコチラの記事が大変参考になった。
ここで紹介されているパック10進数形式というのをCOBOLでは採用しているらしい。


0.1のような小数は

浮動小数点だと近似値となるけど、
BCDなら正確に表現出来る。


■まとめ
誤差を含まず「仮想小数点方式」で計算すれば、面倒だけど他の言語でも誤差を無くせるという事でいいのかな。

Perlで不思議なハッシュが作れるはなし

このエントリーを書いたキッカケはこのtogetterです。


実際に書いて調べて、リストと配列は別物であるとか勉強になりました。
なかでもハッシュのキーに「リスト(配列ではない)」を指定した時の動作は面白かったので、私の検証結果をまとめてみました。


まずは普通のハッシュを作ってDumperで表示します。

use 5.010;
use Data::Dumper

my %hash;
$hash{xyz} = 'hoge';
say Dumper \%hash;

するとこうなります。

$VAR1 = {
    'xyz' => 'hoge',
};


では少し追記して、ハッシュのkeyにリストコンテキストを指定して、値にfugaを入れます。

use 5.010;
use Data::Dumper

my %hash;
$hash{xyz} = 'hoge';
$hash{qw/x y z/} = 'fuga';
say Dumper \%hash;

するとどうなるでしょう?

$VAR1 = {
        'xyz' => 'hoge',
        'xyz' => 'fuga',
    };

ナニコレーーーーーーーーーー!!!!


なんと、keyが重複しているではあーりませんか!
いやいやいや、落ち着いて考えよう。
んなわけないでしょ。


値を取り出してみよう。ってやったらこうなりました。

say $hash{xyz};  # hoge
say $hash{qw/x y z/}; #fuga
say scalar keys %hash; # 2

ほほーー、やはり2セット存在していて
しっかりとkeyを区別しているようだ。


改めてDumpを見てみる。。

$VAR1 = {
        'xyz' => 'hoge',
        'xyz' => 'fuga',
    };

、、、、しっかし不思議なハッシュだなー
Dumpすると同じように見えるけど、実際は何か違うんだよね。きっとね。


じゃあ実際、何が違うのか調べてみる。

use 5.010;
use Data::Dumper

my %hash;
$hash{xyz} = 'hoge';
$hash{qw/x y z/} = 'fuga';
say Dumper \%hash;

for my $key ( keys %hash ) {
    $key =~ s/([^\w])/unpack('H2', $1)/eg;
    say $key;
}

ハッシュのkeyをURLエンコードする時のようにしてみます。
正規表現で半角数字と半角アルファベット以外を変換しています)

$VAR1 = {
        'xyz' => 'hoge',
        'xyz' => 'fuga',
    };
xyz
x1cy1cz

お、リストで指定したkeyは文字の間に「1c」というのが挟まっていますね。
ASCIIコードについて書かれているページを参考にするとHexで1cはFile separatorを表すそうです。

まぁようするに、バイトコードレベルでは違うkeyだったから、パッと見た感じ
不思議に見えたハッシュですが、とくに不思議でもなんでも無かったという事でした。


下記はFile separatorについて少し。

use 5.010;

my @array = qw/x y z/;
say @array;

これはリストコンテキストを配列変数に格納しています。
その配列変数を表示するとこのようになります。

xyz

これはxyzがただjoinされたのではなく、
File separatorによって1つの文字列となっています。
さっきの「1c」ですね。

PerlはFile separatorを「$,」ダラーカンマという特殊変数に格納しています。
というわけで、下記のような事が出来ます。

use 5.010;

$, = '---';
my @array = qw/x y z/;
say @array;

するとこのように表示されます。

x---y---z

以上、普段はそんなに意識しないし、困る事もそんなに無い事でしたー。

Exporter.pmの役割を知ったのでメモ

perl文字コードを扱う時に使用するモジュールといえばEncode.pmですよね。
CPANはこちら


SYNOPSISを見ると下記のようにuseしていますね。

use Encode qw(decode encode);
$characters = decode('UTF-8', $octets,     Encode::FB_CROAK);
$octets     = encode('UTF-8', $characters, Encode::FB_CROAK);

decodeメソッドとencodeメソッドを使いたい場合は、このようにuseします。


こうした場合はencodeメソッドは使えません。

use Encode qw(decode);


でもこれならdecodeメソッドもencodeメソッドも使えます。

use Encode;


内部ではどうなってるの?


Encode.pmのソースを見てみると、なにやらExporter.pmというモジュールで
そのあたりの処理をやっているみたい。
で、Exporter.pmが何をしてくれるのかというと
「関数をモジュールに紐づける」という機能でした。

@EXPORTという配列に関数名を入れておくと、
useしたモジュールに関数を紐づけてくれるんです。

要するに

use Encode;
Encode::encode('utf8', 'test');

ではなくて

use Encode;
encode('utf8', 'test);

と記述出来るのは、
Encode.pmのソースで下記のような配列を宣言しているからです。

our @EXPORT = qw(
  decode  decode_utf8  encode  encode_utf8 str2bytes bytes2str
  encodings  find_encoding clone_encoding
);

確かに@EXPORT配列にencodeが入っていますね。

注意が必要なのは@EXPORTに入っていればuseした時に紐づけられますが、
useした時に引数を指定した場合には、指定した関数しか紐づけられません。
上でも書きましたが

use Encode qw(decode);
encode('utf8', 'test);

これはエラーになります。


@EXPORTの他に@EXPORT_OKというのもあります。
こちらはuseの際に引数で指定しないと絶対に使えない関数名を入れておきます。


Exporter.pmの動作

1. 引数が無かった場合@EXPORTに入った関数名を紐づける。
2. 引数で関数が指定された場合、@EXPORTと@EXPORT_OKの中にある関数であれば紐づける。


その他にも%EXPORT_TAGSというのもあります。
これは紐づけたい関数が沢山あるときに使うもので
Encode.pmでは下記のようになっています。

our %EXPORT_TAGS = (
    all          => [ @EXPORT,    @EXPORT_OK ],
    default      => [ @EXPORT ],
    fallbacks    => [ @FB_CONSTS ],
    fallback_all => [ @FB_CONSTS, @FB_FLAGS ],
);

これはuseするときに下記のように「:タグ名」とすることで、たくさんの関数を紐づけたい時に使います。

use Encode(:all);


Archive::Zipモジュールはこの方法がSYNOPSISに書かれていたりしますね。


useしたことで、使わない関数までも紐づけられるのを避けられるよう考慮されているなんて
Exporter.pm素晴らしい!

はじめてCPANにあげてみた 〜 そして早速バージョンアップもやってみた 〜

タイトルの通り、初めてCPANにあげてみたので、その備忘録です。


アップロードしたモジュールは前回作ったMojolicousのプラグインです。
プラグインの内容や、そもそもそんなのいらないとか、名前空間へんだよ、などなど、もしお気づきの点などあればご指摘頂けると幸いです。


それでは、既にモジュール(.pmファイル、テストなど)は出来ている前提で進めまっす。


STEP1. MANIFESTを作る

モジュールを置いたディレクトリにて
make manifestコマンドで作成できるので、難しいことはありませんでした。

shell> make manifest
/opt/local/bin/perl "-MExtUtils::Manifest=mkmanifest" -e mkmanifest
Added to MANIFEST: lib/Mojolicious/Plugin/Message/Locale.pm
Added to MANIFEST: Makefile.PL
Added to MANIFEST: MANIFEST
Added to MANIFEST: t/000_use.t
Added to MANIFEST: t/001_message_locale.t
Added to MANIFEST: t/002_locale_is_ja.t
Added to MANIFEST: t/003_locale_file.t
Added to MANIFEST: t/004_default_message.t
Added to MANIFEST: t/locale.conf
Added to MANIFEST: t/other_locale.conf

もしモジュールに必要ないファイルなどがあれば
MANIFEST.SKIPという名前のファイルを作成し、
そこにMANIFESTに含めたくないファイルを指定します。
gitの.gitignoreみたいな感じ。


STEP2. tarball形式にする

CPANにアップロードするにはこの形式にする必要があります。

shell> make dist
/opt/local/bin/perl "-MExtUtils::Manifest=manicopy,maniread" \
		-e "manicopy(maniread(),'Mojolicious-Plugin-Message-Locale-0.01', 'best');"
mkdir Mojolicious-Plugin-Message-Locale-0.01
mkdir Mojolicious-Plugin-Message-Locale-0.01/t
mkdir Mojolicious-Plugin-Message-Locale-0.01/lib
mkdir Mojolicious-Plugin-Message-Locale-0.01/lib/Mojolicious
mkdir Mojolicious-Plugin-Message-Locale-0.01/lib/Mojolicious/Plugin
mkdir Mojolicious-Plugin-Message-Locale-0.01/lib/Mojolicious/Plugin/Message
Generating META.yml
Generating META.json
COPY_EXTENDED_ATTRIBUTES_DISABLE=1 COPYFILE_DISABLE=1 tar cvf Mojolicious-Plugin-Message-Locale-0.01.tar Mojolicious-Plugin-Message-Locale-0.01
a Mojolicious-Plugin-Message-Locale-0.01
a Mojolicious-Plugin-Message-Locale-0.01/lib
a Mojolicious-Plugin-Message-Locale-0.01/Makefile.PL
a Mojolicious-Plugin-Message-Locale-0.01/MANIFEST
a Mojolicious-Plugin-Message-Locale-0.01/META.json
a Mojolicious-Plugin-Message-Locale-0.01/META.yml
a Mojolicious-Plugin-Message-Locale-0.01/t
a Mojolicious-Plugin-Message-Locale-0.01/t/000_use.t
a Mojolicious-Plugin-Message-Locale-0.01/t/001_message_locale.t
a Mojolicious-Plugin-Message-Locale-0.01/t/002_locale_is_ja.t
a Mojolicious-Plugin-Message-Locale-0.01/t/003_locale_file.t
a Mojolicious-Plugin-Message-Locale-0.01/t/004_default_message.t
a Mojolicious-Plugin-Message-Locale-0.01/t/locale.conf
a Mojolicious-Plugin-Message-Locale-0.01/t/other_locale.conf
a Mojolicious-Plugin-Message-Locale-0.01/lib/Mojolicious
a Mojolicious-Plugin-Message-Locale-0.01/lib/Mojolicious/Plugin
a Mojolicious-Plugin-Message-Locale-0.01/lib/Mojolicious/Plugin/Message
a Mojolicious-Plugin-Message-Locale-0.01/lib/Mojolicious/Plugin/Message/Locale.pm
rm -rf Mojolicious-Plugin-Message-Locale-0.01
gzip --best Mojolicious-Plugin-Message-Locale-0.01.tar

こんな感じでMojolicious-Plugin-Message-Locale-0.01.tar.gzが作成されました。


STEP3. PAUSEにアップロードする

あ、PAUSEというCPAN AUTHERを管理するサイトでアカウントを作っておいてくださいね(言うのおそい)


User menu > Files > Upload a file to CPAN へ遷移し、
Upload MaterialのIf your browser can handle file upload, 〜 という項目のところで
先ほど作成されたtar.gzファイルを選択し、「Upload this file from my disk」ボタンでアップロード!


User menu > Files > Delete Files へ遷移してみると、アップロードしたファイルが確認出来ます。


STEP4. PAUSEで名前空間の申請
いよいよ大詰め!

User menu > Modules > Register Namespace へ遷移して、モジュールについて入力します。
下記が項目と今回僕が入力した内容です。

▼Name of the module : 名前空間
Mojolicious::Plugin::Message::Locale

▼Module List Chapter:モジュールの種類を選択肢から
015 World_Wide_Web_HTML_HTTP_CGI

▼Development Stage (Note: No implied timescales):モジュールの開発状態
b -- beta(これもてきとう、、)

▼Support Level:サポートレベル
d -- developer(だんだんよくわからなくなってきた、、)

▼Language Used:使用言語
p -- perl

▼Interface Style:モジュールの使い方
f -- functions

▼Public license:モジュールに適用するライセンス
p -- Standard-Perl

▼Description in Module List(44 chars limit):モジュールについて簡単に説明
localize messages in Mojolicious app(こ、これで通じるのか、、?)

▼Places where this module has been or will be discussed publicly:なんかgithubとかのURLでいいっぽい
未入力

▼Modules with similar functionality:似たようなモジュールがあればリストアップする
未入力

▼Rationale:モジュールに対する熱い気持ち
Mojolicious::Plugin::Message::Locale is a plugin for Mojolicious apps to localize messages.
It's a very simple modules, become very useful to localize the message.

最後に「Submit to modules@perl.org」ボタンを押して完了!


数分経過したところで「Module submission Mojolicious::Plugin::Message::Locale
」というタイトルのメールが来ました。


そして翌朝。。。。


アップしてから7時間くらいが経過したところで、CPANにアップされているのを確認!!(とくに連絡はこなかった)

あれ、、SYNOPSISに記載した日本語が文字化けてる、、

どうやらpodの先頭で =encoding utf8 としないとpodに記述したマルチバイト化けてしまうらしい。。

というわけで早速ver0.02を作成。。



修正したら、再びPAUSEへログイン
User menu > Files > Upload a file to CPAN へ遷移し、
最初の時と同じように
Upload MaterialのIf your browser can handle file upload, 〜 という項目のところで
Ver0.02のtar.gzファイルを選択し、「Upload this file from my disk」ボタンでアップロード!

そして続けて
If you have already uploaded のラジオボタンで今アップしたモジュールを選択し、「Upload the checked file」をポチっと


今度は「CPAN Upload: S/SA/SASAKURE/Mojolicious-Plugin-Message-Locale-0.02.tar.gz」というタイトルのメールがきました


数分後に「PAUSE indexer report SASAKURE/Mojolicious-Plugin-Message-Locale-0.02.tar.gz」というタイトルのメールも来ました。
内容は、、なにやらVer0.02について承知したよ!的な、、


さらに数分後「New module Mojolicious::Plugin::Message::Locale」というタイトルのメールがきました。
今度はなんだなんだ、心配になってくる、、、、えっと 新しいモジュールについて、何日何時何分受け付けたよ。公開までは少しかかるよ 的な事が書いてある。なるほど。待ちましょう



しばし待ちます。。 4時間くらい経過して


ちゃんと更新されて、文字化けも直ったー!!

という感じでした。

後置ifでmy宣言しちゃダメ

perlのスコープは{}ブロック毎に出来ます。


例えばこんな感じ。

my $flg = 1;
if ( $flg ) {
    my $hoge = 'sasakure';
    print $hoge;  # output「sasakure」
}
print $hoge;  # Global symbol "$hoge" requires explicit ...<省略>

{}ブロック内で宣言されたmy変数は、そのブロック(スコープ)の外で使えません。



では後置ifではどうなるかというと

my $flg = 1;
my $hoge = 'sasakure' if $flg;
print $hoge;  # output「sasakure」

{}ブロックが無いので、ifの外でも使う事が出来る。(と思ったけど、少し違うので最後まで読んでください)



それではifの評価がfalseになる場合はどうなるでしょうか。

my $flg = 0;
my $hoge = 'sasakure' if $flg;
print $hoge;  # Use of uninitialized value ...<省略>

あれ、このエラーって変数の値が未定義の時に出るエラー。。。。
# Global symbol "$hoge" requires explicit ...<省略>
こういうのを予想してたのに、、


hogeのmy宣言はされている?


下記 perldoc引用
http://perldoc.jp/docs/perl/5.14.1/perlsyn.pod

注意: (my $x if ... のような) 条件構造やループ構造で修飾された my 文の振る舞いは 未定義 です。 my 変数の値は undef かも知れませんし、以前に代入された値かも 知れませんし、その他の如何なる値の可能性もあります。 この値に依存してはいけません。 perl の将来のバージョンでは現在のバージョンとは何か違うかも知れません。 ここには厄介なものがいます。


要するに後置ifでmy宣言しちゃダメという事だそうです。

Mojoliciousのプラグイン作成コマンドを使ってみた

Mojoliciousで開発をしていて、既存のプラグインを使ったり、新規に自分で作ったりすると思いますが
そんな自分で作ったプラグインの中には「CPANにあげたい!」と思うものも出てくるんじゃないでしょうか。


僕は今回、Webサイトの他言語対応(国旗の画像をポチると、表示される言語が切り替わる)をするために、
簡潔に日本語や英語の文言を管理できて、
Mojolicious上のアクションやらテンプレートやらで使えたら良いなー
と思って、簡単なプラグインを作ってみました。
モジュールはgithubにあげました
(githubも初心者なので、いろいろ分からないなりに。。。)


今回はコマンドを使って、モジュールにするところが本題なので
モジュールの中身について説明はありません。


なにやらMojolicious::Plugin::I18Nというのもあるようなのですが、
自分が使いやすいのを作っちゃえ的なノリです。


というわけで、適当なディレクトリでコマンドの実行です。
末尾部分がモジュール名になります。
僕が今回作りたかったのはMojolicious::Plugin::Message::Localeという物なので、こんな感じです。

shell> mojo generate plugin Message::Locale

するとコマンドを実行したとこにMessage::Localeディレクトリが作成され、
その下に一通りのファイルが作成されます。

Message::Locale/lib/Mojolicious/Plugin/Message/Locale.pm
Message::Locale/t/basic.t
Message::Locale/Makefile.PL

Locale.pm
これはもちろんプラグイン本体です。

basic.t
テストモジュールのひな形になります。

Makefile.PL
このモジュールをインストールするために必要になります。
Makefileとか書いた事ないから、これは嬉しかった!


んで、モジュールの本体を書きます。(既に作ってるプラグインの中身をこちらに)
perldocを書き、テストを書く
それが出来たらインストールしてみる。

shell> perl Makefile.PL
Writing Makefile for Mojolicious::Plugin::Message::Locale
Writing MYMETA.yml and MYMETA.json

shell> make
cp lib/Mojolicious/Plugin/Message/Locale.pm blib/lib/Mojolicious/Plugin/Message/Locale.pm
Manifying blib/man3/Mojolicious::Plugin::Message::Locale.3pm

shell> make test
PERL_DL_NONLAZY=1 /opt/local/bin/perl "-MExtUtils::Command::MM" "-e" "test_harness(0, 'blib/lib', 'blib/arch')" t/*.t
t/basic.t .. ok
All tests successful.
Files=1, Tests=3,  1 wallclock secs ( 0.04 usr  0.01 sys +  0.51 cusr  0.05 csys =  0.61 CPU)
Result: PASS

shell> make install
Installing /opt/local/lib/perl5/site_perl/5.12.3/Mojolicious/Plugin/Message/Locale.pm
Installing /opt/local/share/man/man3/Mojolicious::Plugin::Message::Locale.3pm
Appending installation info to /opt/local/lib/perl5/5.12.3/darwin-multi-2level/perllocal.pod 

完成!!
ここまで出来れば、CPANでインストールしたモジュールのように利用する事が出来ます。
次はこの手順で作成したモジュールをCPANに上げてみたいと思います。

どうだろうなー できるのかなー
やったことないからどうなる事やら