Rubyトレーニング
日曜プログラマーの悩みは、新しいプログラム言語を勉強したときにしばらくするとすっかり忘れてしまうと言うことです。ここでは、Ruby の使い方を思い出すための小さなプログラムを集めました。これらのプログラムはわざわざファイルを作成しなくても irb のプロンプトから直接実行することができますから、何度でも手軽に試してみることができます。プログラムの解説は省きますが、irb を起動した後入力して試してみてください。習うより慣れよ。irb でプログラムを動かしているうちに Ruby 本を読みこなすコツが身につくとおもいます。
注:ここに紹介したスクリプトは全て irb のコンソールから対話的に実行するように想定しています。irb では一つの文をすぐに実行するので、複数の文からなるスクリプトを実行したい場合に困る時があります。紹介したスクリプト中の赤字で示した begin と end は、複数の文をブロックにまとめるために使っていますので、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
4) 子クラスのオブジェクトの作成とその内容の確認
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
2)オブジェクトの作成
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
2) オブジェクト 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