Ruby に挑戦

Ruby は日本発のオブジェクト指向スクリプト言語です。熱狂的な支持者がいるようです。しかし、オブジェクト指向と聞いただけで拒否反応がおきて手を出しませんでした。「Perl だけでも結構大変だったのに、新しいスクリプトまで手が回らない。」と思っていたのです。ところが、最近少し勉強したところこれはスクリプト言語の傑作ではないかと思うようになってしまいました。このページではオブジェクト指向プログラムなどこれっぽっちも知らない著者の Ruby挑戦記を書いてみようと思います。

Ruby の情報源

Ruby の情報源は「オブジェクト指向スクリプト言語 Ruby (http://www.ruby-lang.org/ja/)」です。

参考書は「プログラミング Ruby」(デビッド・トーマス+アンドリュー・ハント著、田和勝訳、まつもとゆきひろ監修、ピアソン・エデュケーション)が1冊あればプログラムを書くことができるようになります。この本の中のスクリプト例はほとんど、irb で実行して確認することができます。

Ruby は普通のプログラムも書ける。

あたりまえの事ですが、Ruby は普通のプログラムも書けます。例えば 1 から 10 までの数を加算するプログラムは次のようになります。; や $ や { } などがない分読みやすい気がします。for ループの終りはただの end です。" " の中で変数の値を展開するには変数を #{ } で囲みます。

total = 0
for i in 1..10
  total += i
end
print "total = #{total}\n"

これを total.rb というファイルに保存して、コマンドラインから ruby total.rb と入力すると次のようになります。

$ ruby total.rb
total = 55

for 文の引数には配列もとれます。Rubyではオブジェクトの種類によって自動的に演算子の意味が変わるので、オブジェクトの種類が変わっても意味の同じ操作は同じ表記で行うことができます。

total = 0
for i in [ 2, 4, 6, 8, 10 ]
  total += i
end
print "total = #{total}\n"

Ruby は対話的に操作できる

irb( interactive Ruby )を起動すると、Ruby を対話的に操作することが出来ます。操作の度にオブジェクトの内容を確認することが出来るので便利です。オブジェクトとは何かと聞かれると答えに詰まりますが、「物」の事だと考えてください。Ruby で扱うのは全てがオブジェクトです。数もオブジェクトなのです。オブジェクトについては後で述べるとして。先ずコンソールに irb と入力して Ruby を対話的に使ってみます。irb から抜け出すときは exit とだけ入力します。

$ irb
irb(main):001:0> 

irbのプロンプトが現れたら a = -3.14 を入力してみます。

irb(main):001:0> a = -3.14
-3.14

-3.14 という数が表示されますが、a = -3.14 という式を実行したときに返される値です。Ruby では全ての文が式なので何らかの値を返します。irb の中ではオブジェクトの値を簡単に確認することが出来ます。次のように a とだけ入力すると a の中身の -3.14 を確認することが出来ます。

irb(main):007:0> a
-3.14

先程 Ruby では全てがオブジェクトだといいました。また、オブジェクトとは物であるといいましたが、ただの物ではありません。賢い物なのです。なぜ賢いかと言うと、問い合わせをすると答えてくれるのです。それでは今作ったオブジェクト a に a の絶対値を問い合わせてみましょう。

irb(main):008:0> a.abs
3.14

今度は四捨五入してみましょう。

irb(main):010:0> a.round
-3

a 以下の最大の整数を問い合わせます。

irb(main):011:0> a.floor
-4

さきほど数自体もオブジェクトだと言いました。したがって、数自身にも問い合わせが出来るのです。3.14 を整数に変換したらどうなるでしょうか。

irb(main):012:0> 3.14.to_i
3

数に問い合わせをすると答えてくれるのだから便利です。数を加工するのに、その数にメッセージを送るだけで良いのですから。数を加工する関数とその数の種類が一致するかどうかなど全く考える必要がありません。それらの細かい情報はオブジェクト自身が持っているのでプログラムする側は全く考慮しないでもよいのです。また、問い合わせをして帰って来る返事もまたオブジェクトなので次のように問い合わせを続けることも出来ます。

irb(main):013:0> [ 'banana', 'apple', 'orange', 'grapes' ].sort.reverse
["orange", "grapes", "banana", "apple"]

このオブジェクトに対する問い合わせのことを、「メソッド」と言います。メソッドは関数なので引数を取ることもできます。また、自分でクラスを作るときはそのクラスのメソッドを定義することができます。

Ruby にはメソッドしかない

他の言語で関数、プロシージャ、メソッド、ルーチンなどと呼ばれているものは、Ruby ではメソッドだけしかありません。メソッドはキーワード def で定義します。それでは、数を二倍にするメソッドを作ってみましょう。次のプログラムを double.rb というファイルに作成します。

def double( n )
  n * 2
end

puts double( 2 )

Ruby のメソッドでは最後に評価された式の値が戻り値として返されます。このプログラムでは引数 n を二倍した n * 2 が返されます。このプログラムを実行すると次のようになります。

$ ruby double.rb
4

今度は、階乗を計算するメソッドを作ってみましょう。次のようなプログラムを factorial.rb というファイルに作成します。

def factorial( num )
  if num == 1
    1
  else
    num * factorial( num - 1 )
  end
end

puts factorial( 10 )

Ruby では再帰呼出しもこのようにすっきりと表現することが出来ます。factorial(1) では 1 が、factorial(n) では n * factorial(n-1) が返されることになります。実行してみましょう。

$ ruby factorial.rb
3628800

factorial() メソッドを他の値でも試してみたいのですがいちいちファイルの内容を変更するのは不便です。factorial.rb を irb で動かしてみましょう。ファイルを irb に読み込むときは require を使います。

$ irb
irb(main):001:0> require 'factorial.rb'
3628800
true

それでは factorial を使って 3 の階乗を計算してみましょう。

irb(main):002:0> factorial(3)
6

うまくいったようです。それでは 100 の階乗はどうでしょうか。

irb(main):003:0> factorial(100)
933262154439441526816992388562667004907159682643816214685929638952175999932299
156089414639761565182862536979208272237582511852109168640000000000000000000000
00

随分大きな数になりました。それでは 200 の階乗も調べてみましょう。

irb(main):003:0> factorial(200)
788657867364790503552363213932185062295135977687173263294742533244359449963403
342920304284011984623904177212138919638830257642790242637105061926624952829931
113462857270763317237396988943922445621451664240254033291864131227428294853277
524242407573903240321257405579568660226031904170324062351700858796178922222789
623703897374720000000000000000000000000000000000000000000000000

Ruby ではメモリーの続く限りどんな大きな整数でも扱うことが出来ます。

Ruby は入出力が超簡単

Ruby での入出力は基本的には gets と puts の二つのメソッドさえ使えれば用が足ります。gets は便利なメソッドで、Ruby のスクリプトに a = gets というコマンドがあると、そのスクリプトを走らせたときにコマンドラインに引数がなければ、標準入力から一行を a に代入します。また、コマンドラインに引数としてファイル名が与えられていると、そのファイルから順に一行を取り出します。ためしに、次のようなプログラムを cat.rb という名前で作成して見ましょう。

while( a = gets )
  puts a
end

まず cat.rb を引数無しで動かしてみます。

$ ruby cat.rb
hello
hello
world
world

この場合、キーボードから文字を入力して改行キーを押すたびにその文字列が出力されます。プログラムを中止するには Ctr-d を入力します。それでは、ファイル名を引数として与えた場合はどうでしょう。

$ ruby cat.rb cat.rb
while( a = gets )
  puts a
end

このばあいは、引数として与えられたファイル cat.rb の内容が一行ずつ最後まで表示されます。また、リダイレクトを使って出力をファイルに保存してみましょう。

$ ruby cat.rb cat.rb > outfile.txt
$ cat outfile.txt
while( a = gets )
  puts a
end

確かに cat.rb の内容が outfile.txt に保存されました。このように gets と puts を使うだけで、小さなツールならすぐにでも作ることが出来ます。もちろん、Perl のように print 文や、printf 文を利用して細かい操作を行うことも出来ます。次のプログラムを sum.rb という名前で作成して見ましょう。

print "Please input number: "
n = gets.to_i
total = 0
for i in 1..n
  total += i
end
print "Total is #{total}\n"

このスクリプトを実行すると次のようになります。

$ ruby sum.rb
Please input number: 10
Total is 55

上のプログラムでとくに分かりにくい所はないと思いますが、二行目の n = gets.to_i に注意してください。gets は入力データを文字列オブジェクトとして返します。n には整数を代入したいので to_i というメッセージを gets に送って整数に変換しています。Ruby ではオブジェクトのタイプの変換は明示的に行わなければなりませんが、オブジェクトにメッセージを送るだけなので簡単です。

Ruby の配列は、配列も連想配列も a[ key ]

Perl の連想配列がうれしかった人は多いと思いますが、Ruby にも配列と連想配列があります。しかしキーに対応する値を取り出すのに配列と連想配列の区別はなく、全て a[ key ] の形式で取り出すことが出来ます。irb でちょっと試してみましょう。

$ irb
irb(main):001:0> a[ 1 ] = 'January'
NameError: undefined local variable or method `a' for #
        from (irb):1

おやおや、いきなりエラーになってしまいました。やっぱり Ruby は難しいじゃないかと匙をなげないでください。Ruby が扱うデータは全てがオブジェクトですから a といっても数なのか、配列なのか、連想配列なのか区別がつきません。それであらかじめ a が配列であることを宣言しておく必要があります。それには a = [] を使います。それでは実際にやってみましょう。

irb(main):002:0> a = []
[]
irb(main):003:0> a[1] = 'January'
"January"
irb(main):004:0> a[1]
"January"

どうやらうまくいったようです。それでは、今度は文字列 'January' をキーにして値 31 を入力してみましょう。

irb(main):005:0> a['January'] = 31
TypeError: no implicit conversion from string
        from (irb):5:in `[]='
        from (irb):5

やれやれ、また叱られてしまいました。先程 a は配列だと宣言してあるので、a には文字列をキーにすることが出来ないのです。それでは、a を連想配列だと宣言しなおしましょう。それには、a = {} を使います。

irb(main):006:0> a = {}
{}
irb(main):007:0> a['January'] = 31
31
irb(main):008:0> a['January']
31

配列と連想配列は最初の宣言さえ気をつけておけば、値の取り出しは統一的に扱うことが出来ます。実際には次のように宣言のときに値もいれてしまうことが多いので、あまり宣言を気にする必要はないことが多いと思います。例えば配列の場合は次のようになります。

irb(main):009:0> a = [ 2.93, 'dog', 81 ]
[2.93, "dog", 81]
irb(main):010:0> a[ 0 ]
2.93
irb(main):011:0> a[ 1 ]
"dog"
irb(main):012:0> a[ 2 ]
81

また、連想配列の初期化は次のようになります。

irb(main):013:0> a = { 'dog' => 'bow wow', 'cat' => 'mew' }
{"dog"=>"bow wow", "cat"=>"mew"}
irb(main):014:0> a[ 'dog' ]
"bow wow"
irb(main):015:0> a[ 'cat' ]
"mew"

このように配列も連想配列も同じ呼出しかたができるのはありがたいものです。Ruby の魅力のひとつは、操作の細かい規則にこだわらず、アイディアをそのままプログラムにしていけるところです。

Ruby の配列は賢い

Ruby は全てオブジェクトでできています。配列もオブジェクトです。したがって、Ruby の配列は賢いのです。配列にデータを入れてさえおけば、データの加工は配列の方がやってくれるのです。例えば次のような配列を作ります。

$ irb
irb(main):001:0> a = [ 'dog', 'cat', 'sheep', 'horse' ]
["dog", "cat", "sheep", "horse"]

配列 a の 0 番目から 1 番目までの値を取り出したいときは次のようにします。

irb(main):002:0> a[0..1]
["dog", "cat"]

配列 a の 1 番目から 3 個取り出したいときは

irb(main):003:0> a[1, 3]
["cat", "sheep", "horse"]

アルファベット順に並べ変えたいときは

irb(main):004:0> a.sort
["cat", "dog", "horse", "sheep"]

順序を逆にしたいときは

irb(main):005:0> a.reverse
["horse", "sheep", "cat", "dog"]

アルファベット順に並べて、順序を逆にしたいときは

irb(main):006:0> a.sort.reverse
["sheep", "horse", "dog", "cat"]

データがオブジェクトであるために、そのデータの加工がメッセージを送るだけでできてしまうというところがオブジェクト指向言語の便利なところです。クラスを用いたオブジェクト指向プログラムを使わなくても Ruby を選ぶ理由は十分にあると思います。

Ruby のファイルの読み書きはファイルオブジェクトを作るだけ

Ruby のファイルの読み書きも簡単です。afile = File.new("testfile", "r") とするだけです。第一の引数はファイル名、第二の引数は読むのか書くのかを指定します。それでは実際にやってみましょう。まず、ファイルオブジェクトを書き込みモードで作成して文字列を書き込みます。

$ irb 
irb(main):001:0> afile = File.new("testfile", "w")
#
irb(main):002:0> afile.puts "hello, world"
nil
irb(main):003:0> afile.puts "how are you?"
nil
irb(main):004:0> afile.close
nil

ファイルへの書き込みは afile.puts のように afile ファイルオブジェクトに puts メッセージを送ることで行います。最後は afile.close でファイルをクローズするのを忘れないようにします。次にこのファイルを読み込みモードでオープンします。

irb(main):005:0> afile = File.new("testfile", "r")
#
irb(main):006:0> afile.gets
"hello, world\n"
irb(main):007:0> afile.gets
"how are you?\n"
irb(main):008:0> afile.close
nil

ファイルの読み込みは gets メッセージを afile オブジェクトに送ればできます。gets メッセージ 1 個毎に 1 行が読み込まれます。最後は afile.close でファイルを閉じます。

Ruby のコマンドライン引数は配列 ARGV に入る

Ruby のスクリプトはコマンドライン引数を取ることができます。コマンドライン引数を使うときは ruby myscript.rb arg1 agr2 .. という使いかたになります。スクリプトに渡された引数は配列 ARGV に納められます。それでは、階乗の計算プログラムをコマンドライン引数をとれるように改造しましょう。次のプログラムを fact.rb という名前で作成してください。

def fact(n)
  if n == 1
    1
  else
    n * fact(n-1)
  end
end

puts fact(ARGV[0].to_i)

実行してみましょう

$ ruby fact.rb 4
24

複数のコマンドライン引数は順に ARGV[0], ARGV[1], ... と配列に代入されていきます。次のような1行プログラムを arg.rb というファイルに作成すると。コマンドライン引数をずべて印刷することができます。

ARGV.each { |a| puts a }

実行例は次のようになります。

$ ruby arg.rb A B C D E
A
B
C
D
E

見慣れないプログラムですが、これはイテレータというものを利用したプログラムです。詳しくは次の項で説明します。

Ruby のイテレータはマシンガン

Ruby の大きな特徴にイテレータがあります。オブジェクトのメソッドのひとつですが、オブジェクトからマシンガンのようにデータを弾き出すしくみと考えるとわかりやすいかも知れません。このメソッドが分かりにくいと言われるのは、引数にコードブロック(小さなプログラム)を持って来なくてはならないからだと思います。しかし、使ってみるとそれほど難しいものではありません。たとえば、[ 'dog', 'cat, 'sheep' 'horse' ]の値を印刷したいとします。すると、each イテレータを使って次のように書くことができます。

$ irb
irb(main):001:0> a = [ 'dog', 'cat', 'sheep', 'horse' ]
["dog", "cat", "sheep", "horse"]
irb(main):002:0> a. each do |animal|
irb(main):003:1*   puts animal
irb(main):004:1> end
dog
cat
sheep
horse

イテレータ each は配列 a の値を先頭からひとつずつ取り出して do 〜 end ブロックの | で囲まれた変数 animal に格納します。格納された変数 animal は puts で出力されます。この動作が配列 a のデータの最後まで繰り返されます。イテレータとは繰り返すものと言う意味です。このプログラムは次のように書くこともできます。

irb(main):005:0> a.each { |animal| puts animal }
dog
cat
sheep
horse

イテレータを使うと色々と面白いことができます。例えば 3 回 Ho! と出力したいときは次のようにします。

irb(main):006:0> 3.times { print "Ho! " }
Ho! Ho! Ho! 

ファイルオブジェクトのイテレータを利用すると、ファイルから 1 行ずつ次々に読み込んでいくことができます。

irb(main):007:0> afile = File.new("testfile", "r")
#
irb(main):008:0> afile.each_line {|line| puts "Got #{line.dump}"}
Got "hello, world\n"
Got "how are you?\n"
#

Ruby は機能拡張も簡単

Ruby はモジュール/クラスライブラリーで簡単に機能拡張することができます。それも require "モジュール名" とするだけです。例えば標準ライブラリーに複素数を扱えるようにする complex.rb があります。これを使うと + や * 等の演算子を複素数用に拡張することができます。そうするためには自分のスクリプトにただ1行 require "complex" と記入するだけなのです。例えば次のスクリプトを cmp.rb という名前で作成してみましょう。

require "complex"

a = Complex(1, 1)
print "a = #{ a.to_s }\n"
b = 1.im
print "b = #{ b.to_s }\n"
c = a + b
print "a + b = #{ c.to_s }\n"
d = a * b
print "a * b = #{ d.to_s }\n"
e = a.abs
print "|a| = #{ e.to_s }\n"

実行すると次のようになります。

$ ruby cmp.rb
a = 1+1i
b = 1i
a + b = 1+2i
a * b = -1+1i
|a| = 1.414213562

すごいですね。require "complex" の1行を入れただけで、+ も * も複素数の計算ができるように拡張されてしまいました。もちろん自分のクラスライブラリーをつくることによって、このような拡張を自前ですることは Ruby ではそう難しいことではないのです。

おしまい

どうでしょう、ここまで述べたことだけでも、入出力を持った小さなプログラムを Ruby で作成することが十分可能だと思います。以前に BASIC を使っていて、Visual C++ になってからプログラムをあきらめていた人や、Perl で挫折したことのある人は心がむずむずして来たのではないでしょうか。埃を被っていた「BASIC による ...」という本をもう一度取りだしてみたくなって来たのではないでしょうか。ここでは述べませんでしたが、Ruby では正規表現を用いたスクリプトも Perl の使いかたをそのまま使うことができます。Perl から移行する人にはうれしい話だと思います。また、ここでは、意識的にオブジェクト指向的なプログラムについては述べませんでした。オブジェクト指向プログラムを作らなくても Ruby は十分魅力的なことを知ってもらいたかったからです。しかし、Ruby を利用しているうちにきっとオブジェクト指向プログラムを組んでみたくなるに違いありません。そうすれば、プログラムを書くと言うことはこんなに楽しいことだったのかと驚くに違いありません。