Rubyトレーニング

日曜プログラマーの悩みは、新しいプログラム言語を勉強したときにしばらくするとすっかり忘れてしまうと言うことです。ここでは、Ruby の使い方を思い出すための小さなプログラムを集めました。これらのプログラムはわざわざファイルを作成しなくても irb のプロンプトから直接実行することができますから、何度でも手軽に試してみることができます。プログラムの解説は省きますが、irb を起動した後入力して試してみてください。習うより慣れよ。irb でプログラムを動かしているうちに Ruby 本を読みこなすコツが身につくとおもいます。

注:ここに紹介したスクリプトは全て irb のコンソールから対話的に実行するように想定しています。irb では一つの文をすぐに実行するので、複数の文からなるスクリプトを実行したい場合に困る時があります。紹介したスクリプト中の赤字で示した beginend は、複数の文をブロックにまとめるために使っていますので、irb を使わずにスクリプトをファイルに作って実行するときは取り除いてください。

また、「irb 用 edit コマンド」には、irb 上でエディターを利用してプログラムを編集する方法を解説しています。

irb(Interactive Ruby)を起動するにはコマンドラインから irb と入力します。irb から抜け出すには exit と入力します。

$ irb
irb(main):001:0> print "hello, world\n"
hello, world
nil
irb(main):002:0> exit
$ 

Hellow, World

print "hello, world\n"

入出力

その1

begin
print "name "
name = gets.chop
puts "hello, #{name}"
end

注:上のプログラムの最初の行の begin と 最期の行の end は irb が一行毎に実行しないようにするためのおまじないです。プログラムファイルを作成して実行するときは必要ありません。

その2

gets による入力は改行つきの文字列なので数を入力するためには明示的な型変換 to_i または to_f が必要です。

begin
total = 0
print "number = "
n = gets.to_i
for i in 1..n
  total += i
end
puts "total = #{total}"
end

コマンドライン引数

コマンドライン引数関係の命令はスクリプトファイルを作らないとテストできませんが irb から出たくないときは、

irb> system "vi tempfile.rb"

として system 命令を使って vi を起動します。編修が済んだら vi を終了し、

irb> system "ruby tempfile.rb arg_1 arg_2"

で、実行できます。

コマンドライン引数の受け取り方

ARGV.each {|arg| puts arg}

実行法の例: irb> system "ruby tempfile.rb dog cat mouse"

フィルター用入出力。コマンドライン引数がない場合は標準入力から、コマンドライン引数がある場合は引数で示されたファイルから一行ずつ読みだします。ファイルは複数個指定できます。

ARGF.each {|line| puts line}

実行法の例: irb> system "ruby tempfile.rb *"

正規表現

s = "hello, world"

