• ベストアンサー

ハッシュの値のグループ化について

入門本に、次のような例題がありました(主要部分の抜粋です)。 @list = (20,25,24,32,28,20,28,24,20); #年齢リスト foreach $fld(@list){   $syukei{$fld}++; } print "<table><tr><th>年齢</th><th>人数</th></tr>\n"; foreach $key( reverse( sort( keys( %syukei ) ) ) ){   $cnt = $syukei{$key};   print"<tr><th>${key}才</th><th>${cnt}人</th></tr></table>\n"; これの実行結果は、@list の年齢を各年齢ごとにカウントして、表に各年齢が何人であるかを表示します。 ・$syukei{$fld}++; の部分は $ハッシュ名{'キー名'} でハッシュを作ろうとしているのだと思うのですが、++ がなぜ付くのか分かりません。 ・↑の考え方が合っているとしたら、$cnt = $syukei{$key} で右辺は $ハッシュ名{'キー名'} を表しているのだと思うのですが、左辺がスカラー値を求めている場合はキーの数を返すというルールがあるのでしょうか? 私の質問にこだわらず、分かりやすく全体の説明をして頂ければ助かります。

  • Perl
  • 回答数3
  • ありがとう数4

質問者が選んだベストアンサー

  • ベストアンサー
  • Fooky
  • ベストアンサー率71% (59/82)
回答No.3

手ごわいですね。敢えて説明に挑戦してみます。 どうも、ハッシュの概念またはハッシュの記法の 理解が間違っておられるように見受けられますが。 ハッシュって要はExcel(じゃなくてもいいですが)の 表みたいなもんで、表の左端の列に書かれてる内容で 行を特定する、というイメージです。 回答者   回答日 deagle   01-09-07 zeyper   01-09-07 Fooky   01-09-09 という表があったとします。表の名前はAnsDateとでも しましょうか。ハッシュでは、回答日を取り出すのに、 回答者の名前を使って取り出せる、ということです。 ですから、print $AnsDate{'Fooky'}; の実行結果は 01-09-09となります。このときの回答者名が 「ハッシュキー」、回答日が「ハッシュ値」となります。 表全体が「ハッシュテーブル」、各行が「ハッシュ」と 呼ばれます。 1つ、Excelの表と違うところは、同じ回答者名を持った 行(=同じハッシュキーを持つ異なるハッシュ値)はつくれない、 ということです。ですから、ここで新たに全く同じハンドル ネームのFookyって人が、10日に回答をしてしまうと、 $AnsDate{'Fooky'} = '01-09-10';という代入が為され、 先程の表は 回答者   回答日 deagle   01-09-07 zeyper   01-09-07 Fooky   01-09-10 となってしまいます。 一方、普通の配列は、何行目かを指定することで データを取り出す表、というイメージになります。 ですから、先程の表は、 番号  回答日 0   01-09-07 1   01-09-07 2   01-09-09 となり、0番目の回答者が何日だったかなぁってことで、 print $AnsDate[0];として、01-09-07を取り出します。 ちょっと難しく考えすぎではないですか? 要素番号でしか参照できない配列が、 もうちょっと使いやすくなって、要素番号の ところに任意の文字列が使えるようになった だけのことなんですが。 さて、例題のforeachのところを展開してみると、 $syukei{'20'}++; $syukei{'25'}++; $syukei{'24'}++; .... $syukei{'28'}++; $syukei{'24'}++; $syukei{'20'}++; という処理が順に行われているだけです。 ここで新たに問題になるのは、$syukei{'20'}という のは初めて出て来たので、文字列'20'をキーとする ハッシュはまだ存在しない(=まだ表の左端に20 と書かれた行が存在しない)ということです。 まだ存在しないハッシュ値に対して、1増やす、 という算術演算を施したらどうなるのか? 答えは、'20'をキーとして、ハッシュ値が'0'である ハッシュが自動的に作成され、それに1増やす という処理が行われます。 そうすると、最終的に、 $syukei{'20'} == 3, $syukei{'25'} == 1, $syukei{'24'} == 2, $syukei{'32'} == 1, $syukei{'28'} == 2 という結果になるのは、不思議ではないと思うんですが。 しつこいようですが、この結果を表として書くと、 年齢   人数 20    3 25    1 24    2 32    1 28    2 という表になります。 例題の後半は、年齢の大きいものから順に表示される ように工夫しているだけです。

