Perl入学式の練習問題をおさらいする (第3回)

第3回

[次のような操作をするコードを作成(array_func1.pl)]
1.('Alice', 'Bob', 'Chris') という配列を作って出力してみましょう。

my @array=("Alice","Bob","Chris");        
print "@array\n";       


2.関数を使って 先頭の'Alice'を取り出して出力してみましょう。

my $pickup = shift @array;  
print "$pickup\n";  

3.関数を使って末尾の 'Chris' を取り出して出力してみましょう。

my $pickup = pop @array;  
print "$pickup\n";  

[次のような操作をするコードを作成(array_func2.pl)]
1.('Alice', 'Bob', 'Chris') という配列を作って出力してみましょう。

my @array=("Alice","Bob","Chris");  
print "@array\n";

2.1の配列を元に、関数を使って ('Zappa','Alice','Bob','Chris','Diana') という配列を作って出力してみましょう。

unshift @array,"Zappa";  
push @array,"Diana";  
print "@array\n";  

[次のような挙動をするコードを作成(join.pl)]
1.("0120" "123" "XXX") という内容の配列を qw ショートカットを使って作ってみましょう。

my @array=qw/0120 123 XXX/;  
print "@array\n";  

2.1で作った配列を繋げてみましょう。なんだか電話番号っぽいので、ハイフン ('-') を間にはさみましょう。

my $number=join "-",@array;
print "$number\n";

[次のような挙動をするコードを作成(split.pl)]
1.文字列 "/usr/bin/env perl" という文字列をスラッシュ ('/') で分割し、配列に格納してみましょう。

my @array=split /\//, "/usr/bin/env perl";

2.その配列を出力した時の結果を予想してみましょう. 予想出来たら実際に出力してみましょう.

print "@array\n";

[次のような挙動をするプログラムを作成(hash_profile.pl)]
1.自分の名前 (name)、年齢 (age)、好きな食べ物 (food) を key にしたハッシュを作ってみましょう。

my %hash=(name=>"oisius",age=>39,food=>"rice");

2.key である name, age, food を用いて、それぞれの value をprintしてみましょう。

print "name->$hash{name}\n";
print "age ->$hash{age}\n";
print "food->$hash{food}\n";

[次のような処理を実行するコードを作成(hash_func.pl)]
1.hash_profile.plで作ったハッシュを用意し、そのハッシュの key の一覧を出力してみましょう。

print keys %hash,"\n";

2.existsで年齢の要素が存在するかどうかを確認してみましょう。

if (exists $hash{age}){
  print "exists";
}

3.ハッシュから、年齢(age)の要素を取り除いてみましょう。

delete $hash{age};

4.年齢(age)の要素を削除した後、きちんと削除したか確認します。key の一覧を表示した後、existsで年齢の要素が存在しないことを調べ、きちんと削除されている場合は"Age is not exist."と表示するようにしてみましょう。

print keys %hash;
if (exists $hash{age}){
  print "exists";
}else{
  print "Age is not exist.";
}

[下記のような名前・住所・年齢といった要素を持つリファレンスを新たに作成してみましょう]
さらに Data::Dumper を用いて、作ったリファレンスを出力してみましょう。(reference_dump.pl)
my $papix = {
name => 'papix',
address => 'Tokyo',
age => 25,
};

use Data::Dumper;
my $oisius = {
    name    => 'oisius',
    address => 'Osaka',
    age     => 39,
};
print Dumper($oisius),"\n";


[score.pl には上記のようなハッシュリファレンスがいくつか宣言してあります。]
各人物の perl, ruby, python ... といった言語の合計値を key sum の value としてリファレンスに追加しましょう。
例: $papix の合計値は 270 なので、以下のようになります。(ただし、key の順番がこの通りになるとは限りません)
my $papix = {
name => 'papix',
affiliation => 'namba.pm',
perl => 60,
python => 50,
ruby => 50,
php => 80,
binary => 30,
sum => 270, # => 各言語の合計ポイントが格納されている!
};

my $papix = {
    name        => 'papix',
    affiliation => 'namba.pm',
    perl        => 60,
    python      => 50,
    ruby        => 50,
    php         => 80,
    binary      => 30,
};
my $boolfool = {
    name        => 'boolfool',
    affiliation => 'namba.pm',
    perl        => 40,
    python      => 10,
    ruby        => 20,
    php         => 30,
    binary      => 10,
};
my $moznion = {
    name        => 'moznion',
    affiliation => 'hachioji.pm',
    perl        => 100,
    python      => 70,
    ruby        => 80,
    php         => 50,
    binary      => 50,
};
my $binarian = {
    name        => 'binarian',
    affiliation => 'hachioji.pm',
    perl        => 10,
    python      => 11,
    ruby        => 1,
    php         => 100,
    binary      => 100,
};
my $uzulla = {
    name        => 'uzulla',
    affiliation => 'hachioji.pm',
    perl        => 1,
    python      => 0.01,
    ruby        => 0.5,
    php         => 4,
    binary      => 0.01,
};

my @all=($papix,$boolfool,$moznion,$binarian,$uzulla);
my @words=qw/perl python ruby php binary/;

