- ベストアンサー
PerlでHashのキーを制限する方法
- Perlで関数に値を渡す際にハッシュを利用しようと思っていますが、キーのtypoが気になります。検索してみると、Hash::Util::lock_keys()などが見つかりましたが、これは実行されるとエラーになる仕組みです。もし、ハッシュを使わずに引数を渡す方法があれば教えていただきたいです。
- 関数に対して36個の引数を渡す際にミスがないようにしたいです。ハッシュを使わずに他の方法があれば教えてください。
- 引数を順番に指定する方法よりも、ハッシュを使って渡すほうが良いと思いますが、他にもうまい方法があれば教えていただきたいです。ご存知の方がいらっしゃいましたら、教えてください。
- みんなの回答 (4)
- 専門家の回答
質問者が選んだベストアンサー
たしかに、constantで実行前にエラーになりましたね。ただし、以下の場合は通ります。 - MyUtil::KEY_FOO_NAME を KEY_FOO_NAME と書いちゃう - MyUtil::KEY_FOO_NAME を書き忘れちゃう use strict; use warnings; use feature 'say'; { package MyUtil; use constant KEY_FOO_ID => 'id'; use constant KEY_FOO_NAME => 'name'; sub foo { my ($args) = @_; say "id[" , $args->{MyUtil::KEY_FOO_ID} , "] "; say "name[" , $args->{MyUtil::KEY_FOO_NAME} , "] "; } } MyUtil::foo( { MyUtil::KEY_FOO_ID => '01', KEY_FOO_NAME => 'YAMADA' } ); MyUtil::foo( { MyUtil::KEY_FOO_ID => '01' } ); とりあえず、考え付くのは、 1. 新しく提案されたconstant hash key方式を使う 2. 諦めてruntime checkする 3. 1.と2.を組合せる 4. Perl6だと名前付きのパラメータを指定できる(?)よくわかんない http://perlcabal.org/syn/S06.html#Named_arguments --- runtime check ----- 自分で use strict; use warnings; use feature 'say'; use Error qw(:try); sub valid_args { my $params_ref = shift; my $args_ref = shift; for my $param ( @{$params_ref} ) { if ( !exists $args_ref->{$param} ) { die "Not passed : $param for ", ( caller 1 )[3], '()'; } } } sub foo { valid_args( [qw(name age)], {@_} ); } foo( name => 'A', age => 1 ); try { foo( age => 1 ); } catch Error with { my $e = shift; say $e->text; # Not passed : name for main::foo() }; ----- モジュールで use strict; use warnings; use feature 'say'; use Error qw(:try); use Sub::Args; sub foo { my $args = args( { name => 1, # mandatory age => 0 # optional }, @_ ); } foo( name => 'A' ); foo( name => 'A', age => 1 ); try { foo(); } catch Error with { my $e = shift; say $e->text; }; ----- 引数の明示という観点からのPerlモジュール群 | hirobanex.net http://hirobanex.net/article/2011/06/1306973413
その他の回答 (3)
- _--_1l1_1_
- ベストアンサー率67% (102/152)
BEGINでやらなくてもいいか。 use strict; use warnings; use feature 'say'; my $foo_name; my $foo_age; my %foo_args = ( name => \$foo_name, age => \$foo_age ); sub foo { my %args = (@_); say ${$args{name}}; say ${$args{age}}; } $foo_name = 'hoge'; $foo_age = 1234; foo(%foo_args); $fooo_name = 'hoge'; $fooo_age = 1234; foo(%foo_args);
お礼
回答ありがとうございます。 先に変数を定義しておいてそこに突っ込む形ですね。 定義していない変数(typo)に入れようとすると怒られる、と…。 ただ引数を受け取る関数自体は 関数を呼び出すロジックとは別ファイルになるので our変数かな… 若干微妙かな… とか色々考えたのですが、 ふと未定義変数で怒られるんじゃなくて 未定義定数で怒られれば良いのではないかと思いました。 ということでハッシュのキー値を定数定義しておいて それを利用すれば良いのじゃないかと思ったのですがどうでしょうか。 # テストロジック全体は別のお礼に貼ったので一部抜粋 別ファイル -------------------- package MyUtil; use constant KEY_FOO_ID => 'id'; ... -------------------- 呼び出し部 -------------------- my $args = { MyUtil::KEY_FOO_ID => '01' }; MyUtil::foo($args); -------------------- 皆さんの意見色々参考になります。 ありがとうございます。
- _--_1l1_1_
- ベストアンサー率67% (102/152)
強いてやるなら、こうかなぁ? 表示がくずれるので、空白2文字を全角空白にしていることに注意 use strict; use warnings; use feature 'say'; BEGIN { sub local_die { print $_[0]; exit 1; } $SIG{__WARN__} = \&local_die; our $foo_name; our $foo_age; our %foo_args = (name => \$foo_name, age => \$foo_age); } sub foo { my %args = (@_); say ${$args{name}}; say ${$args{age}}; } $main::foo_name = 'hoge'; $main::foo_age = 1234; foo(%main::foo_args); $main::fooo_name = 'hoge'; $main::fooo_age = 1234; foo(%main::foo_args);
- tsuduki123
- ベストアンサー率32% (21/65)
どうしてもという場合には lvalue を使いますかねぇ? あんまり使ったことないので自信はないです。 個人的なおすすめは配列にして普通に変数でアクセスですね。 以下はlvalueのサンプルです #!/usr/bin/env perl use strict; use warnings; package SAMPLE; { sub new() { my $prot = shift || 'SAMPLE'; return bless { 'MA' => 0 , 'MB' => 0 },$prot; } sub MA : lvalue { shift->{'MA'}; } sub MB : lvalue { shift->{'MB'}; } } package main; my $sample = SAMPLE->new(); $sample->MA = 5; printf "MA:%d, MB:%d\n", $sample->MA, $sample->MB; $sample->MC = 5; exit 0
お礼
lvalue というのは初めて知りました。Perl は色々ありますね… ありがとうございます。 やっぱり配列ですか。 Perl歴が浅いので他の人はどうしている/どうするのかとか参考になります。 ただ引数が多い場合、配列だとうっかり順番が違ってても気づきにくいんじゃないかと気になるんですよね… みなさんのソースを見つつ自分でも色々考えていたのですが、 キー値を定数定義してそれを使うようにしたらどうかと思いました。 どうなんだろう… 一応定義していない定数(typo)を使おうとすると use strict; で怒ってもらえるけど… 一応貼っておきます。 { package MyUtil; use constant KEY_FOO_ID => 'id'; use constant KEY_FOO_NAME => 'name'; sub foo { my ($args) = @_; print "id[" . $args->{MyUtil::KEY_FOO_ID} . "] "; print "name[" . $args->{MyUtil::KEY_FOO_NAME} . "] "; } } sub test { # ハッシュに値を詰める my $hsref = { MyUtil::KEY_FOO_ID => '01', ## MyUtil::KEY_FOO_NAMO => 'SUZUKI', # error MyUtil::KEY_FOO_NAME => 'YAMADA' }; # 関数に渡す MyUtil::foo($hsref); } #test();
お礼
ありがとうございます。 自分で定義した定数は絶対にパッケージ修飾なしで使わない、みたいに縛っちゃおうかと思います。 どこで定義されている定数か一目でわかるしそのほうが良いかなと。 それでもそもそも指定しわすれ、というのはもうどうしようもないので やはり 1 と 2 の組み合わせで行こうかと思います。 ハッシュから値を取り出す関数を作ってみました。 以下の関数を利用して取り出そうかなと。 #------------------------------------------------ # ハッシュから値を取り出す # 期待したキーが存在しない場合は死ぬ #------------------------------------------------ sub get_hash_value( $$ ) { my ($hsref, $key) = @_; if (! exists $hsref->{$key}) { my ($package, $filename, $line, $subroutine) = caller(0); die "Notfound key[$key]\n$package $filename $line"; } return $hsref->{$key}; } Perl6 の名前付き引数、ぐぐってみました。 こんなのもあるんですね。 Perlは知らないものが色々ある… ありがとうございました (´人`)