Tuesday, August 10, 2010

learning perl exercise 6 hash

6-1:ユーザが入力した名前の姓を表示。
(氏名からすべて入力してもらうということかもしれないが、とりあえず。)

use 5.010;

my %family_name = (
"fred" => "flintstone" ,
"barney" => "rubble" ,
"wilma" => "flintstone",
);

print "Hey, type one of the names below and you know the person\'s last name.\n";

my @first_name = keys %family_name;
print "@first_name\n";

chomp ($person = <STDIN>);

print  "family name of $person is $family_name{$person}.\n";



6-2:名前のリストを読み込んで誰が何回登場したか表示。
use 5.010;

open IN, "namelist";

while ( $name = <IN> ) {
 chomp $name;
 if ( exists $namelist{$name} ) {
   $namelist{$name} = $namelist{$name} + 1;
  }
  else {
   $namelist{$name} = 1;
   }
 }
 
foreach $person (sort keys %namelist ) {
print "$person appeared $namelist{$person} time(s) in the list.\n";
}
動いた。

6-3
ENVのすべてのキーとその値を表示する。列幅は最大文字数のキーと値を取得してあわせる。
#! /usr/bin/perl
use 5.010;

my @key = sort keys %ENV;
my @value = values %ENV;

my $digit_key = &max_digit(@key);
my $digit_val = &max_digit(@value);

my $max_digit = 0;

sub max_digit {#最大値を返すサブルーチン
 foreach (@_){
  my $scales = length($_);
  if ($scales > $max_digit) {
   $max_digit = $scales;
  }
 }
 $max_digit;
}

$format = "%${digit_key}s %${digit_val}s\n";

while ( ($key, $value) = each %ENV) {#ハッシュENVのキーと値を最後の行まで代入
 printf $format, $key , $value;
}

回答例を見てあまりの短さに驚いた。

答え合わせ

6-3:
回答例はkeys %ENVの最大文字数だけを取得。

foreachで%ENVのkeyを取得すると同時にifで最大文字数を取得。
foreach my $key ( keys %ENV ) { 
 my $key_length = length( $key ) ;#$keyの文字数を代入。
 $longest = $key if $key_length > $longest;# ifのカッコなし
}


memo


ハッシュはキーと値がペアになった配列。データベース的な配列?
fredのfamily_nameはadamsの場合
$family_name{fred} = "adams";

複数同時に代入する場合(=>(fat array)を使う場合)
my %family_name (
"fred" => "adamas",
"mike" => "mann",
"alex" => "rodrigues",
"jimmy" => "carter",
);
行末がコンマ。

図書館の貸し出しカード。誰(key)が何冊(value)借りているか?

同じ名前が何回出てきたか?(名前をkey、回数をvalue)

初出の場合はundefを返すのでその分岐を利用するケース。

ハッシュの全処理にはeach %hash
while ( ($key, $value) = each %ENV) {#ハッシュENVのキーと値を最後の行まで代入
 printf $format, $key , $value; #あらかじめ決めたformatでキーと値を出力する例。
}

Saturday, August 7, 2010

learning perl exercise 5

5-3
#! /usr/bin/perl
use 5.010;

print "how many digits?\n";
chomp ($digit = <STDIN>);
print "input a word\n";
chomp ($word = <STDIN>);

if ( $digit > 10) {
 $digit_of_scale = ($digit/100)*12 ;
 #10桁目以上なら1.2倍の桁数があればいいかなと。。。wrong!
 #と、思っていたけど小数点以下が切り捨てられるので16以下だとnot working...
 print "$digit_of_scale\n"; #check用
 }
 elsif ($digit < 10 && $digit > 0) {
 $digit_of_scale = 1;
 #10以下であれば10桁で足りる。文字数が10越えたら桁数たりない。
 }
 else {
 die "oops that was not a valid number.";
}
say "1234567890" x $digit_of_scale;
printf "%${digit}s\n", "$word"; # スカラー名を{}で囲む

回答例を見る前に作ろうと思ってできなかったものを回答例を見て整えた。回答例は複数の文字を受け付ける仕様になっていた。

できていなかった部分
・chompを忘れていた。
・&ではなくて&&。
・printfでフォーマット付き出力時で桁数をスカラーで代入する時は$のあとのスカラー名を{}で囲む。

これだと文字数が10文字以上の長い単語だと桁数が足りなくなる。


やり方の例検証

5-2
キーボード入力を配列に代入。foreachですべての要素を20桁フォーマットでprintf。

またはforeachを使わない方法
my $format = "%20s\n" x @lines; #フォーマットは20行の文字x要素数
printf $format, @lines; #そのフォーマットに要素を代入してprint