for my $darekasan(@all) {
  for my $word(@words){
    $darekasan->{sum}+=$darekasan->{$word};
  }
print Dumper($darekasan),"\n";
}

こういうとき、$darekasan->{sum}は初期化?

[復習問題]

1.vote.pl
1-1. リファレンスの作成
「自分の名前 (name)」と「好きな食べ物の配列のリファレンス (favorite_foods)」を持ったハッシュリファレンスを作成しましょう。(「好きな食べ物」は「配列」ですから、複数個必要ですね)
同様のハッシュリファレンスを2, 3個作ってみましょう。

my $oisius ={name=>"oisius" ,favorite_foods=>[qw/rice sushi/]};   #$oisius=|oisius|A001|
my $julien ={name=>"julien" ,favorite_foods=>[qw/tomato sushi/]};
my $coyote ={name=>"coyote" ,favorite_foods=>[qw/steak sushi/]};
my $skipper={name=>"skipper",favorite_foods=>[qw/rice steak/]};

#配列のリファレンスは[]

1-2. リファレンスを配列に格納
作成した複数のハッシュリファレンスを1つの配列に格納しましょう。
直に代入しても良いですし、すでにサブルーチン(関数)を学んだ後であれば、配列を操作する関数を使っても良いですね。

my @all=($oisius,$julien,$coyote,$skipper);
 #@all=|a001|a002|---   |a001|=|oisius|A001|---  |A001|=|rice|sushi|

1-3. ランキングの表示
どのような方法でも良いので、好きな食べ物のランキングを作って、表示してみてください。
複数のハッシュリファレンスを格納した配列を for でひと通り回して (各ハッシュリファレンスにアクセスし)、配列リファレンスfavorite_foodsの中身を別の配列にひと通り格納して……という方法が1つ考えられますね!

さっぱりなので回答を見て解読した。

#ランキングをセットする
#%ranking---key:食べ物名、値:票数
my %ranking;
for my $darekasan(@all){
  my @foods=@{$darekasan->{favorite_foods}};   
       #$darekasan->{favorite_foods}=|A001|なのでそのデリファレンス|A001|=|rice|sushi|
  for my $food(@foods){   #|rice|sushi|から一つずつ
    $ranking{$food}++;    
  }                       
}
print Dumper(%ranking);

#%ranking key  :rice|sushi|tomato|steak| 
#         value:2   |3    |1     |1    |


#%votes---key:票数、値:その票数を得た食べ物一覧(配列のリファレンスで格納)。
my %votes;
for $_(keys %ranking){       #ex.1巡目->rice,2巡目->sushi,3巡目->tomato,4巡目->steak
  push @{$votes{$ranking{$_}}},$food;  
      #@{$votes{3}}---$vote{3}は配列のリファレンス %ranking内の食べ物のkeyの値(票数)
}
#%vote key  :0   |1    |2     |3    | 
#      value:    |e001 |f001  |g001 |・・・e001=|tomato|steak|  f001=|rice|  g001=|sushi|


#票数でsortして表示する
for my $keys(sort{$b<=>$a} keys %votes){  #大きい順に並べ替える(%votesのkeyでsort)キーがsortされて$keysに入る
  print "$keys: \n";                        #ex.1巡目->3,2巡目->2,3巡目->1...
  for my $food(@{$vote{$keys}}){       
            #%voteの要素、$votes{$keys}の、デリファレンスした配列の中から1個ずつ取り出す
    print "$food\n";
  }
}


2.score.pl
score.pl には、下記のようなハッシュリファレンスがいくつか宣言してあります。

my $papix = {
name => 'papix',
affiliation => 'namba.pm',
perl => 40,
python => 10,
ruby => 20,
php => 0,
binary => 0,
};

2-1. 点数の合計

  *上部に同じ問題あり省略

2-2. 言語ごとの平均

$averageというハッシュリファレンスを新たに作り、各人物のperlrubypythonの平均値を格納してください。

my $papix = {
    name        => 'papix',
    affiliation => 'namba.pm',
    perl        => 60,
    python      => 50,
    ruby        => 50,
    php         => 80,
    binary      => 30,
};
my $boolfool = {
    name        => 'boolfool',
    affiliation => 'namba.pm',
    perl        => 40,
    python      => 10,
    ruby        => 20,
    php         => 30,
    binary      => 10,
};
my $moznion = {
    name        => 'moznion',
    affiliation => 'hachioji.pm',
    perl        => 100,
    python      => 70,
    ruby        => 80,
    php         => 50,
    binary      => 50,
};
my $binarian = {
    name        => 'binarian',
    affiliation => 'hachioji.pm',
    perl        => 10,
    python      => 11,
    ruby        => 1,
    php         => 100,
    binary      => 100,
};
my $uzulla = {
    name        => 'uzulla',
    affiliation => 'hachioji.pm',
    perl        => 1,
    python      => 0.01,
    ruby        => 0.5,
    php         => 4,
    binary      => 0.01,
};
my $sum;
my $cnt;
my $avg;

my @all=($papix,$boolfool,$moznion,$binarian,$uzulla);
my @words=("perl","ruby","python");

