Friday, November 12, 2010

Perlがカラシニコフで、RubyがM-16

見ての通り「初めてのPerl」もそこそこにRubyを勉強し始めた。いろいろ読んで回った結果、ウェブサービスに向いている気がしてRubyをがんばってみようかという気になったけれど、どうも難しい。「初めてのPerl」の途中までしか知らない自分が言うのもなんだけれど、Perlのほうが簡単に感じてしまう。使うヒトに喜びをもたらすというRubyを実感するにはたぶんまだ初心者すぎるのか。

どういうところが難しいのか?

1. メソッドがたくさんあってそれを覚える必要がある。
どこのサイトや本を読んでも、そこらじゅう便利そうなメソッドだらけで読んでいる時は感心するのだけれど、覚えるまでが大変。Perlの場合は(「初めてのPerl」の場合かもしれないけど)手間はかかるけれど基本的なツールだけでなんとかなるようなシンプルさがあるような気がする。

例えば配列の要素を足す
(1..10).to_a.inject(0){|t, a| t + a}
にしてもこれが書けたらいいなとは思ってもinjectを知っていないと出てこない。これがPerlだと「よし、順番に読み込んで足していけばいい」という風にとりあえずはやり方の予測がつく(まだ全然すんなりとはできないけど)。つまり高級な言語なので高級な使い方をしたいけど、ひとつひとつのツールが最初はゴージャスすぎて戸惑う感じ。

2. オブジェクトの種類がいっぱいあってそれを覚える必要がある。
これは間違ってるかもしれないけれど、変数のカタチが自由な分だけto_aとかto_iが必要になってきて簡単にコードを書こうと思ってもつまづくポイントが多い。さっきのコードも(1..10)の後にto_aが必要なければいいのに(言っても仕方ないけど)って思う。

積み木で遊ぶ感じがPerlで、接着剤使ったプラモデル作るのがRubyな感じ。
「Perlがカラシニコフで、RubyがM-16」のような感じ。
とりあえずまだしばらくゴルゴを目指す。