mika-o
質問者

お礼

ありがとうございます!ゆっくり考えて、大方理解できました。 私が分かってなかったのは、$syukei{$fld}++; の加算がどの時点行われるか、ということです。私は $fld の値(年齢の数値)に加算されるものだと勘違いしていました。 Fookyさんのおっしゃる「存在しない値に1を加算する」という概念がなかったんです。 foreach $fld(@list){   $syukei{$fld}++; の時点で、目には見えないけどハッシュテーブルを作ってるんだと理解すれば、$syukei{$fld}++; の指し示す行へ1が加算されるのも分かります。 その後の foreach $key( reverse( sort( keys( %syukei ) ) ) ){   $cnt = $syukei{$key}; も、それで理解できました。

その他の回答 (2)

  • zeyper
  • ベストアンサー率12% (1/8)
回答No.2

初めまして、私も最近勉強を始めたものです。 まず、ハッシュとは aaa-bbb0-ccc0   -bbb1-ccc1   -bbb2-ccc2 と言う感じになっていてこれは、%aaaというハッシュになります。 %aaaのbbb0の中に、ccc0と言う値が入っているわけです。 ちなみにこれが@aaaだと、bbb0は数字0、bbb1は数字1でccc0がその値となります。 ですから、ハッシュとは、値(ccc)を入れるだけでなく、その値の入っているキー(bbb)も決められる、配列(@)のパワーアップ版みたいなものだと思ってください。 余計に分からなくなったらすいません。 ハッシュが分かったところで(分からなくても(笑))上のプログラムは、 syukei-20-3    -24-2    -25-1    -28-2 -32-1 と言うハッシュになります。++は下の専門家の言った通りだと(下を読んで初めて知った)数字を1足す、"$***++"と同じ効果があると言うことなので、 syukei-20- と初めて20を出したら++で syukei-20-1 とし、もう一度20を出したら++で、 syukei-20-2 として、それを最後に $cnt = $syukei{$key}; で$cntに値を入れているというわけです。ちなみにこの時$keyが20だと 3 = $syukei{20}; となります。 こんな雑な説明でわかっていただけたら幸いです。 最後にこの時 $abc = @list; とやると$abcには9が入ります。 配列をスカラー変数に入れると、配列の中のリストの数を返します。 $ハッシュ名{'キー名'} はキー名の中のスカラー変数を表しているので、$***の中にはスカラー変数を返しているだけです。

mika-o
質問者

お礼

ありがとうございます。しかし、 foreach $fld(@list){   $syukei{$fld}++; } の部分は $fld に @list の要素を一通り代入しなさい、という命令ですよね? それがなぜ @list の各要素をキーとして代入し、その登場回数を値として代入するのかが分かりません。 配列をスカラー変数に入れると、配列の中のリストの数を返すというのは分かりましたが、 「$ハッシュ名{'キー名'} はキー名の中のスカラー変数を表しているので、$***の中にはスカラー変数を返しているだけです。 」という説明が分かりません。

noname#25358
noname#25358
回答No.1

 またまた登場(笑)  えー。まずこのプログラムは、ハッシュ配列 syoukei に、年齢をキーとしてその人数を入れ、逆順ソートした上で出力する、というものですね。  ちょっとばかしHTMLの組み方がおかしいようですが(笑)  で、ご質問の部分、の「++」は、これは単純に「カウントアップ」の意味です。  $syoukei{$fld} には「$fld 歳」の人の人数が入っているわけですから、これにプラス1しているのです。これを、インクリメント機能と呼びます。  このカウントアップ自体はハッシュに関する機能ではなく、すべての変数に対して使うことが出来ます。  (すべての変数は未定義状態では0、null、undefine のいずれかであると定義されるので、最初のカウントアップの直後には変数の値は1になります)  逆に $syoukei{$fld}--; とするとこれはカウントダウンで、マイナス1されます。  ですので、$cnt = $syoukei{$fld} では、$cnt に単純に、カウントアップして得られた集計結果を代入しているだけということになります。

mika-o
質問者

お礼

@list = (20,25,24,32,28,20,28,24,20); #年齢リスト foreach $fld(@list){   $syukei{$fld}++; } print "<table><tr><th>年齢</th><th>人数</th></tr>\n"; foreach $key( reverse( sort( keys( %syukei ) ) ) ){   $cnt = $syukei{$key};   print"<tr><th>${key}才</th><th>${cnt}人</th></tr></table>\n"; 私の解釈では、 ■foreach $fld(@list){ は、@listの要素を一通り$fldにする→従って $syukei{$fld} は $syukei{$20} など@listにある年齢を入れたもの9つを用意する→$syukei{$20}++ なんていう文は成り立たないのでは? となって、止まってしまいます。

関連するQ&A

  • perl キーの集計について foreach文

    @list=(20,25,24,32,28,20,28,24,20); という、ある組織の人間の年齢の配列を定義したとします。 そこで、 foreach $fld(@list){ $syukei{$fld}++; } というforeach文によって、年齢を「キー」として集計していくという説明を受けたのですが、いきなりスカラー変数の$syukeiが出てきて、さっぱりわかりません。 $syukei{$fld}++; という一行の意味を教えてくださらないでしょうか。 また、++という記号を使用していますが、これを別の書き方で書き換えることはできないでしょうか。

    • ベストアンサー
    • CGI
  • ハッシュの要素の取り出し方

    ハッシュの要素の取り出し方で困っています。 以下のようにハッシュの要素を取り出して thmlタグの中に埋め込もうとしたのですが、 push( @arry, { 'syubetu' => 'cat' , 'name' => 'たま', 'age' => '12才', }, { 'syubetu' => 'dog' , 'name' => 'ポチ', 'age' => '2才' , }, { 'syubetu' => 'bird' , 'name' => 'ピ-コ', 'age' => '5才' , } ); @html_tag = ( '<table><tr><td>' . "\n", 'syubetu=$syubetu : name=$name : age=$age' . "\n", '</td></tr></table>' . "\n", ); foreach ( @arry ){ foreach $tag( @html_tag ){ $tag =~ s/\$syubetu/$_->{"syubetu"}/gm; $tag =~ s/\$name/$_->{'name'}/gm; $tag =~ s/\$age/$_->{'age'}/gm; print $tag; } $tag2 .= "syubetu: " . $_->{'syubetu'} . "\r\n"; $tag2 .= "name: " . $_->{'name'} . "\r\n"; $tag2 .= "age: " . $_->{'age'} . "\r\n"; } print $tag2; 結果が先頭の要素を登録数分、繰り返し出力してしまいます。 でも、同じforeachの中でも$tag2の場合は 正常に出力されます。 この違いがわからなくて困っています。 どなたか教えていただけますでしょうか。 ・出力結果 <table><tr><td> syubetu=cat : name=たま : age=12才 </td></tr></table> <table><tr><td> syubetu=cat : name=たま : age=12才 </td></tr></table> <table><tr><td> syubetu=cat : name=たま : age=12才 </td></tr></table> syubetu: cat name: たま age: 12才 syubetu: dog name: ポチ age: 2才 syubetu: bird name: ピ-コ age: 5才

    • ベストアンサー
    • Perl
  • ハッシュとif文

    CGIを改造しようと何とかがんばってます。 ハッシュから確答するものを表示したいのですが、うまく出来ません。 まずは、別ファイルにリストを保存しています。 %jlist = ( 11 => '項目1', 12 => '項目2', 13 => '項目3', 14 => '項目4', ~~ ); そして、ログファイルには数字が保存されています。 $genre←11から99までの数字が保存 これをwhileで繰り返し表示するリストにちゃんと項目名を表示させたいのですが、うまく出来ません。 foreach $key (keys(%jlist)){  if($key eq $genre){  print "[$value]\n";  } } とてつもなく変なことをしているかもしれませんが、どのようにしたら解決できるか教えてください。

    • ベストアンサー
    • Perl
  • 重複レコードをグループ化したいけど…。

    度々お世話になってます。 table1 【ID|フィールド1】 [1|AAA] [1|BBB] [2|CCC] [3|DDD] [3|EEE] [3|FFF] というようなテーブルがあるとして、クエリでは <table> <tr><td>1</td><td>AAA<br>BBB</td></tr> <tr><td>2</td><td>CCC</td></tr> <tr><td>3</td><td>DDD<br>EEE<br>FFF</td></tr> </table> …という形で表示したいのです。(理想です) 今までいろいろ試してみて、 $sql = " SELECT ID,フィールド FROM table1 GROUP BY ID "; に行き着きましたが、これだと <table> <tr><td>1</td><td>AAA</td></tr> <tr><td>2</td><td>CCC</td></tr> <tr><td>3</td><td>DDD</td></tr> </table> というように表示され、表示されないレコードが出来てしまいます。 GROUP BYが余計!と言われそうですが、同じIDが複数並ぶのを避けたいのです。 ソースコードは、 print "<table>\n"; print "<tr><th>ID</th><th>フィールド</th></tr>\n";  while( $row = mysql_fetch_array($result) ) {  print "<tr><td>$row[ID]</td><td>$row[フィールド]</td></tr>\n";  } print "</table>\n"; になっている状態です。 $row[フィールド]を書き換えればいいのかな?と思いますが、検索してもそれらしいものは見当たらなかったので質問させていただきました。 心当たりのある方、よろしくおねがいします。

    • ベストアンサー
    • PHP
  • Ruby 2次元のハッシュ

    Rubyで2次元のハッシュを扱いたいです。 perlで書くと以下のような感じです。(最近perlに疎遠なので自信無いですが^^;) hash{$key1}{$key2}=$value; foreach $key1 (keys %hash){ foreach $key2 (keys %{$hash{$key1}}){ print "$hash{$key1}{$key2}\n"; } } Rubyだとどんな感じになりますか?

  • 【PHP】smartyとPHPの記述違いとメリット

    smartyというのが、デザインとプログラムを分けれるというので 使ってみたのですが、 //--------------------smarty--------------------- <table border=1> <tr> <th>{$table.title}</th> </tr> {foreach item=result from=$data_list} <tr> <td>{$result[0]}</td> </tr> {/foreach} </table> と、smarty用の記述をHTML内にいろいろ書かなければならず //------------------php---------------------- <table border=1> <tr> <th><?=$table[title] ?></th> </tr> <?php foreach($data_list as $key => $value){ ?> <tr> <td><?= $value ?></td> </tr> <?php } ?> </table> とHTML内にPHPをいろいろ書くのと  あまり大差無い気がして どういう点がよいのがイマイチ分かりません。 smartyは 使う メリットは どういうものがあるのでしょうか。

    • ベストアンサー
    • PHP
  • ハッシュの中身の表示

    ハッシュの中身の確認ができなくて困っています。 下記のような実行文においてです。 当然、test の戻り値は、スカラーとハッシュです。ハッシュを戻すときには参照渡し記号の\もつけています。 my ($return_code, %hash_data) = test(); 表示しようとすると、 Hash(0x5b04) のような表示にしかなりません、、 (試した表示方法は、下記4つです。) (環境は、WindowsXP上での、ActivePerl-5.10.0.1004 です。) foreach $key ( keys( %Hash ) ) { print "キー値 : $key\n"; print "値 : $Hash{$key} \n " } while ( ( $key , $value ) = each %Hash ){ print "キー値 : $key\n"; print "値 : $value \n " ; } use Data::Dump qw(dump); print dump(\%hash); #print %display_test; 宜しくお願いします。

    • ベストアンサー
    • Perl
  • テーブルデータ表示

    mysqlからデータをphpで取得し以下のような多次元配列になっています。 Array ( [0] => Array ( [id] => 116 [name] => あああ ) [1] => Array ( [id] => 58 [name] => いいい ) [2] => Array ( [id] => 89 [name] => ううう ) ) 単純にデータを表示させたく以下のようにしましたが<th>$key2</th>の箇所が上記配列の場合 2回繰り返されて表示されてしまいます。ここはフィールド名なので1回の表示でいいのですが どのように記述すれば思うような表示になるでしょうか? echo "<table border=\"1\">"; echo "<tr>"; foreach ($tmp1 as $key => $val) { foreach ($val as $key2 => $val2) { echo "<th>" . $key2 . "</th>"; //フィールド名 } } echo "</tr>"; foreach ($tmp1 as $key => $val) { echo "<tr>"; foreach ($val as $key2 => $val2) { echo "<td>" . $val2 . "</td>"; // 取得したデータをある分だけ繰り返し } echo "</tr>"; } echo "</table>";

    • ベストアンサー
    • PHP
  • CSVデータベース

    PHPを使い CSVのデータを表示させる事ができました。 がっ見た目がどうも汚いですよね・・・ もっとすっきりする方法はあるんでしょうか? あとCSVのデータを検索させるようにしたいのですが何かサンプルとかありませんでしょうか? 注文多くて申し訳ないですが、是非ともご教授よろしくお願い致します。 <table width="550" class="table01"> <tr> <td width="32" height="18"><div align="center"><strong>画像</strong></div></td> <th width="123"><strong>情報1</strong></th> <th width="58"><strong>情報2</strong></th> <th width="56"><strong>情報3</strong></th> <th width="53"><strong>情報4</strong></th> <th width="55"><strong>情報5</strong></th> <th width="56"><strong>情報6</strong></th> <th width="81"><strong>対象</strong></th> </tr> <?php $csv = fopen ("./data/new.csv", "r") or die(print "ファイルを開く事が出来ませんでした"); while ($items = fgetcsv ($csv, 1000,",")) { print "<tr>\n"; print "<td height='40' rowspan='2'><img src='$items[0]'/></td>\n"; print "<td><center>$items[1]<center></td>\n"; print "<td>$items[2]</td>\n"; print "<td>$items[3]</td>\n"; print "<td>$items[4]</td>\n"; print "<td>$items[5]</td>\n"; print "<td>$items[6]</td>\n"; print "<td>$items[7]</td>\n"; print "</tr>\n"; print "<tr>\n"; print "<td height='16' colspan='7'><div align='left'>$items[8]</div></td>\n"; print "</tr>\n"; } fclose($csv); ?> </table>

    • ベストアンサー
    • PHP
  • 動的ハッシュを作って取り出したいのですが・・・

    お世話になります。 フォームから送られてくるデータを動的に作ったハッシュで参照出来るように取り組んでるんですが、思ったように出来ず思い悩んでおります。 どうすれば、意図した形でデータを取り出すことが出来ますでしょうか my %FORM = ( 'd01' => 'あ', 'd02' => 'い', 'd03' => 'う', 'd04' => 'え', 'd05' => 'お', 'd06' => 'か', 'd07' => 'き', 'd08' => 'く', 'd09' => 'け', 'd10' => 'こ', ); for(sort { $FORM{$a} cmp $FORM{$b} } keys %FORM){ print "$_ = $FORM{$_} \n"; } $list="d01,d02,d03,d04,d05,d06,d07,d08,d09,d10,"; $i=-1; foreach (split/,/,$list){ $i++; $hash{$_}=$i; } for(sort { $hash{$a} <=> $hash{$b} } keys %hash){ print "$_ = $hash{$_} \n"; $view = ${"FORM$_"}; print "$view\n"; }; 最後のprint "$view\n";箇所で、 $list="d01,d02..." を split/,/,$list したので、 $FORM{d01} $FORM{d02} となるようにして、 「あ い う え お」と取り出したいのです。 ご教授のほど、よろしくお願い致します。

    • ベストアンサー
    • Perl

専門家に質問してみよう