ishiducaの日記 このページをアンテナに追加 RSSフィード

2009-10-15

[][][]円の面積と円周を求める方法を考える 16:33 円の面積と円周を求める方法を考える - ishiducaの日記 を含むブックマーク はてなブックマーク - 円の面積と円周を求める方法を考える - ishiducaの日記 円の面積と円周を求める方法を考える - ishiducaの日記 のブックマークコメント

円の面積と円周を求める方法をいくつか。本当はオブジェクトつくってやりたいんだけど、さぼっていたのでリハビリのため、別の方法で書いてみた。

円周を求める関数( &ensyu($radius) ) と面積を求める関数( &menseki($radius) ) を使う

circle_pl

#!/usr/bin/perl -l
use strict;
use warnings;

my $rad = 3;    # 半径

print "半径 $rad";
print "円周 ", &ensyu( $rad );
print "面積 ", &menseki( $rad );

exit 0;

sub ensyu{
    my $r   = shift;
    my $pai = 3.14; # 円周率
    2 * $pai * $r;
}

sub menseki{
    my $r   = shift;
    my $pai = 3.14;
    $pai * $r ** 2;
}

結果

半径 3
円周 18.84
面積 28.26

難しいことをしてないので、悩まないでいいんだけど、別の円で(円周・面積)を求めるのに、毎回関数を呼び出すのが面倒だし、ここで求めた円周と面積が同一の円の要素かどうかは判別できない。このプログラムでは問題ないけど、たくさんの円を扱うようになると困るのかな〜って気がする。

なので、少し書き換えて、円をハッシュで定義して、円周と面積をこの円の要素にする方法を考えてみた

circle2_pl

my  $rad = 3;
my  %en  = &circle( $rad );

print "半径 $rad";
print "円周 ", $en{'ensyu'};
print "面積 ", $en{'menseki'};
exit 0;

sub circle{
    my $r   = shift;
    my $pai = 3.14;
    my $menseki = $pai * $r ** 2;
    (
        "radius",  $r,
        "ensyu",   2 * $pai * $r,
        "menseki", $menseki,
    );
}

上記の方法で個別の円を定義できたような気がする。ただ、一度円を定義すると半径を変更した場合、(例えば、半径を「5」にした場合)

$en{'radius'} = 5;

この方法だと面積・円周が変更前と変わらない。

なので更に、書き換えてみる。クロージャ(?)と無名のサブルーチンを使ってみた。

ハッシュを使うのは同じだけど、半径を変更したタイミング(じゃなくて、ハッシュのプロパティ(関数)を呼び出したタイミング)で、円周(もしくは面積)を変更できるものに

circle3_pl

#!/usr/bin/perl -l
use strict;
use warnings;

my $rad = 3;

sub circle{
    my $r   = shift;
    my $pai = 3.14;
    (
        'radius'  => sub{
            $r = shift if @_;
            $r;
        },
        'ensyu'   => sub{
            2 * $pai * $r;
        },
        'menseki' => sub{
            $pai * $r ** 2;
        },
    );
}

my %en = &circle( $rad );
print '半径 ', $en{'radius'}->();
print '円周 ', $en{'ensyu'}->();   # &{$en{'ensyu'}}と同じ
print '面積 ', $en{'menseki'}->();
print '';

# 半径を変更する
$en{'radius'}->(5);
print '半径 ', $en{'radius'}->();
print '円周 ', $en{'ensyu'}->();
print '面積 ', $en{'menseki'}->();
exit 0;

結果

半径 3
円周 18.84
面積 28.26

半径 5
円周 31.4
面積 78.5

Circleオブジェクト作れよって話ですね。...

MuchammatMuchammat2012/11/01 16:40Son of a gun, this is so hlepful!

skgaxalbmnskgaxalbmn2012/11/02 09:58JDG2Lm <a href="http://xzxepisbhkvz.com/">xzxepisbhkvz</a>