とんちんかんなコト言ってるとは思いますが、後で見て笑える日が来るようがんばります。

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
  • Sunday, July 25, 2010

    learning perl exercise 3

    ex-3-1
    #! /usr/bin/perl
    
    @mylist = <STDIN>;
    @mylist = reverse @mylist;
    print "@mylist" ;
    

    chomp(@mylist = <STDIN> ;)
    としたら配列要素がつながって読みづらくなった。

    最後の出力の後にも改行を入れようと思い、最初
    print @mylist . "\n";
    とやったらできなかった。

    print "@mylist" . "\n";
    だとできた。

    UNIXでの標準入力の終わり「Ctrl + D」は1つの入力が終わった状態で打つ。

    ex-3-2
    #! /usr/bin/perl
    
    @names = qw / fred betty barney dino wilma pebbles bamm-bamm /;
    $n = @names - 1;
    $message = "hit a number between 1 to $n";
    print "$message" . ".\n";
    chomp ($num = <STDIN>);
    
    while (($num < 1) or ($num > $n)) {
     print "$message" . ", PLEASE.\n";
     chomp ($num = <STDIN>);
    }
    
     $num = $num - 1;
     print "$names[$num]" . "\n";
    
    

    0の入力の除外と要素数以上の数の入力があった場合のメッセージを入れようとしたらよくわからなくなった。でもこれだと文字を入力されたらダメだよなあ。とりあえず今は「1から$n以外の数字だったら」という条件の作り方がまだわからない。

    ex-3-3
    #! /usr/bin/perl
    
    print "enter names you like. press Ctrl+D when you finish.\n";
    @namelist = <STDIN>;
    
    print "chomp? enter Y/N.\n";
    chomp ($chomp = stdin);
    
    if ($chomp eq Y) {
     foreach $namelist (@namelist) {
     chomp $namelist;
     }
     @namelist = sort @namelist; 
     print "@namelist" . "\n" ;
    }
    
    elsif ($chomp eq N) {
     @namelist = sort @namelist; 
     print "@namelist" . "\n" ;
    }
    

    これもY/N以外の入力に対して無力。Nの場合はY以外のどのキーでもOKのようにすればごまかせるのかな。あとchompしたほうの表示は全要素の前にスペースが入る。

    【答えの一例を見ながら】

    3-1
    print reverse <STDIN>;
    てやってもいいんですね。

    3-2は、複数の数字を受け入れる想定だったのか。

    3-2
    print sort <STDIN>;
    もOK。

    【もう一度ex-3-2】

    #! /usr/bin/perl
    
    @names = qw / fred betty barney dino wilma pebbles bamm-bamm /;
    $n = @names - 1;
    print "hit some numbers between 1 to $n and ctrl+d when you finish.\n";
    chomp (@nums = STDIN);
    
    foreach $nums (@nums) {
    print "$names[$nums - 1] \n";
    }
    

    教科書通りにやると最後のforeachの後の$numsを省略して{}の中では$_を使うことができる。
    foreach (@nums) {
    print "$names[$_ - 1] \n";
    }
    

    @numにきちんと指定範囲内の数が入力されたかどうかの判定を入れておかないとどうも落ち着かない。配列の中の文字判定はどうやってやるのだろうか。たぶん入力の段階で割り込んでやる感じなのだろうか。

    foreachは「配列の要素をひとつずつスカラー変数に代入して、要素の最後まで{}内の作業をやりつづける」という理解。変数名を指定しない場合はデフォルトの$_に代入される。

    スカラーは$calar。
    配列は@rray。


    【ex-3-3 again】
    いろいろ変だったのでもう一度。

    print "enter names you like. press Ctrl+D when you finish.\n";
    @namelist = <STDIN>;
    
    print "chomp? enter Y/N.\n";
    chomp ($chomp = <STDIN>);
    
    if ($chomp eq Y) {
     chomp @namelist;
     print sort @namelist ;
     print "\n";
    }
    
    elsif ($chomp eq N) {
     print sort @namelist ;
    }
    

    配列のchompは chomp @配列名 でできる。

    演算子はクオーテーションの中では実行されずに文字として表示されるので
    print "sort @list" . "\n";
    
    はできない。

    結果をchompするかの切り分けを最初に持ってくると print sort STDIN が使えてelseのところが少しすっきりしてちょっといい気分。
    #! /usr/bin/perl
    
    print "press Y if you like to chomp the result. if not, press any other key.\n";
    chomp ($chomp = <STDIN>);
    
    print "enter names you like. press Ctrl+D when you finish.\n";
    
    if ($chomp eq Y) {
     chomp(@names = <STDIN>);
     print sort @names;
     print "\n";
    }
    
    else {
     print sort <STDIN>;
    }
    
    

    Wednesday, July 21, 2010

    learning perl exercise 2

    「初めてのPerl」を買いました。


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


    練習問題2.11

    2.11-3
    #! /usr/bin/perl
    print "半径の長さを入力してください。\n";
    chomp  ($hankei = <STDIN>);
    if ($hankei > 0) {
    $enshu = ($hankei * 2) * 3.141592654;
    print "半径" . $hankei . "cmの円周の長さは、" . $enshu . "です。\n";
    }
    else {
    $hankei = 0;
    print "あなたが入力した半径は" . $hankei . "cmになりました。\n";
    }
    

    2.11-4
    #! /usr/bin/perl
    print "まず文字を入力。\n";
     $moji = <STDIN>;
    print "何回繰り返す?\n";
     chomp  ($kaisu = <STDIN>);
    print $moji x $kaisu;
    


    2.11
  • $piで先に円周率を代入しておく方法。




  • 特別な理由がない場合は入力値はchompしておく。





  • 文字列演算子はピリオド。前読んだ本ではコンマだったような気もする。

    single-quoted string literalとdouble-quoted string literalの違いが理解できていない。backslash escapeで特殊文字?の入力ができるのがdouble-quotedで、さらに変数転換(variable interpolation)されるのもdouble-quoted。single-quotedはどういう時に使うのか?

    Saturday, July 17, 2010

    perlのパスが通るまで vol.3

    「パスを通す」というのは「コマンドサーチパスを設定する」ということらしい。思っていたのと少し違っていた。

    「コマンドサーチパスを設定する」ということは、シェルコマンドのように実行されるプログラム(コマンド?)がある場所(パス)をシェルにあらかじめ設定しおくということ。

    Terminal.appでコマンドを入力する時、それがあらかじめシェルに組み込まれたコマンドであれば実行されるが、自分で作ったプログラムの場合そのプログラムへのパスを登録しておかないと、プログラムの場所がわからず実行されないということでいいのか。それと権限。

    Command not foundの状態。権限がなければPermission deniedの状態。

    bashで設定するファイルはルートの?bash_profileとbashrcというファイル。



    参照:UNIXの基本コマンド

    (次回いよいよ設定)

    それにしても、シェルはアプリケーションなのか?じゃあコマンドサーチパスの設定はシェルに対してやっているのか?とかそういう基本的な要素のカテゴリ分けがよくわかっていない状態で説明しようとしている時点でいろいろとおかしくなる。あとで見直して笑える日が来ることを願おう。

    Wednesday, July 14, 2010

    perlのパスが通るまで vol.2

    perlまでのパスを調べる。

    % which perl
    

    /usr/bin/perl

    と出たのでhello.plの1行目に入れる。

    #!/usr/bin/perl
     print "こんちは\n";
    

    hello.plに権限を与える

    chmod 755 heollo.pl
    

    % ./hello.pl
    

    ./
    は「現在のディレクトリの」という意味。

    パスが通って「こんにちは」ができた。

    以前できなかった理由は、ディレクトリを移動してファイルに権限を与える作業のどこかで手順を間違えていたことだと思われる。

    ディレクトリ内のファイルやフォルダの一覧とその権限を見る。

    ls -l パス/ディレクトリ
    


    参照:
    Hello, world!

    Friday, July 9, 2010

    perlのパスが通るまで vol.1

    ディレクトリを移動して「perl ファイル名」だとできるが

    #!/usr/bin/perl
     
    

    のようにファイルの中にパスを通す?方法がよくわからない。
    通るようになるまでシリーズで。


    自分の環境:Mac OSX 10.6.4

    Terminal起動

    pwd
    print working directory 現在のディレクトリを表示。

    cd パス
    change directory

    cd ..
    ..は一つ上の階層。つまりひとつ上の階層に移動する。
    ..の前後にはスペースが入っている。

    cd
    cdのみだとホームディレクトリに移動

    ls
    list segments(ディレクトリの中身を表示)

    chmod
    change mode(パーミッションなどのファイルモードを変更する)

    mkdir 名前
    make directory

    mv
    move 移動と名前の変更

    man mv(mvのmanual表示)

    NAME
    mv -- move files

    SYNOPSIS
    mv [-f | -i | -n] [-v] source target
    mv [-f | -i | -n] [-v] source ... directory

    manで表示させたマニュアルから抜け出す方法がわからない。

    manでマニュアル表示させた後にそこから抜けるには

    % q

    コマンドの前には%を入れるとこれはコマンドですよ、ということになるのか。

    参照サイト
    wikipedkia/pwd
    Terminalを使ってみよう

    Thursday, July 8, 2010

    コードのフォーマット

    #!/usr/bin/perl
     print "こんちは\n";
     
    

    参考にさせてもらったのはBlogger でソースコードに色付けをする - google-code-prettify

    google-code-prettifyでダウンロードしたcssとjsをほとんど使っていないレンタルサーバーのサブドメインにアップして使用。

    Wednesday, July 7, 2010

    hello world!

    プログラムというものに興味を持ち始めた。とりあえず高校時代に友人たちとよくやったUNOのようなカードゲームを作ってみたいと思っています。

    初めてのプログラムはperlが簡単だと聞いたので、ここにはperlで学んだことなどを忘れないように書きためていこうと思っている。