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

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

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

our($h);

foreach( @ARGV ){
    m|^\d{4}/\d{1,2}/\d{1,2}$|
      or warn "Usage: Argument is \"YYYY/MM/DD\"\n" and exit 255;
}

my $usage=<<"USAGE";
Usage : $0 -h
        $0 YYYY/MM/DD [YYYY/MM/DD]
USAGE
;

$h and print $usage and exit 0;

@ARGV or warn $usage and exit 255;
my $employ_day = shift;
my %employ_day = &to_hash_date($employ_day);

my %today;
if( $ARGV[0] ){
    %today = &to_hash_date($ARGV[0]);    
} else {
    my(undef,undef,undef,$date,$month,$year,undef) = localtime time;
    %today = (
        'year'  => $year + 1900,
        'month' => $month + 1,
        'date'  => $date,
    );
}

printf "満%2s 年\n", &get_years(\%employ_day,\%today);
exit 0;

sub get_years{
    my $employ_day = shift;
    my $today      = shift;
    my $year = $today->{'year'} - $employ_day->{'year'};
    $year-- if $today->{'month'} < $employ_day->{'month'};
    $year-- if ($today->{'month'} == $employ_day->{'month'}) and
               ($today->{'date'} < $employ_day{'date'});
    return $year;
}

sub to_hash_date{
    my($year,$month,$date) = split /\//, $_[0];
    $month =~ s/^0//;
    $date  =~ s/^0//;
    return (
        'year'  => $year,
        'month' => $month,
        'date'  => $date,
    );
}

リファクタリングしたい

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-05-23

[][][]単純なリファレンス 18:30 単純なリファレンス - ishiducaの日記 を含むブックマーク はてなブックマーク - 単純なリファレンス - ishiducaの日記 単純なリファレンス - ishiducaの日記 のブックマークコメント

リファレンスは、値の代わりに、変数名に紐付けされたメモリ上のデータの住所を記録するスカラ変数。


内部的にはこんな感じになってるらしい

  • 変数を定義すると
  • 内部メモリのどこかにデータが格納される
  • 格納されたデータの位置(メモリアドレス)は変数の生涯を通じて変わらない
  • このデータに識別子(変数名)を通じてアクセスする代わりに、位置情報を参照(指定)してアクセスするのがリファレンスを使ったアクセス。

リファレンスを取る(参照するデータの位置情報と型を獲得する)

定義された変数(スカラー、配列、ハッシュ)のリファレンス(参照)を取るには、バックスラッシュ(\)を変数名の前に付ける

my @array = ('Ben Harper', 'Nina Shimone', 'Birdy Nam Nam');
my %hash  = ('time' => '40min', 'long' => '21km', 'cal' => '60cal');

my $referencc_array = \@aray;
my $reference_hash  = \%hash;
  • リファレンスを格納した $reference_xxx には、16進数で位置情報が格納されているので、print文を使うと、こんな感じ
print $reference_array;
print $reference_hash;

実行結果

ARRAY(0x1a2c3b)
HASH(0x80174c)

意味ないね。

参照を取ったデータから値を取り出す(デリファレンス)

print '0:$array[1]              = ', $array[1];
print '1:${$reference_array}[1] = ', ${$reference_array}[1];
print '2:$$reference_array[1]   = ', $$reference_array[1];
print '3:$reference_array->[1]  = ', $reference_array->[1];
print '';

print '0:$hash{\'time\'}              = ', $hash{'time'};
print '1:${$reference_hash}{\'time\'} = ', ${$reference_hash}{'time'};
print '2:$$refrernce_hash{\'time\'}   = ', $$reference_hash{'time'};
print '3:$reference_hash->{\'time\'}  = ', $reference_hash->{'time'};
print '';

{
    local $" = "\n";
    print "@$reference_array";
}
print '';

foreach my $key ( keys %$reference_hash ){
    printf "%4s => $reference_hash->{$key}\n", $key;
}
  • 定義された配列、ハッシュの識別子(変数名)の代わりに参照を取った変数を置き換えることで、データにアクセスする
  • 1 の例では中括弧「{}」で、識別子の境界を明示している。ただし、省略できる(省略した例が2 )
  • 省略できない場合もある。例えば「$_[1]」の様な場合。スカラー変数ではなくアクセス式だからダメ
  • 矢印「->」を使ったアクセス方法も使える。2 の頭の「$」を取っ払って、リファレンスを定義した式に「->」演算子用いてデータにアクセスする(でいいのかな?)

同一のデータを扱うということ

上の例だけだと、配列、ハッシュのコピーと変わらないように見えるけど違う。コピーしたら、コピーした時点でのデータをメモリの別の位置に新たに作るけど、リファレンスの場合は、同一のデータを扱う。

my @copy  = @array;
my $reference_array = \@array;

# 参照をいじってみる
@$reference_array = reverse @$reference_array;

$" = ", ";
print "@array";
# Birdy Nam Nam, Nina Shimone, Ben Harper
print "@copy";
# Ben Harper, Nina Shimone, Birdy Nam Nam

無名の配列、ハッシュを使う

my @array = ('Ben Harper', 'Nina Shimone', 'Birdy Nam Nam');
my %hash  = ('time' => '40min', 'long' => '21km', 'cal' => '60cal');

my $referencc_array = \@aray;
my $reference_hash  = \%hash;

配列やハッシュの定義に識別子(変数名)を定義しないで、直接リファレンスを取る形で定義する事ができる。(「無名配列コンストラクタ」「無名ハッシュコンストラクタ」)

my $reference_array = ['Ben Harper', 'Nina Shimone', 'Birdy Nam Nam'];
my $reference_hash  = {'time' => '40min', 'long' => '21km', 'cal' => '60cal'};

print $reference_array->[-1];
print $reference_hash->{'cal'};
  • 無名配列コンストラクタで配列を定義するには、「[]」を使う
  • 無名ハッシュコンストラクタでハッシュを定義するには、「{}」を使う

今のところのまとめ

データに紐を付ける方法として、名札(識別子(変数名))を使うか、住所(内部メモリの位置情報)を使うかの違い

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