5-3
桁数を自動で増やす部分。
自分で思いついたのは1.2倍というアイデアだったが、小数点以下が切り捨てられるので16以下だと1(つまり10桁)足りない!

必要とされる数字は入力された桁数の10の位に1足したもの。つまり9足して10で割る、ということになるのか。これだと10以下をわざわざ1に固定する必要もない。なるほど。
print "1234567890" x (($digit+9)/10), "\n";

ファイルハンドルとか初めて聞く言葉が増えてきた。
ペースも落ちて落ちこぼれ気味。。。

間隔開けないように続けていくことが大事。

ファイルハンドル

Perlプロセスと外部の世界の間のI/O(インプットアウトプット)の結びつきに対してPerlプログラムがつけた名前のこと。ファイル名ではない。大文字推奨。

Sunday, August 1, 2010

learning perl exercise 4

4-1
use 5.010;

my @fred = qw{ 1 3 5 7 9 };
my $fred_total = total(@fred);
print "The total of \@fred is $fred_total.\n";
my $user_total = total( <STDIN> );
print "The total of those numbers is $user_total.\n";

sub total {
 $sum = 0;

 foreach ( @_ ) {
  $sum += $_ ;
  }
 $sum;
}
ほぼ教科書通りになっていた。

4-3
use 5.010;

my @fred = above_average(1..10);
print "\@fred is @fred\n";
print "(Should be 6 7 8 9 10)\n";
my @barney = above_average(100, 1..10);
print "\@barney is @barney\n";
print "(Should be just 100)\n";

sub above_average {
 my $sum = 0;
 @abo_av = undef; 

 foreach ( @_ ) {
  $sum += $_ ;
  }
  
 $av = $sum/($#_+1) ;
 
 foreach ( @_ ) {
 if ( $_ > $av ) {
  push @abo_av, $_ ; 
  }
 }
 @abo_av ;
}
本では平均をとるサブルーチンを別に作っていた。


4-5
#use strictだと$nと$already..関連でエラーがでる。
use 5.010;

greet( "Fred" );
greet( "Barney" );
greet( "Mike" );
greet( "Jessica" );

sub greet {
 state $n = 0;
 $n += 1;
 push @already_there, @_;
  
 if ($n == 1) { #イコールは=が2つ。
  print "Hi @_! you are the first one here!\n";
  } 
  else {
   pop @already_there;
   print "Hi @_! I have seen: @already_there.\n";
   push @already_there, @_;
   }
}
自分バージョンはカウンタを使って1回目のみifで切り分け。
2回目以降は、最後に来た人を「すべての人間リスト」からpop>print>pushで戻す、というアクション。頭に浮かんだ流れを素直に作ったらこういう感じになったが、これってどうなのだろうか。

教科書の方は、最初に残ってる人間用の配列を宣言。その配列が最初は空であることを利用して切り分けて、elseで1番最初のアクション。

ifelseを一度抜ける度に残っている人間リストに今来た人の名前をpushしていく。

普通の思考と逆にプログラムされていく印象。これがらしい書き方なのだろうか?

#use strict だとできない。
use 5.010;

greet( "Fred" );
greet( "Barney" );
greet( "Mike" );
greet( "Jessica" );

sub greet {
 state @already_there ; #次の回でも値を保持するためにプライベート変数宣言

 my $name = shift; #最初のひとつを代入

 if ( @already_there ) { #すでにいるヒトリストが空かどうかの判定で切り分け
  print "Hi $name! I have seen: @already_there.\n"; 
  #Hi $nameは重複しているので本当は上に出してひとにつできる。
  } 
  else {
   print "Hi $name! you are the first one here!\n";
   }
  push @already_there, $name; 
  #ifelse後に今来たヒトの名前をすでにいるリストに追加してもどる
}

以上、本のやり方で自分の?を作り直した改正版。

my $name = shift;
というのは簡潔で気に入った。

サイトによっては@_や$_は使わずその都度名前をつけるべき、とかいてあるがどうなんだろう。素直に考えればその都度特有の名前に変えた方が後から見た時わかりやすいと思う。

わかっていないポイント
  • use strictでもエラーが出ないプライベート変数の使い方
  • sub routineに渡る値の入っている場所、入れ方。


    「初めてのPerl」を読み進めています。

    初めてのPerl 第5版
    初めてのPerl 第5版
    posted with amazlet at 10.07.21
    Randal L. Schwartz Tom Phoenix brian d foy
    オライリージャパン
    売り上げランキング: 23482