Top > MyPage
 

Cygwinとruby、そして少々オブジェクト指向

rubyが使えるまで(for 渡辺)※敬称略

cygwin(windows環境でLinuxを使える)

まずはインストール

  • 下のURLからsetup.exeを取ってくる。「Install or update now!」というようなボタンがあるので、これをクリック。

<URL:http://www.cygwin.com/>

  • すぐにダウンロードが終わるので、そしたらそれを実行します。
    • 実行すると「次へ」ボタンを押し、次の「install from internet」にデフォルトになっているので、そのまま、また「次へ」ボタンを押します。
    • インストール場所を指定する画面が出てきます。通常はc:\cygwinにりますが、このままで大丈夫。
    • そのままさらに「次へ」ボタンを押すと、local package directoryを指定するところがありますが、これはテンポラリに使うところで、多分、デフォルトはsetup.exeを実行したディレクトリになっていると思う。別の場所の方がよかったらそれを指定して、「次へ」ボタンを押します。
    • その次はインターネットに接続する方法ですが、direct connectionかIE5 settingで大丈夫だと思う。またまた「次へ」ボタンを押す(proxyなどを設定している場合は、その通りに設定する必要があるかも)。
    • 次はダウンロード先の指定ですが、日本の「ring.exp.fujixerox.co.jp」が比較的速いので、これを選んで「次へ」ボタンを押す。
    • そしたらどのソフトをダウンロードするかを選ぶ画面ですが、一番上のAllの横のdefaultをクリックしてinstallにしたら、「次へ」ボタンを押す(多少レスポンスが遅くて,クリックしても一瞬反応がないように思えるが,ほんの1秒ほど待つとdefault->installに変わります,確か)。
    • そうするとダウンロードが始まり、そこそこ時間がかかります、,ダウンロードが終わったらインストール(ファイルの展開)が始まって、これで基本的にはほぼ終了。
    • 最後にアイコンを作るかどうか聞いてくるので、作っておけばそれをクリックして、linux環境が味わえます。

次は少々環境設定(と思ったけど、デフォルトでいっちまおう!)

linuxではユーザという概念があり、そのユーザーごとにホームディレクトリというのが作られますが、cygwinの根本はwindowsなのでその概念自体がありません。この辺のところはwindows2000の場合は多少色々できたりするらしいが---少々挑戦して、再インストールした経緯がある---その辺を解説していると長くなるので、今回はデフォルトでいきましょう。

ただ、cygwinのデフォルトではwindows2000でログインする時の名前、僕などは面倒なので、Administratorなので、cygwinにおけるユーザーはAdministrator,ホームディレクトリはc:\cygwin\home\Administratorになる(はずです)。従って通常はこのホームディレクトリー以下で作業することになります(もちろん、それ以外の場所にもアクセスできるのですが)。

cygwinの使い方のさわり

  • 先ほどつくられたアイコンの実体は c:\cygwin\cygwin.bat で、中身は以下のようになっています。
    @echo off
    
    C:
    chdir C:\cygwin\bin
    
    bash --login -i
    

