- ベストアンサー
動的な? 多次元ハッシュ
text--------------------- 社会\t1 社会\t社会問題\t4 社会\t社会問題\t教育問題\t5 ---------------------text タブ区切りのテキストファイルを読み込んで 最後の値がハッシュ値になるような 深さがまちまちな多次元ハッシュをループで作りたいのです 手作業だと $HASH{社会}=1 $HASH{社会}{社会問題}=4 $HASH{社会}{社会問題}{教育問題}=5 できるだけ動作を軽くしたいので、evalや$1での置き換え等は 使いたくないのです どなたか教えてください
- みんなの回答 (5)
- 専門家の回答
質問者が選んだベストアンサー
>キー自体を使いたい(splitするコストがかかりますよね) キー自体は、読み出す時にsplitするのだろうし、キーと値が切り出せれば別にsplitする必要もなく、'\t'を'/'にする必要もない(タブそのままでも良い)ですし、 ハッシュから値を読み出す時にも、キーが既に別々になっている状態で $key1."/".$key2 のように連結すればいいだけのことで、 かえってテキストファイルによって動的に深さが決まるようなキーの場合、 %HASH{$key1}{$key2}…のような形で動的に深さを決定してアクセスするのが面倒です。 >いかんせん大量のデータ数&量を処理しようとしているので、なるべく軽くと思ってます 読み込みの部分が大量にあるとしても、それほど、重くなるとも思えません。 ハッシュからのアクセスの部分で言えば、かえって手順が簡単だと思います。 #3で指摘されているように、構造としてもうまく行かない場合があるようです。 #3でも言われているように、大量にデータがあるようなハッシュの場合、データの構造を変更して、DBに接続したハッシュにするのも1つの方法だと思います。
その他の回答 (4)
- Tacosan
- ベストアンサー率23% (3656/15482)
#1 です. う~ん, ちょっとはチェックしたつもりだったんですけど, やっぱりそうなりましたか.... チェックの結果が奇妙だったんですがうまくいったように見えたので.... $hash{a}{b} って ${$hash{a}}{b} と同じだから当然といえば当然. で, while (@keys > 1) { .... } を foreach my $key (@keys) { $vref->{$key} = { }; $vref = $vref->{$key}; } $vref->{val} = $val; くらいでいいかと. 社会\t1 に対して $hash{社会}{val} = 1 になりますけど, まあ許容範囲ではないでしょうか.
補足
追記ありがとうございます 確かにこれくらいが許容範囲ですね、しかもうまくいく! ただ、 BLUEPIXYさん提案の方に傾きつつあります ちょっとテストしてみましたが、10万くらいのデータだとさほど変わらないこと判明 さらに、人として読んだときにはBLUEPIXYさん提案のほうが手が入れやすいようでした 申し訳ない、趣旨変更しそうです でも、ここまで付き合ってくれて感謝します
- osamuy
- ベストアンサー率42% (1231/2878)
> $HASH{社会}=1 > $HASH{社会}{社会問題}=4 > $HASH{社会}{社会問題}{教育問題}=5 evalとかを使わないと記述が複雑になって、そんなに軽くならないし、メンテナンス性が悪くなるかと。 それと別に、こういうデータ構造だと、いくつか問題がありそう。 例えば、 $HASH{社会}=1; $HASH{社会}{社会問題}=4; $HASH{社会}{社会問題}{教育問題}=5; $HASH{社会}{人生問題}=4; $HASH{社会}{人生問題}{教育問題}=15; print $HASH{社会}{社会問題}{教育問題}; ??で、5と出力されません。 use strictを指定した場合、エラーになってしまいますし、リファレンスが生成されるため、通常のハッシュより遅くなります(微々たるものでしょうが)。 なので、BLUEPIXYさんみたいなやり方が適切と思われます。 タブ区切りテキストをやめてdbmにするという手も。
補足
あーほんとだ!(スイマセン) 確かに、うまくいかないですね 朝の5時までかかって考えぬき 出た答えがここに質問しょうでした 朦朧としていたのかもです 構造的なところは全然再検証していませんでした(最初のひらめきだけで進めていました) ん~、かなり納得してます 指摘ありがとうございます、BLUEPIXYさんのやりかたにググッと傾いてきました
- BLUEPIXY
- ベストアンサー率50% (3003/5914)
回答とは違うのですが、多重ハッシュにする理由がよくわかりません。 キーを{社会}{社会問題}{教育問題}とかしないで 社会/社会問題/教育問題とかすれば、 簡単だし軽いと思うのですが #サンプル my %HASH; while (<>) { chomp; my (@keys) = split /\t/; my $val = pop @keys; print join('/', @keys) . "\n"; print "$val\n"; $HASH{join('/', @keys)}=$val } print $HASH{'社会'} . "\n"; print $HASH{'社会/社会問題'} . "\n"; print $HASH{'社会/社会問題/教育問題'} . "\n";
補足
助言(かな?)ありがとうございます 理由ですが、それなりに考えてのことなのですが・・ たとえば、 キー自体を使いたい(splitするコストがかかりますよね) とか、他にもあるんですけど・・・ いかんせん大量のデータ数&量を処理しようとしているので、なるべく軽くと思ってます 簡単さは認めますが、あまり重要ではないんです とりあえず使ったデータ(社会とか社会問題とか)が悪かったかもしれませんね でも、BLUEPIXYさんに感謝!
- Tacosan
- ベストアンサー率23% (3656/15482)
こんな感じかなぁ? my %HASH; while (<>) { chomp; my (@keys) = split /\t/; my $val = pop @keys; my $vref = \%HASH; while (@keys > 1) { $vref->{$keys[0]} = {}; $vref = $vref->{shift @keys}; } $vref->{$keys[0]} = $val; }
補足
ありがとうございます さっそく試してみました ちょっとやりにくいので、多少変更していますが趣旨は変わってないと思います $array[0] = "社会\t1"; $array[1] = "社会\t社会問題\t4"; $array[2] = "社会\t社会問題\t教育問題\t5"; my %HASH; for (@array) { my (@keys) = split /\t/; my $val = pop @keys; my $vref = \%HASH; while (@keys > 1) { $vref->{$keys[0]} = {}; $vref = $vref->{shift @keys}; } $vref->{$keys[0]} = $val; } print $HASH{'社会'} . "\n"; print $HASH{'社会'}{'社会問題'} . "\n"; print $HASH{'社会'}{'社会問題'}{'教育問題'} . "\n"; とすると、 HASH(0x1a99a38) HASH(0x1ab8c64) 5 と出力され、どうもうまくいかないようです
お礼
ありがとうございます ご指摘のとおりに、構造の変更をしようと思います 本当は、初期のころちょっとだけその方法がよぎってました じつは、複数DBの呼び出しのインデックス代わりに使おうと思っていたのです で、最初はテキストを開いて ターゲットのDBファイルを読み込みに行こうと もくろんでました いやー勉強になりました、やっぱり人の意見は役に立ちます osayumさん・・おっとosamuyさんの指摘もあわせてとても有意義なやりとりでした