for my $darekasan(@all) {
  for my $word(@words){
    $sum->{$word}+=$darekasan->{$word};
    $cnt->{$word}++;
  }
}
for my $word(@words){
	$avg->{$word}=$sum->{$word}/$cnt->{$word};
}
print "sum--\n",Dumper($sum),"\n";
print "cnt--\n",Dumper($cnt),"\n";
print "avg--\n",Dumper($avg),"\n";


2-3. 五段階評価

下記を参考に、各人物の言語ごとの成績を5段階で出力して下さい。
全角文字を出力するので、冒頭(use warnings;の下など)にbinmode STDOUT, ":utf8";と書いておくとよいでしょう

0 - 19 =>
20 - 39 => ★
40 - 59 => ★★
60 - 79 => ★★★
80 - 99 => ★★★★
100 => ★★★★★

STDOUTの使い方は?と思ったけど、print関数は実はSTDOUTしている模様。

#!/usrr/bin/env perl
use strict;
use warnings;
use Data::Dumper;
#binmode STDOUT, ":utf8";←これ入れると文字化けした。

my $papix = {
    name        => 'papix',
    affiliation => 'namba.pm',
    perl        => 60,
    python      => 50,
    ruby        => 50,
    php         => 80,
    binary      => 30,
};
my $boolfool = {
    name        => 'boolfool',
    affiliation => 'namba.pm',
    perl        => 40,
    python      => 10,
    ruby        => 20,
    php         => 30,
    binary      => 10,
};
my $moznion = {
    name        => 'moznion',
    affiliation => 'hachioji.pm',
    perl        => 100,
    python      => 70,
    ruby        => 80,
    php         => 50,
    binary      => 50,
};
my $binarian = {
    name        => 'binarian',
    affiliation => 'hachioji.pm',
    perl        => 10,
    python      => 11,
    ruby        => 1,
    php         => 100,
    binary      => 100,
};
my $uzulla = {
    name        => 'uzulla',
    affiliation => 'hachioji.pm',
    perl        => 1,
    python      => 0.01,
    ruby        => 0.5,
    php         => 4,
    binary      => 0.01,
};

my @all=($papix,$boolfool,$moznion,$binarian,$uzulla);
my @words=qw/perl python ruby php binary/;

for my $darekasan(@all) {
  print "name<",$darekasan->{name},">\n";	
  for my $word(@words){
  	print $word,":";
    if ($darekasan->{$word}>=0 && $darekasan->{$word}<=19){
    	  print "\n";
    	}elsif($darekasan->{$word}>=20 && $darekasan->{$word}<=39){
          print "★\n";
        }elsif($darekasan->{$word}>=40 && $darekasan->{$word}<=59){
          print "★★\n";
        }elsif($darekasan->{$word}>=60 && $darekasan->{$word}<=79){
          print "★★★\n";
        }elsif($darekasan->{$word}>=80 && $darekasan->{$word}<=99){
          print "★★★★\n";
        }elsif($darekasan->{$word}==100){
          print "★★★★★★\n";
    }
  }
}

2-5. JSON風Dumper (オプション)
どのような方法でもよいので、score.plで用意されたデータを, 次のようなフォーマットで出力するようにしてみてください.

出力例
[
{
"python":10
"binary":0
"name":"boolfool"
"ruby":20
"perl":40
"php":0
"affiliation":"namba.pm"
"sum":70
},
{
"python":50
"binary":0
"name":"papix"
"ruby":50
"perl":60
"php":0
"affiliation":"namba.pm"
"sum":160
}
]

my $papix = {
    name        => 'papix',
    affiliation => 'namba.pm',
    perl        => 60,
    python      => 50,
    ruby        => 50,
    php         => 80,
    binary      => 30,
};
my $boolfool = {
    name        => 'boolfool',
    affiliation => 'namba.pm',
    perl        => 40,
    python      => 10,
    ruby        => 20,
    php         => 30,
    binary      => 10,
};
my $moznion = {
    name        => 'moznion',
    affiliation => 'hachioji.pm',
    perl        => 100,
    python      => 70,
    ruby        => 80,
    php         => 50,
    binary      => 50,
};
my $binarian = {
    name        => 'binarian',
    affiliation => 'hachioji.pm',
    perl        => 10,
    python      => 11,
    ruby        => 1,
    php         => 100,
    binary      => 100,
};
my $uzulla = {
    name        => 'uzulla',
    affiliation => 'hachioji.pm',
    perl        => 1,
    python      => 0.01,
    ruby        => 0.5,
    php         => 4,
    binary      => 0.01,
};

my @all=($papix,$boolfool,$moznion,$binarian,$uzulla);
my @words=qw/name affiliation perl python ruby php binary/;

for my $darekasan(@all) {
  print "{\n";	
  for my $word(@words){
  	if ($word eq "name" || $word eq "affiliation"){
   	  print "\"",$word,"\"",":","\"",$darekasan->{$word},"\"","\n";
 	}else{
  	  print "\"",$word,"\"",":",$darekasan->{$word},"\n";
  	}
  }
  print "}\n";	
}