xdctjnxdctjn2012/11/04 23:134WkFUn <a href="http://vcffiznohtay.com/">vcffiznohtay</a>

fhfczzjfhfczzj2012/11/05 12:34ZHLIat , [url=http://qohvrqhbcoso.com/]qohvrqhbcoso[/url], [link=http://tqntyqxfqoxt.com/]tqntyqxfqoxt[/link], http://ztdvlyytdwon.com/

トラックバック - http://perl.g.hatena.ne.jp/ishiduca/20091015

2009-09-07

[][][]リファレンスを使ってデータを構造化する 21:06 リファレンスを使ってデータを構造化する - ishiducaの日記 を含むブックマーク はてなブックマーク - リファレンスを使ってデータを構造化する - ishiducaの日記 リファレンスを使ってデータを構造化する - ishiducaの日記 のブックマークコメント

カテゴリー、摘要、金額の順にスペースで区切られたファイル(data.txt)を読み込み、構造的なハッシュテーブルを作ります。

data.txt

その他    両替手数料                     100
移動費    乗車券(直江津〜東京都区内)    4840
移動費    特急指定券(直江津〜越後湯沢)  1220
移動費    新幹線自由券(越後湯沢〜東京)  2720
飲料      水(2l)                         178
菓子      ベビースター                   118
食事      四川風麻婆豆腐丼               450
駐車      駐車料金                       300
食事      寿司食べ放題+飲み放題         5000
宿泊      宿泊費                        4800
宅配便    宅配便の箱                     300
宅配便    送料                          1200
食事      親子丼                         470
移動費    乗車券(東京駅構内〜高田)      4840
移動費    新幹線自由席(東京〜越後湯沢)  2720
移動費    特急指定席(越後湯沢〜高田)    1120
飲料      アイスカフェラテ               300
デザート  ラムレーズンのアイス           300

ハッシュ(%ls)のキーはカテゴリー(その他、移動費など)、値は摘要と金額で構成された配列を値とした配列とします。...と言ってもわかりにくいので、先にハッシュテーブルを見ましょう。今回書いたスクリプト(dump.pl)を実行すると表示されます。

$VAR1 = {
          'デザート' => [
                              [
                                'ラムレーズンのアイス', '300'
                              ]
                            ],
          '食事' => [
                        [
                          '四川風麻婆豆腐丼', '450'
                        ],
                        [
                          '寿司食べ放題+飲み放題', '5000'
                        ],
                        [
                          '親子丼', '470'
                        ]
                      ],
          'その他' => [
                           [
                             '両替手数料','100'
                           ]
                         ],
          '菓子' => [
                        [
                          'ベビースター', '118'
                        ]
                      ],
          '飲料' => [
                        [
                          '水(2l)','178'
                        ],
                        [
                          'アイスカフェラテ','300'
                        ]
                      ],
          '移動費' => [
                           [
                             '乗車券(直江津〜東京都区内)','4840'
                           ],
                           [
                             '特急指定券(直江津〜越後湯沢)','1220'
                           ],
                           [
                             '新幹線自由券(越後湯沢〜東京)', '2720'
                           ],
                           [
                             '乗車券(東京駅構内〜高田)','4840'
                           ],
                           [
                             '新幹線自由席(東京〜越後湯沢)','2720'
                           ],
                           [
                             '特急指定席(越後湯沢〜高田)','1120'
                           ]
                         ],
          '駐車' => [
                        [
                          '駐車料金', '300'
                        ]
                      ],
          '宿泊' => [
                        [
                          '宿泊費', '4800'
                        ]
                      ],
          '宅配便' => [
                           [
                             '宅配便の箱', '300'
                           ],
                           [
                             '送料','1200'
                           ]
                         ]
        };

dump.pl

#!/usr/bin/perl -wnlaF'\s+'
use strict;
use Data::Dumper;

our(%ls);
BEGIN{
    $ARGV[0] = 'data.txt';
}

my $category = shift @F;
# exists $ls{$category} or $ls{$category} = [];
push @{$ls{$category}}, [@F];

END{
    print Dumper(\%ls);
    exit 0;
}

今回はデータ構造を見るだけでしたが、これで応用の効くデータ構造ができました

トラックバック - http://perl.g.hatena.ne.jp/ishiduca/20090907

2009-06-25

[][][][]関数の引数に複数のハッシュを指定する場合はリファレンスを使う 11:47 関数の引数に複数のハッシュを指定する場合はリファレンスを使う - ishiducaの日記 を含むブックマーク はてなブックマーク - 関数の引数に複数のハッシュを指定する場合はリファレンスを使う - ishiducaの日記 関数の引数に複数のハッシュを指定する場合はリファレンスを使う - ishiducaの日記 のブックマークコメント

入社日から任意の日付(例えば、今日)までで満何年かを調べるのに一度日付をハッシュに直して計算するようにした。

たとえば、"2009/06/25" は "( 'year' => 2009, 'month' => 6, 'date' => 25 )" のように。

ハッシュに直すのは、後日色々な日付計算をするかもしれないと思ったためで、その方が扱いやすいと思ったから。


さて、本題。

一つのハッシュを引数に渡すなら、そのまま渡しても問題ない。

my %date = ( 'year' => 2009, 'month' => 6, 'date' => 25 );
print &format(%date);

sub format{
    my %d = @_;
    sprintf "%04d-%02d-%02d", $d{'year'}, $d{'month'}, $d{'date'};
}

しかし、複数のハッシュを渡す場合、そのまま渡しても受け取るサブルーチン側で、一つの @_ に格納してしまう。

my(%hash_a, %hash_b) = @_;

とか

my %hash_a = shift;
my %hash_b = shift;

では期待した結果にならない。渡されたハッシュのキーと値を交互にした配列が @_ に渡され、それを最初の引数の %hash_a に渡して、展開してしまう。

my %date = ( 'year' => 2009, 'month' => 6, 'date' => 25 );
my %time = ( 'hour' => 10, 'minute' => 10, 'sec' => 22 );
print &test(%date,%time);

sub test{
    my(%d,%t) = @_;
    return $d{'sec'};
    # 22 がかえる
    # return $t('set');
    # とすると "Use of uninitialized value in print at ..."とエラーを吐く
}

なので、直接配列を渡すのではなく、リファレンスのリストを渡すのがポピュラー

my %date_small = (
    'year'  => 2008,
    'month' => 10,
    'date'  => 9,
);
my %date_large = (
    'year'  => 2009,
    'month' => 6,
    'date'  => 25,
);

print &Years(\%date_small,\%date_large);
exit 0;

sub Years{
    my $S = shift;
    my $L = shift;
    my $year = $L{'year'} - $S{'year'};
    $year-- if $S{'month'} > $L{'month'};
    $year-- if $S{'month'} == $L{'month'} and $S{'date'} > $L{'date'};
    return $year;
}

むしろ、日付オブジェクトを生成するとか、日付計算に使えるモジュールを使う方がPerl使いに取っての正解だと思うんだけど、標題の「関数の引数に複数のハッシュを使う」方法の習得になりそうだったので、今回は敢えて「無駄」をしてみた。


んな訳で、満何年かを調べるスクリプト

get_year.pl

t

リファクタリングしたい

DashDash2011/10/10 12:42Your article perfectly shows what I needed to know, tnhaks!

isbentyyowlisbentyyowl2011/10/10 18:32bNeKLH <a href="http://penkuwxjpjyb.com/">penkuwxjpjyb</a>

phslmuuhphslmuuh2011/10/12 02:06ZjXfZ1 , [url=http://napedayzihvv.com/]napedayzihvv[/url], [link=http://ijommnclvhrk.com/]ijommnclvhrk[/link], http://ntcebrpashsh.com/

wwpbswoawwpbswoa2011/10/13 23:38XwQx2I <a href="http://zocjzpqmtbcf.com/">zocjzpqmtbcf</a>

jdthwfffmmjdthwfffmm2011/10/14 19:57rO5PUb , [url=http://tmuypkupwzly.com/]tmuypkupwzly[/url], [link=http://xenmuwpxnwow.com/]xenmuwpxnwow[/link], http://xmpageqkljmw.com/

RaniRani2012/11/01 18:42I can't hear anything over the sound of how aeswome this article is.

gvelconaefhgvelconaefh2012/11/02 10:04fAXQ4u <a href="http://kxzofmhmaqrd.com/">kxzofmhmaqrd</a>

zgpobuzgpobu2012/11/02 14:40lSNt9m , [url=http://otnqpcouctww.com/]otnqpcouctww[/url], [link=http://hgwekhrbxogs.com/]hgwekhrbxogs[/link], http://shvfahutlsjm.com/

ktrioksqcktrioksqc2012/11/04 23:18FOVZJJ <a href="http://fzfmfvxsbdhf.com/">fzfmfvxsbdhf</a>

fzjnrzamfzjnrzam2012/11/05 12:38Yqnnj5 , [url=http://hgfpsadaybiw.com/]hgfpsadaybiw[/url], [link=http://stywutofmhtg.com/]stywutofmhtg[/link], http://mzvxzsxifzhy.com/

トラックバック - http://perl.g.hatena.ne.jp/ishiduca/20090625

2009-06-03

[][]サブルーチンのリファレンス 07:56 サブルーチンのリファレンス - ishiducaの日記 を含むブックマーク はてなブックマーク - サブルーチンのリファレンス - ishiducaの日記 サブルーチンのリファレンス - ishiducaの日記 のブックマークコメント

あんまりいいサンプルが思いつかなかった>

名前付きのサブルーチンのリファレンスを取る

#!/usr/bin/perl -wl
use strict;

# 名前付きサブルーチンのリファレンスを取る
my $beki = \&beki;

# デリファレンスして出力
print &$beki(2,3);

sub beki{
    # べき乗:: n**m と同じ事をする
    my $num   = shift;
    my $loops = shift;
    local $_  = $num;
    while( $loops-1 ){
        $_ = $_ * $num;
        $loops--;
    }
    return $_;
}
  • 配列やハッシュのときと同じで、バックスラッシュを付けて参照を取る
  • デリファレンスするときも、配列やハッシュの時と同じ
  • デリファレンスする時は「&{$beki}(2,3)」と「$beki->(2,3)」でもOK

無名サブルーチン

まず単純にスカラー変数に無名のサブルーチンを格納する
#!/usr/bin/perl -wl
use strict;

my $beki = sub{
    my $num   = shift;
    my $loops = shift;
    local $_  = $num;
    while( $loops-1 ){
        $_ = $_ * $num;
        $loops--;
    }
    return $_;
};

print $beki->(3,3);

exit 0;
名前付きサブルーチンの定義と違う点
  • sub とその後のブロックの間にサブルーチン名がない
  • 文なので、セミコロン(もしくは、その他のセパレータ)が必要

ハッシュの中に直接無名サブルーチンを置く

#!/usr/bin/perl -wl
use strict;

my %ben_haper = (
    'name' => 'Ben Harper',
    'sing' => sub{
        my $song = shift;
        print "\"$song\"";
    }
);

$ben_haper{'sing'}->('With My Own Tow Hands.');

#こちらの書き方でもOK
&{$ben_haper{'sing'}}('Black Rain.');
exit 0;
トラックバック - http://perl.g.hatena.ne.jp/ishiduca/20090603

2009-06-01

[][]「リファレンスを使ってネストしたデータ構造を作る」を改造する 16:44 「リファレンスを使ってネストしたデータ構造を作る」を改造する - ishiducaの日記 を含むブックマーク はてなブックマーク - 「リファレンスを使ってネストしたデータ構造を作る」を改造する - ishiducaの日記 「リファレンスを使ってネストしたデータ構造を作る」を改造する - ishiducaの日記 のブックマークコメント

前回は多重配列(配列に配列)を作った。今回はハッシュに配列を格納する。


点数を記録したファイル

$ cat score.tsv
score  1st 2nd 3rd
jiji   12  64  33
baba   22  32  67
child  88  76  21

2行目以降では、最初のデータが名前、2番目以降が実際の点数なので、名前をキーにして値の代わりに無名配列を格納する

#!/usr/bin/perl -wnlaF'\t'
use strict;

our(%score,@title);

BEGIN{
    $ARGV[0] = 'score';
}

# 1行目はヘッダ列なので
if( $. == 1 ){
    @title = (@F,"sum");
}
# 2行目以降をハッシュ %score にデータを格納する
else {
    my $name = shift @F;  # 各行の最初のデータをキーに
    $score{$name} = [@F]; # 2番目以降のデータを無名配列に格納
}

END{
    {
        local $, = "\t";
        print @title;
    }
    foreach my $name ( sort keys %score ){
        my $sum = 0;
        printf "$name\t";
        foreach my $point ( @{$score{$name}} ){
            printf "$point\t";
            $sum += $point;
        }
        print $sum;
    }
    exit 0;
}

実行結果

$ perl nest2.pl
score   1st 2nd 3rd sum
baba    22  32  67  121
child   88  76  21  185
jiji    12  64  33  109

ハッシュも多重配列のとき同様、$score を使ってみる

#!/usr/bin/perl -wnlaF'\t'
use strict;

our($score,@title);

BEGIN{
    $ARGV[0] = 'score';
}

if( $. == 1 ){
    @title = (@F,"sum");
} else {
    my $name = shift @F;
    $score->{$name} = [@F];
}

END{
    {
        local $, = "\t";
        print @title;
    }
    foreach my $name ( sort keys %$score ){
        my $sum = 0;
        printf "$name\t";
        foreach my $point ( @{$score->{$name}} ){
            printf "$point\t";
            $sum += $point;
        }
        print $sum;
    }
    exit 0;
}

[][]リファレンスを使ってネストしたデータ構造を作る 14:43 リファレンスを使ってネストしたデータ構造を作る - ishiducaの日記 を含むブックマーク はてなブックマーク - リファレンスを使ってネストしたデータ構造を作る - ishiducaの日記 リファレンスを使ってネストしたデータ構造を作る - ishiducaの日記 のブックマークコメント

リファレンスを利用して入れ子の配列を作れば、データ管理が楽(かも)

例えば、下のような点数を記録したファイル(score.tsv)を扱う場合

$ cat score.tsv
score  1st 2nd 3rd
jiji   12  64  33
baba   22  32  67
child  88  76  21

配列@scoreに無名の配列にデータを格納して、それを格納する。

#!/usr/bin/perl -wnlaF'\t'
use strict;

our(@score);

BEGIN{
    $ARGV[0] = 'score';
}

push @score, [ @F ];

END{
    print $score[0]->[1];
    $, = "\t";
    print @{$score[2]};
    exit 0;
}

実行結果

$ perl nest.pl
1st
baba    22    32    67

push @socre, [ @F ]; を

push @score, @F;

のように [] を使わないでやると失敗する。


これも同じ。@scoreの代わりに$scoreを使う

#!/usr/bin/perl -wnlaF'\t'
use strict;

our($score);

BEGIN {
    $ARGV[0] = 'score';
}

push @$score, [ @F ];

END {
    print $score->[0]->[1];
    $, = "\t";
    print @{$score->[2]};
    exit 0;
}

正直ごちゃごちゃしてて、分りにくいと感じてしまう。慣れていないせいなのか、書き方が悪いのか。まだ理解が不足してるんだろうな

トラックバック - http://perl.g.hatena.ne.jp/ishiduca/20090601