s.index(/world/)
/world/ =~ s
puts $~
puts $`
/(\w+)/ =~ s
puts $1
puts $'

s =~ /world/
s.index(/w.*d/)

w = /world/
s.index(w)

s = "hello, hello"
s.sub(/hello/, "HELLO")
s.gsub(/hello/, "HELLO")

"Rhythm & Blues".sub(/(\w+) & (\w+)/, '\2 & \1')

ファイルの読み出し

最初に以下のようなテキストファイルを sample.txt という名前で保存しておいてテストプログラムを走らせます。

hello, world
how are you?
I'm fine thank you.

1) ファイルの内容全部を一気にオブジェクト a に文字列として格納する

begin
file = open("sample.txt")
a = file.read
print a
file.close
end

2) ファイルの内容を一行ずつ読み出す

begin
file = open("sample.txt")
puts file.gets
puts file.gets
puts file.gets
puts file.gets
file.rewind
puts file.gets
file.close
end

3) ファイルの内容を一行ずつの配列として a に格納する

begin
file = open("sample.txt")
a = file.readlines
print a[1]
file.close
end

4) each イテレータを使う

begin
file = open("sample.txt")
file.each {|line| puts line }
file.close
end

5) foeach イテレータでファイルのクローズを自動的に行う

IO.foreach("sample.txt") {|line| puts line}

ファイルの書き込み

1)puts メソッドを使う

begin
file = open("sample2.txt", "w")
file.puts "hello, world"
file.puts "how are you?"
file.puts "fine thanks"
file.close
end

2)write メソッドを使う

begin
a = ["hello, world\n", "how are you?\n"]
f = open("sample3.txt", "w")
f.write( a )
f.close
end

パイプを利用する

begin
p = IO.popen("ls", "r")
puts p.readlines.reverse
p.close
end

if制御文

begin
a = 1
if a == 0
  puts "A"
elsif a==1
  puts "B"
else
  puts "C"
end
end

for制御文

その1

for i in 1..3
  puts i
end

その2

for i in ['dog', 'cat']
  puts i
end

while制御文

begin
i = 0
while i < 4
  puts i
  i += 1
end
end

begin ... end while 文

begin
i = 3
begin
  puts i
  i -= 1
end while i > 0
end

until制御文

begin
i = 0
until i > 4
  puts i
  i += 1
end
end

case制御文

begin
a = 'cat'
case a
when 'dog'
  puts 'A'
when 'cat'
  puts 'B'
else
  puts 'C'
end
end

break 制御文

break でループを抜けます

for i in 1..10
  if i == 5
    break
  else
    puts i
  end
end

next 制御文

next でループの最期にジャンプ

for i in 1..10
  if i%2 != 0
    next
  else
    puts i
  end
end

redo 制御文

redo でループのその回をもう一度最初からやり直します

begin
j = 2
for i in 1..10
  if i == 5
    if j > 0
      puts i
      j -= 1
      redo
    end
  else
    puts i
  end
end
end

イテレータ

1) 配列オブジェクトに each イテレータを使うと配列の要素がひとつずつ取り出されてブロックの変数 a に代入され、puts a が実行されます。

['dog', 'cat', 'rat'].each {|a| puts a}

2) メソッドは引数にブロック({}でかこまれたプログラム)をとることができます。メソッドに渡されたブロック { puts "hello, world" } は、yield の位置で実行されます。

begin
def blk
  yield
  yield
end
blk { puts "hello, world" }
end

3) yield には引数をとらせることもできます。yield の引数はブロック { |x| x * x } の || で挟まれた変数 x に代入され、x * x が実行されます。

begin
def blk(n)
  yield(n)
end
puts blk(3){ |x| x * x }
end

メソッド

1) メソッドの定義

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

2) メソッドの実行

fact( 6 )

3) メソッドの引数の先頭に * がついていると任意の個数の引数が配列としてメソッドに渡ります(可変長引数)。

def sum( *number )
  total = 0
  number.each {|i| total += i}
  total
end

4) sum() の実行例

sum(1, 2, 3) #=> 6

可変長引数のメソッドに配列のデータを渡したいときは引数の先頭に * をつけます。

a = [1, 2, 3]
sum(*a) #=> 6

クラス

1) 長くなるのでプログラムを分けて入力します。まず次のプログラムでクラスを作成。プログラムのなかの @name のように @ が先頭についた変数はインスタンス変数で、オブジェクトを作成したときにどのオブジェクトにも含まれます。(変数の内容はそれぞれのオブジェクトで異ります) initialize メソッドはそのクラスのオブジェクトを作成するときに最初に実行される、オブジェクトの初期化のためのメソッドです。attr_reader と attr_writer はそれぞれ、オブジェクトのインスタンス変数の内容を表示したり、書き込んだりできるようにするための命令です。

class A1
  def initialize( name, id )
    @name = name
    @id = id
  end
  attr_reader :name, :id
  attr_writer :name, :id
  def show
    puts "#{@name}-#{@id}"
  end
end

2) 上のプログラムを irb で実行してから次のプログラムを入力して、A1 クラスのオブジェクト a を生成します。

a = A1.new('hanako', 1)

3) a のメソッド show を呼び出します。

a.show

4) a の @name インスタンス変数の内容を確認します。

a.name

5) a の @name インスタンス変数の内容を変更します。

a.name = 'taro'

6) a の @name インスタンス変数の内容が変更されたことを確認します。

a.name

7) クラス A1 で定義されたメソッドにはどのようなものがあるか確認します。

A1.instance_methods

7) クラス A1 で 親クラスから継承したものも含めて使用可能なメソッドを確認します。

A1.methods

8) クラス A1 の親クラスを確認します。

A1.ancestors

継承

次のプログラムを順に irb に入力します。

1) 親クラスの定義

class A2
  def initialize( name )
    @name = name
  end
  def to_s
    @name.to_s
  end
end

2) 子クラスの定義

メソッド定義の中にあらわれた super というメソッドは、そのメソッド名と同じ名前の上位クラスのメソッドを呼び出します。Ruby では文字列は + 演算子で連結することができます。

class B < A2
  def initialize( name, id )
    super( name )
    @id = id
  end
  def to_s
    super + " -- " + @id.to_s
  end
end

3)親クラスのオブジェクトの作成とその内容の確認

a = A2.new('hanako')
a.to_s

3) 子クラスのオブジェクトの作成とその内容の確認

b = B.new('taro', 10)
b.to_s

メソッド

オブジェクトのメソッドはクラス定義の中で行います。メソッドはオブジェクト(レシーバ)にメソッド名を送る形で実行されます。説明より使ってみる方が良く分かります。

1) メソッドの定義

class A3
  def mthd( n )
    puts "method - #{n}"
  end
end

2) オブジェクト作成

a = A3.new

3) メソッドの呼び出し

a.mthd(2)

クラス・メソッド

クラスメソッドはオブジェクトを作成しなくても利用できます。定義のとき名前の先頭にクラス名を置く必要があります

1) クラス・メソッドの定義

class A4
  def A4.msg
    puts "class method"
  end
end

2) クラス・メソッドの呼び出し

A4.msg

特異メソッド

特定のオブジェクトのメソッドだけを再定義できます。次のプログラムを順に irb に入力して確認してください。

1) クラス定義

class A5
  def bonus
    puts 40
  end
end

オブジェクトの作成

a = A5.new
b = A5.new

3) オブジェクト a の bonus メソッドを再定義

def a.bonus
  puts 100
end

4)a.bonus と b.bonus の値を表示

a.bonus
b.bonus

Proc オブジェクト(手続きオブジェクト、クロージャ)

Proc オブジェクトはブロック( {} でくくられたコードの塊)をオブジェクトにしたものです。Proc.new につづけて {} で囲んだコードを置いてオブジェクトを作ります。ブロックを実行するときは、オブジェクトに call メッセージを送ります。具体的には次のようにします。例題は全て一行プログラムです。

foo = Proc.new{ puts "hello" }
foo.call

Proc.new は次のようにも書けます。

bar = proc{ puts "world" }
bar.call

また、ブロックは引数をとらせることもできます。

foo = Proc.new{ |x| x * x }
foo.call(2)

foo.call(2) は省略して foo[2] と書くことができます。

foo[2]

ブロックに与える変数は複数でも構いません。

foo = Proc.new{ |x, y| x + y }
foo.call(1, 2)
foo[1, 2]

また、どんなメソッドもブロックを引数としてとることができます。メソッドの定義のときに引数に & をつけるとその変数は Proc オブジェクトと解釈されます。

def proctest( n, &x )
  print "the result is ", x.call(n), "\n"
end
proctest(2) { |x| x + 1 }
proctest(2) { |x| x * x }

引数に関数を渡す場合は Proc オブジェクトとして定義されたものを渡します。

begin
def func_A ( f )
  f.call(%w(hello, world))
end
func = Proc.new do |x|
  puts x
end
func_A( func )
end

演算子の定義

クラスに演算子を定義することができます。

1) クラスと演算子の定義

下のプログラムでは self で自分自身のオブジェクトのことを表します

class A6
  def initialize( name )
    @name = name
  end
  attr_accessor :name
  def +( other )
    A6.new( self.name + other.name )
  end
end

1) オブジェクト a とオブジェクト b を作成して c に a + b を代入します。

a = A6.new("hello")
b = A6.new(" world")
c = a + b
c.name

モジュールとインクルード

共通に使う定数や関数をモジュールにしておくと、include 文でそれらを利用することができます。モジュールはインスタンスを持たないので、a = Module.new などとしてオブジェクトを作ることはできません。

1) モジュールを定義する

module M
  CONST = "M"
  def hello
    "hello"
  end
end

2)クラスにモジュールを導入

class A7
  include M
end

3)クラス A7 のオブジェクトを作成

a = A7.new

4)導入した関数や定数を試す

a.hello
A7::CONST

例外処理

1) エラー処理

begin 〜 end ブロックの最期に rescue をいれることでエラーが起こったときにプログラムを中断せずに rescue 以下の自前のエラー処理を行うことができます。

begin # irb でこのプログラムを実行するときのおまじない
for i in 1..3
  begin #ここから次の end までに起こったエラーは ensure で捕捉できます
    print "file name = "
    name = gets.chop
    puts File.ftype(name)
  rescue
    print "ERROR: #$!\n"
  end
end
end

実行例

file name = /usr
directory
file name = usr
ERROR: No such file or directory - "usr"
file name = /etc/resolv.conf
file

1) ユーザによる例外の発生

raise 例外クラス, "メッセージの文字列" でユーザが例外を発生させることができます

begin # irb でこのプログラムを実行するときのおまじない
for i in 1..3
  begin #ここからつぎの end までがエラー処理の対象
    print "x = "
    x = gets.to_f
    raise RuntimeError, "division by zero" if x == 0
    puts 1 / x
  rescue
    print "ERROR: #$!\n"
  end
end
end

実行例

x = 2
0.5
x = 0
ERROR: division by zero
x = 3
0.3333333333

オブジェクトをファイルに保存する

Marshal モジュールを使うとオブジェクトをバイナリーの文字列に変換してファイルに記録することができます。バイナリーへの変換は dump メソッド、バイナリーからの復元は load メソッドを使います。ここではファイルではなく文字列にエンコーディングします。

irb(main):001:0> a = [[1,2],[3,4]]
[[1, 2], [3, 4]]
irb(main):002:0> tmp = Marshal.dump( a )
"\004\005[\a[\ai\006i\a[\ai\010i\t"
irb(main):003:0> b = Marshal.load( tmp )
[[1, 2], [3, 4]]

外部のファイルを読み込む

require で外部のファイルを読み込んで利用することができます。

require "complex"
a = Complex(1, 1)
a.to_s

スレッドを走らせる

その1

begin
t = Thread.new do
  sleep(2)
  puts "thread end"
end
while t.alive?
  puts "main thread"
end
end

その2

begin
t1 = Thread.new do
  sleep(3)
  puts "thread 1 end"
end
t2 = Thread.new do
  sleep(2)
  puts "thread 2 end"
end
t1.join
t2.join
end

GUIをつくる

Ruby/Tk が標準でついているので GUI のプログラムも作れます。

begin
require 'tk'
TkButton.new(nil,'text'=>'Exit','command'=>proc{exit}).pack
Tk.mainloop
end

GNUPLOT を Ruby から利用する

begin
gp = IO.popen("gnuplot -persist", "w")
gp.puts 'plot "-" w l'
(-1).step(1, 0.05) do |x|
    gp.puts "#{x} #{x * x}"
end
gp.puts "end"
gp.close
end