Ruby(ルビー)は、まつもとゆきひろ(通称: Matz)により開発された、オブジェクト指向スクリプト言語。
概要 Rubyは、1993年2月24日に生まれ、1995年12月にfjで発表された。
「命名の一番大きな理由は、名前の短い美しい宝石だから」https://x.com/yukihiro_matz/status/20225829731457926372000年代は「真珠(Pearl)の次の誕生石がルビー」説が有力だった。
2011年3月22日、「JIS X 3017」に制定。
2012年4月1日、「ISO/IEC 30170」に承認、日本人が開発したプログラミング言語では初めて、国際電気標準会議(IEC)に認証された。
2025年9月現在、言語仕様は明文化されていない「ISO/IEC 30170」による厳密な言語仕様はない。2010年6月以降、言語仕様をテストするRubySpecという外部ライブラリの開発が行われていたが、Ruby 2.2.0リリース直後の2014年12月31日、開発が突然終了された。Ruby 2.2.0が「RubySpecのテストをパスしない」等、MRI側によるRubySpec軽視が一因とされている。。
文字列・数値等すべてのデータをオブジェクトとして扱う。
クラス、ガベージコレクション、例外処理、Mixin、正規表現等の基本機能に、別途機能をRubyGemsで追加可能。
バージョン1.9.3以降、BSDとのデュアルライセンスで頒布されている。
設計思想 なぜ、Rubyを開発したのか。
Rubyの言語仕様策定において、最も重視しているのは、ストレスなくプログラミングを楽しむことである/enjoy programming - 「まえがき 監修者」より。
クラスと定数の先頭をアルファベット大文字に限っている言語仕様で日本語のクラス名が付けられないという意見に対して、Matzは次のとおり説明している。
静的型について、Matzは(基本設計思想である動的型付けを維持し)『静的型付けを導入しない』意思を改めて示した。
MINASWAN「Matzがniceだから俺らもniceでいよう」
実装 公式
MRI Matzによる実装。MRI(Matz Ruby Interpreter)またはCRuby。ソースコードを構文木にコンパイルした後、evalで実行するインタプリタ。1.8以前を指すこともある。
YARV 1.9で採用されたYARVは、#MRIのevalをバイトコード実行に置き換えたもの。ソースコードをバイトコードにコンパイルし実行するインタプリタ。
その他
JRuby JRuby - Javaによる実装。Rubyと互換性が高い。インタプリタ/実行時コンパイラ/事前コンパイラがある。事前コンパイラでは(JRubyがなくても)、他のJavaプラットフォーム上で動作可能。
IronRuby IronRuby - .NET Frameworkによる実装。.NET Frameworkのライブラリと連携可能。JIT方式のバイトコードインタプリタ。共通言語基盤に準拠した実装(Monoなど)で動作するため、プラットフォーム非依存の利用も可能。ただし、.NET Frameworkのライブラリに依存している場合、Monoでの動作は不可能。
mruby mruby - 組み込みシステム向けの軽量版。家電製品、スマートフォン、コンピュータゲームなどでの使用を想定。
仮想マシン 仮想マシン上で動作する実装
GraalVM GraalVMによる実装 truffleruby
Parrot Parrotによる実装 Cardinal - Rubyコンパイラ
コード例 ここでは、実用途に即したコード例を中心に記載する。クロスプラットフォームでの使用を考慮しているが、Unix系コマンドを利用できない環境では正常に動作しないことがある。なお、ウィキブックスには、より多くの基本的なコード例が記載されている。
はじめに すべてのデータがオブジェクト I1 = 123
p f1 = I1.to_f #=> 123.0 浮動小数点数に変換 p i1 = I1.to_i #=> 123 整数に変換 p s1 = I1.to_s #=> "123" 文字列に変換
p -f1.abs #=> 123.0 p -i1.abs #=> 123 p s1.length #=> 3 p s1.index("1") #=> 0
メソッドチェーン メソッドを.で連結し順に処理パイプも参考のこと。 # 1行表示 p "Rubyは楽しい?" #=> "Rubyは楽しい?" p "Rubyは楽しい?".length #=> 9 p "Rubyは楽しい?".gsub("?", "!!") #=> "Rubyは楽しい!!" p "Rubyは楽しい?".gsub("?", "!!").length #=> 10
# 複数行表示 p "Rubyは楽しい?" .gsub("?", "!!") # "Rubyは楽しい!!" .length #=> 10
# 参考 p "Rubyは楽しい?" .split(//) # 配列に変換 .map{|v| v.sub("?", "!!")} # 配列要素に対して処理/gsub()でなくsub()であることに留意 .join("") # 配列から文字列に変換 .length #=> 10
コメントアウト #で始まる行は、1行コメント。 # コメント1 # コメント2
=begin ... =end行は、複数行コメント。 =begin コメント1 コメント2 =end
変数と定数 ローカル変数 小文字 または '_' で始まる識別子は ローカル変数 var = 123 p var = 456 #=> 456 _v = 123 p _v = 456 #=> 456
グローバル変数 '$' で始まる識別子は グローバル変数 $var = 123 p $var = 456 #=> 456
定数 アルファベット大文字 [A-Z]で始まる識別子は 定数 Var = 123 p Var = 456 #=> 警告: 定数 Var はすでに初期化されています
日本語による表記例 # 変数 p 変数 = "これは変数です" p 変数 = "変数なので代入可能です"
# グローバル変数 p $変数 = "これはグローバル変数です" p $変数 = "グローバル変数なので代入可能です"
# 定数 p S定数 = "これは定数です" p S定数 = "定数なのでエラーが発生します"
コマンドライン引数 ARGV # 本コードを「aaa.rb」として保存し、 # $ ruby aaa.rb -help -title="Hello, Ruby!" "Ruby was born in 1993." # を実行したとき。
p ARGV #=> ["-help", "-title=Hello, Ruby!", "Ruby was born in 1993."]
ARGV.each do |_s1| # オプションあり if _s1 =~ /^-[a-z]/ _k, _v = _s1.split("=") # 引数あり if _v puts "オプションあり/引数あり:#{_k} = #{_v}" # 引数なし else puts "オプションあり/引数なし:#{_k}" end # オプションなし else puts "オプションなし:#{_s1}" end end #=> # オプションあり/引数なし:-help # オプションあり/引数あり:-title = Hello, Ruby! # オプションなし:Ruby was born in 1993.
配列 配列の作成と使用法 # 数値と文字列が混在する配列 A1 = [1, "UPCASE", 3.14, 1, 2, [2.0, 4], "downcase"]
# 要素を取得 p A1 #=> 3.14 p A1[0..2] #=> [1, "UPCASE", 3.14] p A1[0, 2] #=> [1, "UPCASE"]
# 大文字・小文字を区別しないソート p A1.sort_by{|v| v.to_s.downcase} #=> [1, 1, 2, 3.14, [2.0, 4], "downcase", "UPCASE"] p A1.flatten.sort_by{|v| v.to_s.downcase} #=> [1, 1, 2, 2.0, 3.14, 4, "downcase", "UPCASE"] p A1.flatten.uniq.sort_by{|v| v.to_s.downcase} #=> [1, 2, 2.0, 3.14, 4, "downcase", "UPCASE"]
# いずれも以下を出力 # ["A", "B C", "D"]
# 文字列を配列に変換 S1 = "A \n B C\nD\n" p S1.split("\n").map{|v| v.strip}
# ヒアドキュメントを配列に変換 S2 = <<EOD A B C D EOD p S2.each_line.map{|v| v.strip}
# %w記法で配列に変換 p %w(A B\ C D)
p %w( A B\ C D )
%w記法で都道府県コードの配列を作成する例 # [1..47] = ["北海道".."沖縄県"](一部抜粋) p Apref = [nil] + %w(北海道 青森県 岩手県 宮城県) #=> [nil, "北海道", "青森県", "岩手県", "宮城県"] printf("%02d: %s\n", 4, Apref) #=> "04: 宮城県"
上記コードをクラス化する例(#現実世界の情報も参照のこと) class Pref def initialize() @aPref = [nil] + %w(北海道 青森県 岩手県 宮城県) end
def explain(id = 0) sprintf("%02d: %s", id, @aPref[id]) end end
pref = Pref.new
puts pref.explain(4) #=> "04: 宮城県"
ハッシュ ハッシュの作成と使用法 h10 = {} h10["water"] = "wet" h10["fire"] = "hot" h20 = {"water" => "wet", "fire" => "hot"} h31 = {:water => "wet", :fire => "hot"} h32 = {:"water" => "wet", :"fire" => "hot"} h41 = {water: "wet", fire: "hot"} h42 = {"water": "wet", "fire": "hot"}
# いずれも以下を出力 # "hot" p h10["fire"] p h20["fire"] p h31[:fire] p h32[:"fire"] p h41[:fire] p h42[:"fire"]
# いずれも以下を出力 # water: wet # fire: hot h10.each { |_k, _v| print _k, ": ", _v, "\n" } h20.each { |_k, _v| print _k, ": ", _v, "\n" } h31.each { |_k, _v| print _k, ": ", _v, "\n" } h32.each { |_k, _v| print _k, ": ", _v, "\n" } h41.each { |_k, _v| print _k, ": ", _v, "\n" } h42.each { |_k, _v| print _k, ": ", _v, "\n" }
四則演算など 加法 + 減法 − 乗法 * 除法 / 冪乗 **
数値 整数と浮動小数点数を意識して使い分ける必要がある。 p 1 + 2 #=> 3 p 1 - 2 #=> -1 p 1 * 2 #=> 2 p 1 / 2 #=> 0 期待値は 0.5 だが? p 1.0 / 2 #=> 0.5 期待どおり。 p 1 ** 2 #=> 1
文字列 p "Ru" + "by" #=> "Ruby"
配列 p ["Ru"] + ["by"] #=> ["Ru", "by"] p ["Ru", "by"] - ["Ru"] #=> ["by"] p [1, 2, 3] * ":" #=> "1:2:3"/[1, 2, 3].join(":") と同義
制御構造 ほかの言語でもよくみられるような制御構造を用いることができる。
条件分岐 if, case # [Ctrl]+[C] Signal.trap(:INT) do puts exit end
# キー入力 print "[Y/n] ? " p s1 = STDIN.gets.strip.upcase
print "例1-1: " if s1 == "Y" puts true elsif s1 == "N" puts false else puts nil end
print "例1-2: " puts( if s1 == "Y" true elsif s1 == "N" false else nil end )
print "例2: " puts s1 == "Y" ? true : nil
print "例3: " print true if s1 == "Y" puts
print "例4: " puts( case s1 when "Y" true when "N" false else nil end )
ブロック ブロックは { ... } または do ... end によって囲まれたコード列である。 一行で収まるときは { ... }、複数行にまたがるときは do ... end が使用される。
# { ... } "Hello, Ruby!".split { |_s1| puts _s1 }
# do ... end "Hello, Ruby!".split do |_s1| puts _s1 end
gsub() の罠 S1 = "ABC-ABC"
# ok # 期待どおり => "AびC-AびC" p S1.gsub("B", "び")
# NG # 期待 => "AびBC-AびBC" # 結果 => "AびC-AびC" p S1.gsub(/(B)/, "び#{$1}")
# ok # 期待どおり => "AびBC-AびBC" p S1.gsub(/(B)/){ "び#{$1}" }
繰り返し処理 for a1 = [1, 2, 5, 13, 21] a2 = [] for i1 in a1 a2 << i1 * 2 end p a2 #=> [2, 4, 10, 26, 42]
while a1 = [1, 2, 5, 13, 21] i1 = 0 while i1 < a1.length a1[i1] *= 2 i1 += 1 end p a1 #=> [2, 4, 10, 26, 42]
each メソッド a1 = [1, 2, 5, 13, 21] a2 = []
a1.each { |i1| a2 << i1 * 2 } p a1 #=> [1, 2, 5, 13, 21] p a2 #=> [2, 4, 10, 26, 42]
a1.each.with_index(0) { |i1, i2| a1[i2] = i1 * 2 } p a1 #=> [2, 4, 10, 26, 42]
map, map! メソッド a1 = [1, 2, 5, 13, 21]
# map a2 = a1.map { |i1| i1 * 2 } p a1 #=> [1, 2, 5, 13, 21] p a2 #=> [2, 4, 10, 26, 42]
# map! a1.map! { |i1| i1 * 2 } p a1 #=> [2, 4, 10, 26, 42]
times メソッド a1 = [1, 2, 5, 13, 21] a1.length.times { |i1| a1[i1] *= 2 } p a1 #=> [2, 4, 10, 26, 42]
指定した回数の繰り返し処理 # 以下、いずれも "foofoofoo" を出力。 s1 = "foo"
3.times { print s1 } puts
print (s1 * 3), "\n"
連続する数字の繰り返し処理 # 連続する数字の配列を作成 p (1..5).to_a #=> [1, 2, 3, 4, 5]
# 断続する数字をキーにした空のハッシュを作成 hi1 = {} [0..5, 10, 20..21, 30].each do |e1| if e1.class == Range e1.each do |i1| hi1[i1] = "" end else hi1[e1] = "" end end p hi1 #=> {0=>"", 1=>"", 2=>"", 3=>"", 4=>"", 5=>"", 10=>"", 20=>"", 21=>"", 30=>""} p hi1 #=> "" p hi1 #=> nil
# 上記コードをワンオフ向けに特化した例 hi1 = {} [(0..5).to_a, 10, (20..21).to_a, 30].flatten.each do |i1| hi1[i1] = "" end p hi1 #=> {0=>"", 1=>"", 2=>"", 3=>"", 4=>"", 5=>"", 10=>"", 20=>"", 21=>"", 30=>""}
ファイルの読み書き テキストファイル # 入力ファイル IFn = __FILE__
# 出力ファイル OFn = "output.txt"
s1 = "" s2 = ""
# メモリに余裕があるなら、全行読み込んで処理。 File.read(IFn).each_line do |_s1| if _s1.match("全行") s1 << _s1 end end
# メモリに余裕がないときは、一行ずつ読み込んで処理。 File.open(IFn, "r") do |_fs| _fs.each_line do |_s2| if _s2.match("一行ずつ") s2 << _s2 end end end
# 書き込み File.write(OFn, s1)
# 追加書き込み File.open(OFn, "a") do |_fs| _fs.write s2 end
# OS判定 # Cドライブが存在すれば Windows # 上記以外は Unix系 Cmd = Dir.exist?("c:/") ? "type" : "cat"
puts %x(#{Cmd} #{OFn})
バイナリファイル テキストファイルと異なるメソッド等 File.read(... => File.binread(... File.open(..., "r") => File.open(..., "rb") File.open(..., "w") => File.open(..., "wb") File.open(..., "a") => File.open(..., "ab")
その他、マルチバイト文字を扱うときは、エンコーディングが必要になる。
URL http経由でテキストデータを読み込む例(#例外処理、#gsub() の罠も参照のこと) require 'open-uri'
# URL Url = "https://ja.wikipedia.org/w/index.php?title=Ruby&action=history&offset=&limit=5"
# 検索文字列 Search = "「Ruby」の変更履歴"
begin # 全行をメモリに先読みした後、一行ずつ処理する。 URI.open(Url).read.each_line.with_index(1) do |_s1, _i1| # 文字列検索 if _s1.match(Search) _s1.strip! print( # 行番号 "\033[94mL#{_i1}\033[0m\t", # 一致した文字列を色付け表示 _s1.gsub(Search, "\033[95m#{Search}\033[0m"), "\n" ) end end rescue => e p e end
正規表現 正規表現による検索例
# 検索対象文字列 p S1 = "12345 ABCdef あいうえおかきくけこ"
# 検索文字列(正規表現) p Rgx1 = /(\d{3}).*?([A-z]+).*?(あ.+お)/
# 検索実行 if S1 =~ Rgx1 # $& で結果取得 print "全体: ", $&, "\n" #=> "12345 ABCdef あいうえお" # $~[1..] で部分一致の配列取得 print "部分: ", $~[1..], "\n" #=> ["123", "ABCdef", "あいうえお"] end
クラス
既存クラス 既存のStringクラスのメソッドの「書き換え」と「新規追加」を行う例 class String # 既存メソッドの書き換え/危険!! def to_s self.swapcase end
# 新規メソッド追加 def foo self.split(//).join(" ") end end
s1 = "Hello, Ruby!" puts s1 #=> "Hello, Ruby!" puts s1.to_s #=> "hELLO, rUBY!"/本来は "Hello, Ruby!" を表示 puts s1.foo #=> "H e l l o , R u b y !"
現実世界の情報 「動物(Animal)」の「種類(breed)」と「鳴き声(bark)」をクラス化する例 class Animal def initialize(breed, bark) @breed, @bark = breed, bark end
def explain "#{@breed}は「#{@bark}」と鳴く。" end end
#(例1)定数に格納 # 静的データ限定。
Dog = Animal.new("犬", "わんわん") Cat = Animal.new("猫", "にゃー")
puts Dog.explain #=> "犬は「わんわん」と鳴く。" puts Cat.explain #=> "猫は「にゃー」と鳴く。"
#(例2)ハッシュに格納 # 動的データを扱うとき、使い勝手がよい。
List1 = <<EOD Dog1 犬1 わんわん Cat1 猫1 にゃー EOD
hAnimal = {}
List1.each_line do |_s1| _key, _breed, _bark = _s1.strip.split("\t") hAnimal[_key] = Animal.new(_breed, _bark) end
puts hAnimal["Dog1"].explain #=> "犬1は「わんわん」と鳴く。" puts hAnimal["Cat1"].explain #=> "猫1は「にゃー」と鳴く。"
#(例3)evalで動的に生成した定数に格納 # 参考程度。
List2 = <<EOD Dog2 犬2 わんわん Cat2 猫2 にゃー EOD
List2.each_line do |_s1| _key, _breed, _bark = _s1.strip.split("\t") eval "#{_key} = Animal.new('#{_breed}', '#{_bark}')" end
puts Dog2.explain #=> "犬2は「わんわん」と鳴く。" puts Cat2.explain #=> "猫2は「にゃー」と鳴く。"
仮想データ コマンドラインインタプリタ上のカーソル操作をクラス化する例 class Cursor def up(n) (n - 1).times { print "*\033[1D\033[1A" } print "*\033[1D" sleep 1 end
def down(n) (n - 1).times { print "*\033[1D\033[1B" } print "*\033[1D" sleep 1 end
def right(n) n.times { print "*\033[1C" } print "\033[2D" sleep 1 end
def left(n) n.times { print "*\033[3D" } print "\033[2C" sleep 1 end end
cursor = Cursor.new cursor.down(6) cursor.right(6) cursor.up(4) cursor.left(4) cursor.down(4) puts
インスタンス変数へアクセス クラス内のインスタンス変数 @foo にアクセスするための定義例 attr_reader 読込メソッド attr_writer 書込メソッド attr_accessor 読込/書込メソッド
class Gemstone def initialize(id = 0, name = "") @id, @name = id, name end
# 読込メソッド生成 @id, @name attr_reader :id, :name # def id # @id # end # def name # @name # end
# 書込メソッド生成 @name attr_writer :name # def name=(name) # @name = name # end
# 上記は、 # attr_reader :id # attr_accessor :name # とも書ける。 end
# 初期化 @id, @name gemstone = Gemstone.new(123, "?") p gemstone.id #=> 123 p gemstone.name #=> "?"
# データ書き換え @name gemstone.name = "Ruby" p gemstone.name #=> "Ruby"
例外処理 想定外のエラーが発生したとき、エラーを分別して実行を継続する。 begin # 処理 rescue error_type => e # error_type の例外あり p e rescue => e # 例外あり p e else # 例外なしのとき実行 ensure # 必ず実行 end
例外なし # 下記を出力 # Hello Ruby! # else # ensure begin puts "Hello Ruby!" rescue => e puts "rescue", e else puts "else" ensure puts "ensure" end
例外は raise ... でも発火できる。
例外あり raise "エラー" # 下記を出力 # rescue # エラー # ensure begin raise "エラー" rescue => e puts "rescue", e else puts "else" ensure