日曜プログラマーの悩みは、新しいプログラム言語を勉強したときにしばらくするとすっかり忘れてしまうと言うことです。ここでは、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 $
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
begin a = 1 if a == 0 puts "A" elsif a==1 puts "B" else puts "C" end end
その1
for i in 1..3 puts i end
その2
for i in ['dog', 'cat'] puts i end
begin i = 0 while i < 4 puts i i += 1 end end
begin i = 3 begin puts i i -= 1 end while i > 0 end
begin i = 0 until i > 4 puts i i += 1 end end
begin a = 'cat' case a when 'dog' puts 'A' when 'cat' puts 'B' else puts 'C' end end
break でループを抜けます
for i in 1..10 if i == 5 break else puts i end end
next でループの最期にジャンプ
for i in 1..10 if i%2 != 0 next else puts i end end
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.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
Ruby/Tk が標準でついているので GUI のプログラムも作れます。
begin require 'tk' TkButton.new(nil,'text'=>'Exit','command'=>proc{exit}).pack Tk.mainloop end
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