これからもわかるように、cygwin.batを実行すると、bashというshellが立ち上がります。

  • 今ではlinuxもx-windowsなどを使って、だいぶグラフィカルになりましたが、そしてcygwinでもx-windowsを立ち上げること自体は可能なのですが、設定も面倒ですし(特に日本語化が使えるようにするのは)、別にwindowsがあるので基本的にはこのbashを使って作業をするのが一番(だと思います)。
  • bashは昔のDosの発展バージョンという感じで、強力なスクリプトなども組める(これもまたDosのバッチのすごいやつという感じ。もっとも、本来は逆で、DosがUnixのシェルの簡易版を作ったというのが本当のところだと思う)。ちょっぴりDosとコマンド名が違ったりするので、勉強する必要がありますが、windowsのエクスプローラなどでも作業ができるので、あまり覚えなくても大丈夫。
  • editorなどもcygwinをインストールすると付いてきますが、日本語入力ができないなど面倒なので、例えばhidemaruなどを使うのが一番。僕の場合はlinuxで有名なEmacsのwindows版で、日本語化もされているMeadowというの使っていますが、これはまたそのうち紹介します。
  • ディレクトリーですが c:\cygwin 以下しか使わない場合は c:\cygwin がbashにとっては「/」・・・つまりrootディレクトリ(linuxではドライブ、つまり c:\ という概念がなく、常に「/」からディレクトリが始まる)。したがってAdministratorのホームディレクトリは /home/Administrator になります。また、 c:\cygwin とは別のディレクトリーの場合は c:\winnt/cygdrive/c/winnt という感じで /cygdrive/c/c:\ の代わりになります。
  • bashのコマンドのいくつか紹介。
    • cd は同じ。しがたってホームディレクトリの下に work などというディレクトリをつくって、そこにカレントディレクトリを移動する時には cd /home/Administrator/workcd ./workcd work でいけます。「.」はカレントディレクトリを意味しています。またただ cd と引数なしに実行すると自分のホームディレクトリに行けます。さらに「~(チルダ) 」は自分のホームディレクトリを意味します。つまり cd ~cd とは同じ意味。
    • cp がコピーコマンド。
      • cp sakai.txt chikkun.txt はsakai.txtをchikkun.txtとしてコピー。
      • cp sakai.txt ./work はsakai.txtをサブディレクトリのworkに同名のままコピー。
      • cp -rf ./work/* ./temp/ はサブディレクトリのwork以下のファイル(もしディレクトリがあったらその下のファイルなども全部・・・オプションの-r)をサブディレクトリのtempに「上書きするかの問い合わせを表示させず(オプションの-f)」コピー。
    • mv はcpと基本的に同じ(移動するという意味では違うけど使い方は同じ)。
    • rm はファイルの削除。 rm sakai.txt はsakai.txtを削除。 rm -rf ./work は危険だけれど(これで僕は何度か泣いた!!)、サブディレクトリのworkを再帰的に(つまりさらにそのサブディレクトリなども全部)強制的に削除。
    • tar はlinuxでよく使う圧縮ファイルを展開するコマンド(圧縮もできるが)。通常は拡張子がsakai.tar.gzの.tar.gzになっている。 tar xvzf sakai.tar.gz とすれば展開されます。最近は時々、sakai.tar.bz2などbz2という拡張子のものがあるけれど、その場合は tar xvjf sakai.tar.bz2 で展開されます。
    • unzip はwindowsでも使うzipファイルの展開コマンド。 unzip -d sakai.zip でOK。
    • まだまだあるし使い方を,いっぺんには覚えられませんが、 cp --help の--helpでたいてい使い方が表示されますし、 man cp でcpコマンドの詳しい使い方なども出ます(英語だけど)。

さてさてruby

実際にはrubyがcygwinにインストールすると,すでにインストールさています。が,しかし,現在のrubyのバージョンは1.8なんですが,これはまだ出たばっかりで,まだまだ色々なライブラリなどが、この1.8にバージョンアップされていないようです。したがって,rubyのバージョンを1.6系にするために、このもともとインストールされたrubyを削除します。先のbashで

cd /bin
rm ./ruby*

これでとりあえずrubyは削除されます。その次に,ついこの間までのstableなバージョンは1.6なんですが,これをダウンロードし展開します。以下のURLにある。ファイル名は

ruby-1.6.8-20030727-i386-cygwin.tar.gz

<URL:http://ftp.ruby-lang.org/pub/ruby/binaries/cygwin/1.6/>

これをc:\cygwinにコピーして

tar xvzf ruby-1.6.8-20030727-i386-cygwin.tar.gz

ruby -v
ruby 1.6.8 (2003-03-26) [i386-cygwin]

となれば,これでとりあえずOK。

結構便利なツール

rubyの基本はbashやdosプロンプトで

ruby hoge.rb

などとやるのが基本なわけですが,いちいちスクリプトファイルを作成して,dosプロンプトを開いて,そこにcdして,実行するのはめんどくさい。特に,ちょっとしたテストをするのには。そこで,rubyの「Rubyの開発環境」のRDEなるものがあります。ちょっとでかいけど,下にその画面を示します。

これだと,ソースのif文やコメントなど色づけしてくれるので,見やすくて,ワンキーで実行してくれて,一番下のコンソールにその結果を示してくれます。

RDEのイメージ画像

これは下のURLからダウンロードして,確か,解凍して,patchがあるので,それを上書きでまたもや解凍すると使えます。結構便利なので,是非ご利用を。

<URL:http://homepage2.nifty.com/sakazuki/rde.html>

rubyの設定

rubyは色々ライブラリをすでに持っているが(標準ライブラリ),少々外部のライブラリを使うとコーディングが楽になることがあります。 RAAというのがそれで,それらのライブラリが色々そろえてあります。以下がそのURL。

<URL:http://raa.ruby-lang.org/>

ここを色々探検するのも野面白い。

今回は,とりあえずCSVというCSVファイル(あのデータベースの「,」で区切られたもの)を扱うものと,そのcsvのライブラリで使われるstrscanというライブラリをインストールしてみよう(実は最終的にexcelのデータから,メールアドレスを読み取って,その人達にメールを送ることを想定しているので,こうなるのです)。まずは以下からstrscanというものをダウンロードします(何をするものかは <URL:http://i.loveruby.net/en/man/strscan/doc.html> に書いてあります---僕にはわかっていない(--;)。

<URL:http://raa.ruby-lang.org/list.rhtml?name=strscan>

そんで,ダウンロードしたのを/home/Administratorの下の例えばsourceディレクトリにコピーします。

cd ~
mkdir source
cp /cygdrive/c/ダウンロードした場所/strscan-0.6.7.tar.gz ~/source/
cd ./source
tar xvzf strscan-0.6.7.tar.gz
cd ./strscan-0.6.7

インストールは(rubyはたいていこれでライブラリーをインストールできる)いたって簡単。

ruby install.rb config
ruby install.rb setup
ruby install.rb install

これでインストール終了。

次にcsv.rb。以下からダウンロード。

<URL:http://raa.ruby-lang.org/list.rhtml?name=csv>

strscan同様に

cd ~
mkdir source
cp /cygdrive/c/ダウンロードした場所/csv-1_2_2.tar.gz ~/source/
cd ./source
tar xvzf csv-1_2_2.tar.gz
cd ./csv-1_2_2

今回は

ruby install.rb

で終了。

簡単なrubyのスクリプト

エディタで次のようなソースを書いてみる(study1.rb・・・通常拡張子はrbに する---単なる習慣だけど)。

puts "Hello World!"

これで

bashのコンソールで,

ruby study1.rb


Hello World!

と表示されたら無事動いたことになります。

ちょっぴり複雑なスクリプト

rubyで良く使う定番のフレーズを列挙すると...

  • 変数
  • 配列
  • 連想配列
  • print 文
  • puts文
  • each文(これがruby特有なので,少々最初は取っつきにくいかも?)
  • while文
  • for文
  • if文

右クリックで「新しいウィンドウで開く」で,ソースを見ながら読んでください。

  • さて,まずは配列ですが,Array.newで配列オブジェクトを作っているのですが,3行目のような形だったらこの「new」をする必要がありありません(ちなみに#はコメントです)。僕の場合は今javaをやっているので,この方がやりやすいのですが,rubyやphpやperlでは変数は相当いい加減に使えます。また,perlやphpでは変数には$data1みたいに「$」をつける必要があるのですが,rubyではその必要がありません。
  • 8行目からが,eachでこれがruby特有の構文で,イテレータといわれています。実際には色々難しいことはあるのですが,(僕が)よく使っているのは配列に対して,
    data1.each {|a|
                puts a
    }
    i=0
    

これで,aという変数にdata1にある配列の値の方が代入されて,しかも,data1の データがある限り,繰り返されます。今回の例だと,data1[0]=0で data1[1]=1,data1[2]=2というように添え字0に対して,値の0が入っているので (添え字1に対しては1が入っている),上記の場合はdata1に入っている値の 0,1,2が表示されます。

  • phpやperlや,ひいてはC言語では(ソースでは9行目です)for文は

    for(i=0;i<something;i++){
       .....
    }
    

    のような構文が普通ですが,rubyにはこれがありません(何故か)。for文自体はあるのですが,

    for i in data1#
       puts i
     end
    

というような,data1という配列の値をiに入れて処理する方法が上記のソースです。

  • 次に連想配列というのがperl(たぶん)から取り入れられて,結構便利な配列で,ソースの23行目あたりからですが,配列の0,1,2,3,4,のような添え字ではなく,添え字自体が文字列になっているというものです。普通配列はa[0]="sakai",a[1]="tomoko"などというように,添え字([]の中)が数字なのですが,連想配列の場合は添え字自体が文字列でa["sakai"]="kazuro",やa["watanabe"]="ken_ichiro"というようにになります。ただ,そのぶん添え字を重複するとことができないので,watanabeという名字は,今回の例で言えば,1人しか許容できません。また,eachのあとの|a,b|でaが添え字,bが値をaやbという変数で取得できます!また配列の場合と同様,25行目のようにすると,Hash.newする必要はありません。
    data2.each {|a, b|
      print a," ",b,"\n"
    }
    
  • 最後にif文です。これもプログラムでは定番ですね。
    if(a == "watanabe")
       print a," ",b,"\n"
    end
    

Cやphpやperlなどでは

if(a == "watanabe"){
   print a," ",b,"\n"
}

という書式なのですが,rubyの場合はendで囲みます。また,ifの次の()なども 書かないのが一般的です。つまり,

if a == "watanabe"
   print a," ",b,"\n"
end

csvファイル

今回はCSVファイルの中身を取り出して、あるカラムを標準出力する例です(あともうすぐでメールを大量に送れるようになります)。

Excelとその他のソフトなどでデータのやりとりをする際、CSVファイルを介することはご存じだと思います。このCSVファイルというのは単なるコンマで区切られたデータというだけではなく、例えば「"sakai,ha」という1行1カラムのデータをCSVファイルではどのような表現になるかというと(僕はこれをExcelで入力してCSVで保存するを実行して、中身を確認してみました)。

"""sakai,ha"

上記のようになります。これは、データの途中にコンマ(,)があったりすると次のカラムとの境界もコンマなので、困ります。そこでその場合にはダブルクウォーテーションマーク(")をいれます。さらに、今回の例ではデータの最初にそのダブルクウォーテーションマーク自体があるので、それをエスケープするために""のように2つ続けます。

つまり、CSVファイルを扱うのは意外に面倒だと言うことです(以前、メールの中身を1つのCSVの1カラムとしてもらって---それ以外に出した人の名前・メールアドレスなどのデータもあった---それを処理するという仕事をしたことがあるのですが、相当苦労したことがあります)。

そこで登場するのが、ライブラリーです。以前、少し触れましたが、もともとrubyには標準ライブラリというものと拡張ライブラリ、それから各個人が作っている(もちろんすばらしい)ライブラリーなどがあります。今回は自分で、CSVのデータ解析をするのが面倒なので(そんな必要がないほど単純な例になってしまいますが)、CSV.rbというものを使って楽しようと思います。そこでインストールですが、実際はすでにやっています、ここで。

終わっていたら以下のソースをエディタで入力して(例えばcsvtest.rb)、データとしてやはり以下のようなものを用意したら(ファイル名はaddress.csv )、

####ソース####
#!/usr/local/bin/ruby -Ks
require 'csv.rb'
CSV::Reader.parse(File.open('address.csv', 'r') ) do |row|
  print row.to_a[1],"は",row.to_a[0],"を持っているのだ。\n"
end

####CSVファイル####
chikkun@chikkun.com,知久和郎
sakai@syuei.tv,坂井和郎
chikkunk530@docomo.ne.jp,坂井携帯

以下のコマンドを実行すると

ruby csvtest.rb

知久和郎はchikkun@chikkun.comを持っているのだ。
坂井和郎はsakai@syuei.tvを持っているのだ。
坂井携帯はchikkunk530@docomo.ne.jpを持っているのだ。

というように、出力されるはずです。

ではソースは短いですが、説明すると結構長くなりそうです。

ハッシュ・バン

まずは最初の

#!/usr/local/bin/ruby

Linuxでは、本来実行ファイルとそうでないという区別がありません。通常の テキストファイルも実行ファイルにできるわけです。ただしそのためには、そ のファイルに実行属性を付けなくてはなりません。./csvtest.rb

chmod u+x filename

このchmodというのがそのファイル属性を変えるコマンドで、u+xのuはそのファ イルの所有者(この辺も面倒ですが、linuxはファイルやディレクトリに所有者 という考え方があるのですが、今回のcygwinにはありません---実際のところ windows2000の場合はその考え方を導入できるらしいのですが、昔断念しまし た)という意味で、その所有者に対してxが実行権を与えるという意味になりま す。

さて、と説明しながらcygwinではデフォルトですべてのファイルに実行権が与 えられています(先に述べたようにcygwinではユーザという考え方がないので、 すべてのファイルが自分のファイルになっている)例えば、僕のあるディレク トリで、

ls -l

とやったら(lsはDosのdirで、-lは詳細モードという意味で、これがないと単にファイル名だけが表示される)

-rwxrwxrwx 1 Administ なし 2577 Feb 6 17:06 0000-00-00-000000.howm*
drwxrwxrwx+   4 Administ なし            0 Feb  6 16:45 2004/
-rwxrwxrwx    1 Administ なし         3068 Feb  6 16:45 build.ie*
-rwxrwxrwx    1 Administ なし         7103 Feb  6 19:49 build.xml*
-rwxrwxrwx    1 Administ なし         2715 Feb  6 16:45 build.xml.ie*
-rwxrwxrwx    1 Administ なし         2739 Feb  6 16:45 build.xml.juku*
-rwxrwxrwx    1 Administ なし          660 Feb  6 16:45 h2r.rb*
-rwxrwxrwx    1 Administ なし        13098 Feb  6 16:45 howm2*
-rwxrwxrwx    1 Administ なし         2526 Feb  6 16:45 makefile*
-rwxrwxrwx    1 Administ なし          257 Feb  6 16:45 nikki.sh*
-rwxrwxrwx    1 Administ なし         5865 Feb  6 18:44 rd2.rb*
-rwxrwxrwx    1 Administ なし          397 Feb  6 16:45 template.txt*

という感じになっていますが、この

-rwxrwxrwx

が、最初の-を除いた(最初の-はディレクトリの時にはdと書いてあります)3 つがオーナ(自分)、次の3つがグループ、次の3つが他人にたいする属性なの ですが、rが読める、wが書き込める,xが実行できる(ディレクトリに対する場 合は少し意味が違いますが)ということになります。したがって上記を見れば わかるように、オーナはAdministratorでグループはなし、属性は誰でも読め るし、書き込めるし、実行できると言うことになります。これじゃLinuxの場 合は危険きわまりないわけですが(多くの人が1つのlinuxにアクセスする訳な ので、他人のファイルを勝手に書き換えられると言うことだから)、windowsで はOKでしょう。もし、渡辺先生がAdministratorでログインしていなかったら 違う名前になっているかもしれません。

そして

#!/usr/local/bin/ruby -Ks

はこのファイルを実行させる時はrubyが実行され、オプションには-Ks(スクリ プトはシフトJISでおこない)をrubyに対して渡し、ファイルの中身をスクリプ トとして実行するということになります。ちなみに、#! (シャープ・ビックリ) の部分は、「ハッシュ バン」と呼ばれているらしい(もともと#はシェルスク リプト---Dosでいうところのバッチ---のコメント行になる)。

さて、だいぶ長くなりましたが、そんなこんなで

ruby csvtest.rb

という感じでcsvtest.rbというスクリプトを実行させなくても、

./csvtest.rb

で同様なことが実行できるはずです。ちなみに「./」というのは「.」がカレ ントディレクトリで、「/」がディレクトリの区切りなので、「./」でカレン トディレクトリにあるファイルを表しているのですが、通常Linuxではパスに 指定しているディレクトリ以外は、直接指定しない限り実行できないので、こ のように「./csvtest.rb」で直接してしているわけです。

require

これは先に述べたライブラリを読み込むためのメソッドで(オブジェクト指向で はCなどの関数という言い方に対してメソッドというみたい)CSV.rbというの読 み込んでいる。じゃあ、このrequireされるファイルはどこにあるかというと、 次のコマンドを実行するとわかる。

ruby -e 'puts $:'

僕の環境では

/usr/local/lib/ruby/1.6
/usr/local/lib/ruby/site_ruby/1.6
.
/usr/local/lib/ruby/site_ruby/1.6
/usr/local/lib/ruby/site_ruby/1.6/i386-cygwin
/usr/local/lib/ruby/site_ruby
/usr/local/lib/ruby/1.6
/usr/local/lib/ruby/1.6/i386-cygwin

になっていますので、このどこかにないとエラーになります。

静的メソッドの呼び出し

以下はちょっと難しい(理解するのも、実は、僕が説明するのも---よくわかっ ていないのです(--;)

CSV::Reader.parse(File.open('address.csv', 'r') ) do |row|
    print row.to_a[1],"は",row.to_a[0],"を持っているのだ。\n"
  end
  • 上記の「CSV::Reader.parse(File.open('address.csv', 'r') )」はCSVに書いてあるReaderクラスにあるparseというメソッドに「File.open('address.csv', 'r')」が返すオブジェクトを引数に渡している。
  • File.open('address.csv', 'r')のFileは組み込みクラスなので、requireする必要がなく、そのFileクラスの静的メソッドopen()---ファイルをオープンする---を、リードオンリーでaddress.csvを読み込んで、Fileオブジェクトを返値として返しています。
  • 繰り返しですが、Fileオブジェクトをparseメソッドの引数として渡すと、そのファイルの中身を1行ずつ、コンマごとに分解して、今回だったらCellオブジェクトをrowという変数に入れて、そのCellオブジェクトのメソッドto_aでCellオブジェクトにある値を配列にして、さらに[0]や[1]ででその1桁目の(1カラム目の)値を取り出している、というわけです。

これじゃ、よくわかりませんよね。そこで、

オブジェクト指向のほんのさわり

オブジェクト指向というのが前世紀末からJavaなどを中心に、プログラムの世 界では完全なトレンドになりました。このオブジェクト指向というものが、な かなかやっかいで、特に、BasicやCなどを勉強した人にはわかりづらい。僕も 未だによくわかっていないところがあって、説明も不十分でしょうが、rubyで は避けて通れないので(phpやperl、c++などでは)少々ふれます。

もともとのBasicやCなどは手続き型のプログラムと呼ばれていて、プログラム の先頭から順々関数などを実行していって、時にif文などで分岐させたり、 while文などで繰り返させたりするわけですが、この基本が、オブジェクト指 向でまったく無くなっているかというと、もちろん、そういう訳じゃありませ ん。しかし、オブジェクトというものを中心に据えて、プログラムしていこう というスタンスがオブジェクト指向ということになります。

手続き型のプログラムとオブジェクト指向によるプログラム

じゃあ、具体的にはどうなるかというと、

  • オブジェクトには設計図が必要で、Javaやrubyではそれがクラス(class)ということになります。
  • その設計図を具体的なオブジェクトにするには設計図をインスタンスというものに実体化する(rubyやJavaではnewメソッド)。そして、このインスタンスがまさにオブジェクトというものです。
  • じゃあ、このオブジェクトとはいったい何かと言いますと、特性などを表す属性(フィールド)とメソッド(昔で言うところの関数)の両方を持ったものです。

さらに、rubyでより具体的にするために,と次のような簡単なものを考えてみましょう。

  • CSVファイルからデータを読み取る。
  • CSVファイルには名前と年齢と性別が書いてある。
  • その中のデータを読み取って、「こんにちは、僕は坂井和郎です。」と標準出力させる。
  • 標準出力させる際に、「僕は」という1人称が、女なら「私は」になり、男で40才以上の場合は「わしは」でそれ未満の場合は「僕は」と変わる。

実はrubyはオブジェクト指向の言語ですが、実際には手続き型もサポートして います。以下はrubyのホームページからの引用。

Rubyとは
Rubyは、手軽なオブジェクト指向プログラミングを実現するための種々の機
能を持つオブジェクト指向スクリプト言語です。本格的なオブジェクト指向
言語であるSmalltalk、EiffelやC++などでは大げさに思われるような領域で
のオブジェクト指向プログラミングを支援することを目的としています。も
ちろん通常の手続き型のプログラミングも可能です。

Rubyはテキスト処理関係の能力などに優れ、Perlと同じくらい強力です。さ
らにシンプルな文法と、例外処理やイテレータなどの機構によって、より分
かりやすいプログラミングが出来ます。


まあ、簡単にいえばPerlのような手軽さで「楽しく」オブジェクト指向しようという言語です。どうぞ使ってみてください。

Rubyはまつもと ゆきひろが個人で開発しているフリーソフトウェアです。

そこで、先のようなことを手続き型で実行しようとすると

####human.csvの中身####
坂井和郎,30,male
知久友子,44,female
知久和郎,44,male
渡辺健一郎,44,male

####human1.rbの中身####
#!/usr/local/bin/ruby
require 'csv.rb'
CSV::Reader.parse(File.open('human.csv', 'r') ) do |row|
  if(row.to_a[1].to_i >40 && row.to_a[2].to_s == "male")
    print "こんにちは、","わしは",row.to_a[0],"じゃ。\n"
  elsif(row.to_a[1].to_i <=40 && row.to_a[2].to_s == "male")
    print "こんにちは、","僕は",row.to_a[0],"です。\n"
  else
    print "こんにちは、","私は",row.to_a[0],"ですわ。\n"
  end
end

これを実行すると

こんにちは、僕は坂井和郎です。
こんにちは、私は知久友子ですわ。
こんにちは、わしは知久和郎じゃ。
こんにちは、わしは渡辺健一郎じゃ。

となるはずです。

これをオブジェクト指向で考えて、作ろうとすると以下のように考えます。

  1. Humanというクラス(設計図)をつくる。
  2. このHumanには名前と年齢と性別という属性を持っている。
  3. Humanにはaisatuという能力を持っていて、それを実行すると「僕は坂井です。こんにちは」のような標準出力するようにする。
human2.rb
#!/usr/local/bin/ruby
require 'csv.rb'
##まずはクラスという設計図
class Human
  #*propertiesというのは配列propertiesで引数を受け取るという意味
  #initializeというのはオブジェクトを作る時に(通常はnewメソッド)、
  #必ず呼び出されるメソッドで、通常コンストラクタと呼ばれる。
  def initialize(*properties)
    #@nameはインスタンス変数というもので、インスタンスごとに保持される
    @name = properties[0]
    @age = properties[1]
    @sex = properties[2]
  end
  def aisatu
    if(@sex == "female")
      print "こんにちは、","私は",@name,"ですわ。\n"
    elsif(@age > 40)
      print "こんにちは、","わしは",@name,"じゃ。\n"
    elsif(@age <= 40)
      print "こんにちは、","僕は",@name,"です。\n"
    end
  end
end
#ここからmain
#インスタンスを指す変数(メモリー上のどこかにあるオブジェクトの
#居場所を保持している変数---参照変数などという)を保持する配列
hairetu=Array.new
CSV::Reader.parse(File.open('human.csv', 'r')) do |row|
  #Humanというクラスから4人分のオブジェクトを作って(newしてなどという)、先のhairetuにどんどん格納していく。
  hairetu << Human.new(row.to_a[0].to_s,row.to_a[1].to_i,row.to_a[2].to_s)
end
#hairetuに格納されたオブジェクトを取り出して、aisatuさせる。
hairetu.each{|kojin|
  kojin.aisatu
}

そして、これを実行しても以下のように同じに出力されるわけです。

こんにちは、僕は坂井和郎です。
こんにちは、私は知久知子ですわ。
こんにちは、わしは知久和郎じゃ。
こんにちは、わしは渡辺健一郎じゃ。

カプセル化

ここで、手続き型とオブジェクト指向との差は何だろうか?ここがなかなか掴 めない。こういう僕もよくわかってるわけじゃありません。なんせ、すごいプ ログラマーがオブジェクト指向の方が、「いろいろな意味でいいって言って いるから、いいんだろう」ぐらいの知識しか、僕もないわけですが、この1〜2 年、一応オブジェクト指向のプログラム言語をチョロチョロと関わってきたの で、僕なりの解釈を述べます。

まず、最初の手続き型のプログラムですが、あれを関数にまとめることは出来 ます。そうすると、以下のようになると思います。

#!/usr/local/bin/ruby
require 'csv.rb'

def aisatu(name,age,sex)
  if(sex == "female")
    print "こんにちは、","私は",name,"ですわ。\n"
  elsif(age > 40)
    print "こんにちは、","わしは",name,"じゃ。\n"
  elsif(age <= 40)
    print "こんにちは、","僕は",name,"です。\n"
  end
end

CSV::Reader.parse(File.open('human.csv', 'r')) do |row|
  aisatu(row.to_a[0],row.to_a[1].to_i,row.to_a[2].to_s)
end

こうみると、手続き型とオブジェクト指向のものの差がないように思えますが、 もう一度問題の部分を引用すると

  • 手続き型
    • メソッドの定義
      def aisatu(name,age,sex)
        if(sex == "female")
          print "こんにちは、","私は",name,"ですわ。\n"
        elsif(age > 40)
          print "こんにちは、","わしは",name,"じゃ。\n"
        elsif(age <= 40)
          print "こんにちは、","僕は",name,"です。\n"
        end
      end
      
    • 呼び出し側
      aisatu(row.to_a[0],row.to_a[1].to_i,row.to_a[2].to_s)
      
  • オブジェクト指向
    • クラスの定義とその中のメソッドの定義
      #!/usr/local/bin/ruby
      require 'csv.rb'
      class Human
        def initialize(*properties)
          #@nameはインスタンス変数というもので、インスタンスごとに保持される
          @name = properties[0]
          @age = properties[1]
          @sex = properties[2]
        end
        def aisatu
          if(@sex == "female")
            print "こんにちは、","私は",@name,"ですわ。\n"
          elsif(@age > 40)
            print "こんにちは、","わしは",@name,"じゃ。\n"
          elsif(@age <= 40)
            print "こんにちは、","僕は",@name,"です。\n"
          end
        end
      end
      
    • インスタンスを作っている部分
      hairetu << Human.new(row.to_a[0].to_s,row.to_a[1].to_i,row.to_a[2].to_s)
      
    • 呼び出し部分
      kojin.aisatu
      

一見すると、あまり差がないように見えますが、その差を見ていくと、

  • オブジェクト指向の方はクラスという設計図を作っている。
  • そしてその中で、インスタンス変数(@nameなど)に各個人のデータを格納している。
  • さらに、インスタンス(設計図を実体化したもの)をnewして作って、それを配列に格納している。
  • そして、実体化したインスタンスに定義してあるメソッドのaisatuを実行している(kojin.aisatu)。
  • 今回のこの例ではhairetuの中には["坂井和郎","知久友子","知久和郎","渡辺健一郎"]という感じで、人間の属性とメソッドを持ったインスタンスが入っています(実際には、以前も少し触れたように、hairetuという配列にはインスタンスがあるアドレス(居場所)が入っているわけですが)。

かえって、面倒じゃん!!という人も多いようです。特に短いプログラムの場合はかえってコーディングの量が増えたりするのは事実です。しかし、世界中のプログラマがこぞって乗り換えているオブジェクト指向(日本人はむしろ、遅れているようですが)をもう少し前向きに見ています。時々人様の作ったフリーのプログラムを色々変更して自分でも作ろうとしていると、手続き型のプログラムは本当に変更が難しい。それ比べ、クラス設計をしっかりやっていると(クラスはできる限り独立している方が良いとされてい)、その部分を直せば良いというのがはっきりしていて、すっきり直せることが多いような気がします。それは

オブジェクトとインスタンスはほぼ同義語で、これにはデータ自体と、メソッドが両方備わっているというのが特徴です。このことをカプセル化といい、オブジェクト指向では重要な概念で、メソッドや属性の名前を変えない限り、そのクラスを変更しても(例えば先ほどの例でcsvファイルから名前を取り出していましたが、これを直接データベースからデータを取り出すような別のクラスに置き換えたりする)、他は何も変更しないですみます。

継承

次に、今回の例では出ていませんが、継承というのが、同様にオブジェクト指向では重要です。

実際の例で考えると、「誰かが国籍のデータも入れて、国籍も挨拶の時に言うべきだよ」と言い、今回のプログラムを修正するとするとどうなるか。

#!/usr/local/bin/ruby
require 'csv.rb'
##先ほどのHumanクラスが定義されているファイルをインクルード
##ただし、実行部分があるとそれまで実行されてしまうので、
##クラスの定義のところだけを記載したファイルhumanClass.rbを呼び込む。
require 'humanClass.rb'
class Human2 < Human
#            ^^^^^^^ここで以前定義したHumanを継承している
  def initialize(*properties)
    super
#   ^^^^^ここでHumanの方のクラスの(スーパークラスという)initializeを呼び出す。
    @nationality = properties[3]
#   ^^^^^^^^^^^^^^^^^^^^^^^^^^^国籍をインスタンス変数に代入
  end
  def aisatu
    super
#   ^^^^^Humanのaisatuを実行
#その後に、国籍を宣言している。
    if(@nationality=="Japanese")
      print "     見てわるように日本人なんだな、これが。\n"
    else
      print "     見てわるように外国人なんだな、これが。\n"
    end
  end
end
##ここからmain
hairetu=Array.new
CSV::Reader.parse(File.open('human2.csv', 'r')) do |row|
hairetu << \
    Human2.new(row.to_a[0].to_s,row.to_a[1].to_i,row.to_a[2].to_s,row.to_a[3].to_s) \
# ^^^^^^^^^^^^^^^^^^^^^国籍を引数に渡している
end

hairetu.each{|kojin|
  kojin.aisatu
}

ただし、データファイルを次のように修正して、human2.csvとする。

坂井和郎,30,male,Korean
知久友子,44,female,Japanese
知久和郎,44,male,Mongolian
渡辺健一郎,44,male,Japanese

これを実行すると以下のように、

こんにちは、僕は坂井和郎です。
     見てわるように外国人なんだな、これが。
こんにちは、私は知久友子ですわ。
     見てわるように日本人なんだな、これが。
こんにちは、わしは知久和郎じゃ。
     見てわるように外国人なんだな、これが。
こんにちは、わしは渡辺健一郎じゃ。
     見てわるように日本人なんだな、これが。

ここのポイントはもともとあるHumanというクラスを継承して、少し修正して使っているところで、今回の例はも、もちろんいわゆる「コピペ」で継承などせずに、Human自体を修正してもかまわないのだけれど、色々な似たようなクラスを作る必要があったり、大きなクラスなどを利用するなどを考えると、これは結構使えるはずです。

さらに、機能を追加してみましょう。せっかくHuman2クラスの人々は挨拶が出来るのだから、当然、さよならもいえなきゃ。そこで、

#!/usr/local/bin/ruby
require 'csv.rb'
##先ほどのHumanクラスが定義されているファイルをインクルード
##ただし、実行部分があるとそれまで実行されてしまうので、
##クラスの定義のところだけを記載したファイルhumanClass.rbを呼び込む。
require 'humanClass.rb'
class Human2 < Human
  def initialize(*properties)
    super
    @nationality = properties[3]
  end
  def aisatu
    super
    if(@nationality=="Japanese")
      print "     見てわるように日本人なんだな、これが。\n"
    else
      print "     見てわるように外国人なんだな、これが。\n"
    end
  end
  ##さよならメソッド
  def bye
    if(@sex == "female")
      print "                                  さよなら。\n"
    elsif(@age > 40)
      print "                                  左様ならば、おいとまもうす。\n"
    elsif(@age <= 40)
      print "                                  バイバイキ〜ン\n"
    end
  end
end
##ここからmain
hairetu=Array.new
CSV::Reader.parse(File.open('human2.csv', 'r')) do |row|
hairetu << \
    Human2.new(row.to_a[0].to_s,row.to_a[1].to_i,row.to_a[2].to_s,row.to_a[3].to_s) \
end

hairetu.each{|kojin|
  kojin.aisatu
  kojin.bye
}

出力は

こんにちは、僕は坂井和郎です。
     見てわるように外国人なんだな、これが。
                                  バイバイキ〜ン
こんにちは、私は知久友子ですわ。
     見てわるように日本人なんだな、これが。
                                  さよなら。
こんにちは、わしは知久和郎じゃ。
     見てわるように外国人なんだな、これが。
                                  左様ならば、おいとまもうす。
こんにちは、わしは渡辺健一郎じゃ。
     見てわるように日本人なんだな、これが。
                                  左様ならば、おいとまもうす。

ポリモルフィズム

さて、最後にもう一つ、ポリモルフィズムというのがオブジェクト指向では重要です。次のプログラムでは人間ではなく、イヌのクラスを作っています。

#!/usr/local/bin/ruby
require 'csv.rb'
require 'humanClass.rb'
class Human2 < Human
  def initialize(*properties)
    super
    @nationality = properties[3]
  end
  def aisatu
    super
    if(@nationality=="Japanese")
      print "     見てわるように日本人なんだな、これが。\n"
    else
      print "     見てわるように外国人なんだな、これが。\n"
    end
  end
  ##さよならメソッド
  def bye
    if(@sex == "female")
      print "                                  さよなら。\n"
    elsif(@age > 40)
      print "                                  左様ならば、おいとまもうす。\n"
    elsif(@age <= 40)
      print "                                  バイバイキ〜ン\n"
    end
  end
end

class Dog < Human2
  def aisatu
    if(@sex == "female")
      print "わんわん、","わんわんは",@name,"だわん。\n"
    elsif(@age > 40)
      print "ウォンウォン、","ウォンウォンは",@name,"だウォン。\n"
    elsif(@age <= 40)
      print "バウワウ、","バウワウは",@name,"だバウワウ。\n"
    end

    if(@nationality=="Japanese")
      print "     日本人だキャーン。\n"
    else
      print "     外国人だキャーン。\n"
    end
  end
  ##さよならメソッド
  def bye
    if(@sex == "female")
      print "                                  さよならだワン。\n"
    elsif(@age > 40)
      print "                                  さよならだウォンウォン\n"
    elsif(@age <= 40)
      print "                                  バイバイ、バウ\n"
    end
  end
end
##ここからmain
hairetu=Array.new
doghairetu=Array.new

CSV::Reader.parse(File.open('human2.csv', 'rb')) do |row|
hairetu << \
    Human2.new(row.to_a[0].to_s,row.to_a[1].to_i,row.to_a[2].to_s,row.to_a[3].to_s) \
doghairetu << \
    Dog.new(row.to_a[0].to_s,row.to_a[1].to_i,row.to_a[2].to_s,row.to_a[3].to_s) \
end

print "--------人間の場合\n"
hairetu.each{|kojin|
  kojin.aisatu
  kojin.bye
}

print "--------イヌの場合\n"
doghairetu.each{|inu|
  inu.aisatu
  inu.bye
}

出力は以下のようになります。

--------人間の場合
こんにちは、僕は坂井和郎です。
     見てわるように外国人なんだな、これが。
                                  バイバイキ〜ン
こんにちは、私は知久友子ですわ。
     見てわるように日本人なんだな、これが。
                                  さよなら。
こんにちは、わしは知久和郎じゃ。
     見てわるように外国人なんだな、これが。
                                  左様ならば、おいとまもうす。
こんにちは、わしは渡辺健一郎じゃ。
     見てわるように日本人なんだな、これが。
                                  左様ならば、おいとまもうす。
--------イヌの場合
バウワウ、バウワウは坂井和郎だバウワウ。
     外国人だキャーン。
                                  バイバイ、バウ
わんわん、わんわんは知久友子だわん。
     日本人だキャーン。
                                  さよならだワン。
ウォンウォン、ウォンウォンは知久和郎だウォン。
     外国人だキャーン。
                                  さよならだウォンウォン
ウォンウォン、ウォンウォンは渡辺健一郎だウォン。
     日本人だキャーン。
                                  さよならだウォンウォン

さて、このポリモルフィズムとは最後の、

print "--------人間の場合\n"
hairetu.each{|kojin|
  kojin.aisatu
  kojin.bye
}

print "--------イヌの場合\n"
doghairetu.each{|inu|
  inu.aisatu
  inu.bye
}

上記のうちの問題のメソッド

kojin.aisatu
kojin.bye
inu.aisatu
inu.bye

aisatuとbyeというメソッドがHuman2クラスにもDogクラスにも定義されているわけですが、同じメソッド名で違う動作が期待できるわけです。これはrubyでは多用されていて結構覚えるのに便利です。例えば、次の簡単な例はlengthというメソッドを文字列と配列にたいして行った場合です。"abc"は3文字なので、3が出力。[1,2,3,4]は配列が4つ入っているので、4が出力されるということになります。

obj = "abc"
print obj.length, "\n"          # => 3
obj = [1,2,3,4]
print obj.length, "\n"          # => 4

実際には、まだまだこのポリモルフィズムは奥深いのですが、しっかりとは理解していません(;´_`;)。最後にJavaの例を出すと

interface I {
    public abstract void m();
}

class C1 implements I {
    public void m() {
        System.out.println("C1");
    }
}

class C2 implements I {
    public void m() {
        System.out.println("C2");
    }
}

class C3 implements I {
    public void m() {
        System.out.println("C3");
    }
}

public class Polymorphism {
    public static void main(String[] args) {
        I[] a = new I[3];
        a[0] = new C1();
        a[1] = new C2();
        a[2] = new C3();
        for (int i = 0; i < a.length; i++) {
            a[i].m();
        }
    }
}

この例ではinterfaceというものを使っていますが、Javaではよく使われるのですが、今回の例ではJavaでは変数には「型」があって、rubyやperlのように変数に何でも(文字列でも数字でも、あるいはオブジェクトでも)入れられるわけじゃないので、3つのクラスC1、C2、C3という3つのクラスのインスタンスをIというインスタンスを格納するI[]という配列に入れる方法---もちろんもっと奥深いのですが---という感じで理解してください。

そして、一番下の方にあるfor文でm()というメソッドを実行していますが、それぞれ"C1"、"C2"、"C3"と標準出力にプリントされます。

つまり、m()という同じメソッドで挙動が変えられるわけです。

わかりました?わかるはずないですよね、わかっていない人がわかっていない人に教えるのは無理かも(@_@)。

さてさてrubyでメールを送ろう

とりあえず手続き型でメールを送ってみましょう。

このスクリプトは以下のアドレス用のファイル(add.csv,今回の例では固定)とメールの中身とfromとsubjectが書いてあるファイル(今回はmail.txtだが、これは引数で渡す)を用意しておく。

##以下がadd.csv##
chikkun@chikkun.com,知久和郎
sakai@syuei.tv,坂井和郎
chikkunk530@docomo.ne.jp,坂井携帯


##以下がmail.txt##
subject:メールのアドレスが変わりました。
from:知久和郎 <chikkun@chikkun.com>
おら、もう疲れただ。そろそろ家に帰りたいだ。でも、このプログラムが終わ
らないと、何か中途半端な気がして、とりあえずこれが終わったら帰るだ。

とは言いながら、どうしても帰って暖かい焼酎飲んで、おいしいおでんでも
つまんでいたい、という妄想にかられてしまう。

いっそのこと帰っちゃうか。よし、そうしよう、そうしよう。

ではでは。

--------------------------------------
知久 和郎 <chikkun@chikkun.com>
         TEL:042-799-6253
         FAX:042-799-6253
         携帯:090-1535-3319
  〒194-0004 町田市鶴間1013-10 608号
          HP:http://www.chikkun.com
--------------------------------------

一通り見てみると....

  • 2行目は前にも出てきた、CSVファイルを扱うライブラリーのロード
  • 4行目は、日本語のコードを変換するライブラリーのロード。メールの漢字コードはJIS(iso-2022-jp)なので、shift_JISは変換する必要がある。
  • 6行目は、本来、SubjectのところやFromのところはasciiし使ってはいけないことになっており、知久和郎 <chikkun@chikkun.com>などは駄目なのだが、これをbase64という方法でエンコードして(つまりasciiにしてしまって)送り、メーラはしっかりそれを反対にデコードしている。
  • 8行目は時刻を単に読み取るためのライブラリーのロード(たしか組み込みメソッドにもあったと思うのだけど...)。
  • 10行目はめーるを送るためのライブラリーのロード。下のコメントにもあるように、 send_mail(本文,送り手のメールアドレス,送り先のメールアドレス) というメソッドがある。
  • 14行目〜30行目から自分用のメールを送るメソッドを定義してある。というのも先に述べたように、FromやSubjectに日本語が使えないので、自らbase64エンコードをしている。 ?ISO-2022-JP?B? の中の ?B? の部分がbase64でエンコードしていますよ、という印。最初のISO-2022-JPは漢字コードはJISですよ、ということ。
  • 28行目の Net::SMTPSession.start('localhost', 25)localhost はsmtpサーバーのアドレスで、変更か。ただし、ノートなどでメールを外から出したいときなど、smtpが最近では一度メールを見に行ってからでないと送れなかったり、まったく受け付けてくれなかったりする。そこで次のアドレスから「melon」というWindows用のフリーのsmtpをダウンロードして(単に解凍して、それを起動すればOK。僕の場合はスタートアップに入れて自動起動させている)、起動するとlocalhostのままで大丈夫(ダウンロード先は ここ )。
  • 32行目から38行目までは、単にエラーオブジェクトというのを作っている。Javaなどもそうなんだが、エラーの処理なんかもオブジェクトを使ってコントロール仕組みを作っているのだけれど、詳細は僕もわかっていない(T_T)。
  • 44行目〜78行目まではメールの中身を読み取る部分。
  • 以下のbeginとendの間はエラーをトラップするための部分で、subjectが書いてなかったり、fromがなかったり、本文がなかったりすると、わざとraise ErrorNoSubjectなどとエラーを起こさせ、下のrescueでトラップして、エラーメッセージを出させて、終了させている。ちなみにARGV.shiftでコマンドラインの引数ruby sendmail.rb mail.txtのmail.txtを取り出しており、もしそのファイルがなかったら最後のrescueでトラップしている。

    begin ... rescue ... ensure end

  • 83行目からは以前も出てきた、CSVのファイルから送り先を読み取って、toという配列にどんどん格納している。
  • メールを送っているのは87行目から89行目で、上で定義したsendmailというメソッドを使って、メールを送っている。

さて、家の実験ではしっかり、送れています(ただ実験で僕の携帯にはメールが来たことがないのだけれど、突然20通ほど貯まってしまいました(^_^)。

オブジェクト指向では

mail.txtやadd.csvは全く同じで、別の考え方で、スクリプトを書いてみます(しっかりわかっている人に笑われそうですが。しかも下のUMLは初めて書いているので、よくわかりません)。

  • オブジェクト指向でプログラムする際には、最近はやりのUMLというものを使います(とは言っても今回の例のように単純な場合には、もちろん、必要ないわけですが、勉強をかねて)。例えば次のような図を書きます(ユースケース図)。
ユースケース図
  • 次はシーケンス図。
シークエンス図
  • クラス図
クラス図

これらを煮詰めていって、コーディングに入る(らしい---やっぱ、よくわか らん)。

  • 前回の手続き型と少々仕様を変えました。といっても、大きくは変えていません。
    • 基本は ruby sendmail.rb mail.txt というようにsubjectなどが入っているテキストファイルを引数に実行するのは同じですが、あとから別のテキストファイルをもとに、別の内容で別のSubjectで、別のメールアドレス群にも送れるようにした。
    • メールを送るためのクラスなどを別ファイルにして、sendmail2.rbはわずかに次のようにした。わざわざMainクラスなどを作る必要はないのですが、今回はできる限りクラスにしてしまおうとしました。クラスの外にあるのは Main.new だけです(Javaだとすべてがクラスになるのですが...)。
      require "MailSend.rb"
      ####ここからMainクラス
      class Main
        ###GetMailToaddressesオブジェクト取得
        a=GetMailToaddresses.new("add.csv")
        ###ReadMailオブジェクト取得
        m = ReadMail.new(ARGV.shift)
        ###SMailオブジェクト取得
        s = SMail.new("localhost",a,m)
        ###さあメール送信!!
        s.sendmail
      
        ##メールの中身と宛先を変えて再度メールを送る
        a.csvFile = "add2.csv"
        m.file = "mail2.txt"
        s.sendmail
      end
      
      Main.new
      
    • まず require "MailSend.rb" で別ファイルにしたのを呼び込む。
    • 次に a=GetMailToaddresses.new("add.csv")m = ReadMail.new(ARGV.shift) でインスタンスを生成し、 s = SMail.new("localhost",a,m) でSmailのインスタンスを生成しているのですが、ここでaとmという(つまり、先ほど生成したGetMailToaddressesクラスのインスタンスとReadMailクラスのインスタンスを引数に渡しています(渡されたものをどう使うかは後々に述べます)。
    • 次に、インスタンス変数に直接アクセスし別の内容で、別の人々に送ります。それが最後の3行です。a.csvFileを別のadd2.csvにし、m.fileを別なものしてメールの内容を変えて送っています。つまり今回は実際にはメールを2度送っているわけです。
      a.csvFile = "add2.csv"
      m.file = "mail2.txt"
      s.sendmail
      
    • 使ったファイルは以下の4つ(すべてWindows標準のシフトJIS)。
      ##add.csv
      chikkun@chikkun.com,知久和郎
      sakai@syuei.tv,坂井和郎
      chikkunk530@docomo.ne.jp,坂井携帯
      
      ##mail.txt##
      subject:メールのアドレスが変わりました。
      from:知久和郎 <chikkun@chikkun.com>
      おら、もう疲れただ。そろそろ家に帰りたいだ。でも、このプログラムが終わ
      らないと、何か中途半端な気がして、とりあえずこれが終わったら帰るだ。
      
      とは言いながら、どうしても帰って暖かい焼酎飲んで、おいしいおでんでも
      つまんでいたい、という妄想にかられてしまう。
      
      いっそのこと帰っちゃうか。よし、そうしよう、そうしよう。
      
      ではでは。
      
      --------------------------------------
      知久 和郎 <chikkun@chikkun.com>
               TEL:042-799-6253
               FAX:042-799-6253
               携帯:090-1535-3319
        〒194-0004 町田市鶴間1013-10 608号
                HP:http://www.chikkun.com
      --------------------------------------
      
      ##add2.txt##
      chikkun@chikkun.com,知久和郎
      sakai@chikkun.com,坂井和郎
      
      ##mail2.txt
      subject:なかなか時間がありませんな。
      from:知久和郎 <chikkun@chikkun.com>
      色々やりたいことがいっぱいあるのに、それをこなすための時間がきわめて不
      足していて、毎日やり残しがいっぱい出てたまる一方なので、それが全て欲求
      不満となって、酒を思わず飲む。そして、それがまた時間不足を生み....。
      
      やれやれ。
      --------------------------------------
      知久 和郎 <chikkun@chikkun.com>
               TEL:042-799-6253
               FAX:042-799-6253
               携帯:090-1535-3319
        〒194-0004 町田市鶴間1013-10 608号
                HP:http://www.chikkun.com
      --------------------------------------
      
    • 以上で ruby sendmail2.rb mail.txt と打ち込めば、上記の5名のメールアドレスに最初の3人にはmail.txtの内容が、次の2人(add2.csv)にはmail2.txtのメールが送られます(実際確認しました)。
    • 最初の10行はsendmail.rbと同じ。
    • 14行目からはSmailというクラスの定義。
    • 15行目の attr_accessor :smtp, :getMail, :readMail はアクセッサの定義。アクセッサとは通常クラス内の変数には外から(sendmail2.rbのMailクラスなどから)はアクセスできません(つまり値を取り出したり、値を代入したり出来ません)。これを書いておくとr.fname="mail2.txt"などと代入したり出来るようになるわけです。つまり、先に述べたsendmail2.rbの a.csvFile = "add2.csv"m.file = "mail2.txt" のようにできるわけです。
    • rubyの場合コンストラクタはinitialize()というメソッドになります。Javaの場合はそのクラスと同じ名前のメソッドがコンストラクタなのですが(今回の例で言えば、Smail()というメソッド)、その辺がJavaとは違います。
    • そもそもコンストラクタとは何かというと、一番大事な仕事としては、新しいインスタンスを生成して、それを返すということでsendmail2.rbの a=GetMailToaddresses.new("add.csv") などのようにGetMailToaddressesというクラスから(クラスは設計図みたいなもので、まだ実体化していない---オブジェクトになっていない)インスタンスを生成し、aという変数にそれを代入しているわけです。
    • しかもコンストラクタには引数を渡せることができ、今回はinitialize("smtpのアドレス",GetMailAddressesのインスタンス,ReadMailのインスタンス)という3つの引数を渡しています。つまり、変数の初期化なども行うことができるわけです。
    • 21行目から27行目まではsmtpの指定を忘れたり、initializeの2番目と3番目の引数が必要なGetMailToaddressesやReadMailでない時に、例外を(エラーを)能動的に起こしています。
    • 30行目からは以前の手続き型のところにもあったメールを送るメソッドの定義で、ちょっと違うのは、32/34行目の2行で、ここでアドレスとメールの内容等を取得しています(取得といっても、インスタンスの中に蓄えられていて、このSmailというクラスの中にはまだありません)
      @getAdd.getAddresses
      @readMail.getMailContents
      
    • 上記でアドレスなどを@readMailが指しているインスタンスの中に取得したものを、35・37行目・47行目の @readMail.subject@readMail.from@readMail.naiyo でこのクラスの中に取り入れています。
    • 46行目からはSMTPと通信できななどのエラーが起こった時の対処です。
    • 次の2つのクラスはやっていることはほぼ同じで、ReadMailクラスではメールの内容や題名を、GetMailToaddressesクラスではメールの宛先を取得しています。

う〜ん

あまりにも駆け足で、ここまで来てしまったので、これじゃ「よくわからない」でしょうが、今後は、これまで書いてきたものをもう少し掘り下げて(特にオブジェクト指向やらrubyのメソッドなど)、別のファイルにしていこうと思います。

ここで眠りにはいる(^。^)。 [2004-01-30 21:37]