Ruby

オブジェクト指向プログラミング言語 From Wikipedia, the free encyclopedia

Ruby(ルビー)は、まつもとゆきひろ(通称: Matz)により開発された、簡潔な文法が特徴的なオブジェクト指向スクリプト言語[3]

登場時期 1995年 (31年前) (1995)
最新リリース 4.0[1] ウィキデータを編集/ 2025年12月25日 (2か月前)
概要 パラダイム, 登場時期 ...
閉じる

カテゴリ / テンプレート

概要

Rubyは、1993年2月24日に生まれ、1995年12月にfjで発表された。

6月の誕生石Pearl(真珠)と同じ発音をするPerlに続くという意味で、7月の誕生石Ruby(ルビー)に由来する。

2011年3月22日、「JIS X 3017」に制定。

2012年4月1日、「ISO/IEC 30170」に承認、日本人が開発したプログラミング言語では初めて、国際電気標準会議(IEC)に認証された[4][5]

2025年9月現在、言語仕様は明文化されていない[注釈 1]

文字列・数値等すべてのデータをオブジェクトとして扱う。

クラスガベージコレクション例外処理Mixin正規表現等の基本機能に、別途機能をRubyGemsで追加可能。

バージョン1.9.3以降、BSDとのデュアルライセンスで頒布されている。

設計思想

なぜ、Rubyを開発したのか[6]

そのように問われるときに、もっとも適切な答えは、Linux開発者であるリーナス・トーバルズの言葉と同じではないかと思います。

それがぼくには楽しかったから

Rubyの言語仕様策定において、最も重視しているのは、ストレスなくプログラミングを楽しむことである/enjoy programming[7]

Ruby には Perl や Python とは決定的に違う点があり、それこそが Ruby の存在価値なのです。それは「楽しさ」です。私の知る限り、Ruby ほど「楽しさ」について焦点を当てている言語は他にありません。Ruby は純粋に楽しみのために設計され、言語を作る人、使う人、学ぶ人すべてが楽しめることを目的としています。しかし、ただ単に楽しいだけではありません。Ruby は実用性も十分です。実用性がなければ楽しめないではありませんか。

クラスと定数の先頭をアルファベット大文字に限定した仕様(基本設計思想)について、Matzは次のとおり説明している[8]

2文字目以降は自由なので、もしどうしても日本語が使いたいのであれば、少々不自然にも見えますが、先頭だけ大文字の接頭辞をつけるのはどうでしょうか。しかし、私個人としては、日本語の変数名などを使うことは、そのプログラムを読む人の範囲を日本語が読める人に限定してしまうことになるので、ひどくもったいないのではないかと感じています。そこで、この点を積極的に改善する気にはなれないのです(#日本語による表記例も参照のこと)。

静的型について、Matzは(基本設計思想である動的型付けを維持し)『静的型付けを導入しない』意思を改めて示した[9]

Rubyと同様のスクリプト言語(インタープリタ型言語)であるPythonPHPPerlでは静的型を導入しており、Rubyも型を導入するべきでは、と長年言われてきました。しかしまつもとさんは「Rubyに型を取り入れたくない(DRYではないから⁠[注釈 2])⁠」⁠、「⁠型宣言することはコンピュータに使われているような気になる」と述べ、Rubyでは静的型は導入しない方針を示しました。

その代わり、静的解析を導入[10]し、型チェックを行えるようにすることを明らかにしました。(中略)静的解析に期待してほしい、とまつもとさんは述べました。

MINASWAN「Matzがniceだから俺らもniceでいよう」[11]

そしてRuby(中略)コミュニティを表現した標語に"MINASWAN"というものがあると紹介しました。これは"Matz is nice so we are nice"の略で、もめごとがあっても「matzがniceだから俺らもniceでいよう」と、なだめることが海外のメーリングリストではよく見られるそうです。(中略)今ではスローガンにまで昇華しています。(中略)日本のRubyコミュニティにこの特質を逆輸入したいと考え、講演の最初に取り上げたと述べました。

実装

公式

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による実装

Parrot

Parrotによる実装

コード例

ここでは、実用途に即したコード例を中心に記載する。クロスプラットフォームでの使用を考慮しているが、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

メソッドチェーン

メソッドを.で連結し順に処理[12]

p S1 = "Rubyは楽しい?"                             #=> "Rubyは楽しい?"
p S1.split(//)                                     #=> ["R", "u", "b", "y", "は", "楽", "し", "い", "?"]
p S1.split(//).map{|v| v.gsub("?", "!!")}          #=> ["R", "u", "b", "y", "は", "楽", "し", "い", "!!"]
p S1.split(//).map{|v| v.gsub("?", "!!")}.join("") #=> "Rubyは楽しい!!"

以下のように書くこともできる。

p "Rubyは楽しい?"
    .split(//)
    .map{|v| v.gsub("?", "!!")}
    .join("")
#=> "Rubyは楽しい!!"

コメントアウト

#で始まる行は、1行コメント。

# コメント1
  # コメント2

=begin ... =end行は、複数行コメント。

=begin
コメント1
  コメント2
=end

おまじない

ヘッダ情報の付与

#!/usr/bin/env ruby
#coding:utf-8

puts "本コードは、これ以降に書く。"
1行目:シバン行。使用するインタプリタを記述(Windows系実装では無視される)。
2行目:使用する文字コードを記述。ここではutf-8を指定。

変数と定数

ローカル変数

小文字 または '_' で始まる識別子は ローカル変数

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[2]    #=> 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[4])                 #=> "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[0]  #=> ""
p hi1[99] #=> 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
  puts "ensure"
end

例外あり raise ArgumentError, "Argument エラー"

# 下記を出力
#   ArgumentError rescue
#   Argument エラー
#   ensure
begin
  raise ArgumentError, "Argument エラー"
rescue ArgumentError => e
  puts "ArgumentError rescue", e
rescue => e
  puts "rescue", e
else
  puts "else"
ensure
  puts "ensure"
end

外部コマンド等の利用

Ruby on Railsが有名になったため、Rubyを書いたことがない人は「Rubyは敷居が高い」と敬遠するかもしれないが、それは誤解である。Rubyは「小さなことを少しの努力/Doing small things with little effort」で実装できる言語の一つであり、ちょっとしたプロトタイピング開発やシェルスクリプトの代替に向いている。

「少ない労力でより多くの成果を/Do more with less」

以下、外部コマンド等(コマンド実行ファイルスクリプト言語)のうち、Unix系コマンドを利用したコード例を記す。

(例1)実行/成功可否(true, false)を取得。

bool = system("...")

falseコマンドを実行

p b1 = system("false") #=> false
if ! b1
  STDERR.puts "FALSE"  #=> "FALSE"
end

(例2)実行/結果を取得。

string = %x(...)

echoコマンドを実行

p s1 = %x(echo)       #=> "\n"
if s1.strip.length == 0
  STDERR.puts "EMPTY" #=> "EMPTY"
end

実行中のRubyスクリプトのソースコード各行に連番を付与

print %x(cat #{__FILE__} | nl -ba -w1)

(参考)Rubyのみで実装

File.read(__FILE__).each_line.with_index(1) do |_s1, _i1|
  print _i1, "\t", _s1
end

ホームディレクトリの隠しファイルを再帰抽出し、ディレクトリとファイルを配列にする例

a1 = []

# "~/" を絶対パス "/home/foo" に変換
sAbsPath = File.expand_path("~/")

%x(find #{sAbsPath} -type f -name ".*").each_line do |_s1|
  _s1.chomp!
  # ファイル名直前の "/" 位置
  i1 = _s1.rindex("/", -1)
  a1 << [_s1[..(i1 - 1)], _s1[(i1 + 1)..]] #=>(例)["/home/foo", ".bashrc"]
end

# ディレクトリ順にソート
a1.sort.each { |_a1| print(_a1, "\n") }

(参考)Rubyのみで実装

# 直感的な理解を期待し、敢えて File.basename(), File.dirname() を使用した。
a1 = []

# "~/" を絶対パス "/home/foo" に変換
sAbsPath = File.expand_path("~/")

Dir.glob("**/*", File::FNM_DOTMATCH, base: sAbsPath).each do |_fn|
  sPath = File.join(sAbsPath, _fn)
  # ファイルか?
  if FileTest.file?(sPath)
    sFn = File.basename(sPath)
    # "." で始まるファイル名か?
    if sFn[0] == "."
      a1 << [File.dirname(sPath), sFn] #=>(例)["/home/foo", ".bashrc"]
    end
  end
end

# ディレクトリ順にソート
a1.sort.each { |_a1| print(_a1, "\n") }

並列処理

Parallel gem[13] で並列処理する例

出力結果は「処理が完了する順序で異なる」ことに注意。

配列による実装例

require 'parallel'

CmdList = ["sleep 2", "ls -la", "sleep 8"]

# Windows版Ruby3.4現在、オプション in_processes: 未対応のようなので in_threads: を使用した。
Parallel.each(CmdList, in_threads: 4) do |_cmd|
  system(_cmd.strip)
end

ヒアドキュメントによる実装例

require 'parallel'

CmdList = <<EOD
	# Ruby
	ruby -e 'print "Hello, Ruby!\\n"'
	# Perl
	perl -e 'print "Hello, Perl!\\n";'
	# Python
	python3 -c "print('Hello, Python!')"
EOD

Parallel.each(CmdList.each_line, in_threads: 4) do |_cmd|
  system(_cmd.strip)
end
eval

Rubyスクリプトを動的に評価/実行するときは eval で実装(#例外処理も参照のこと)

require 'parallel'

def SubEval(_sec = 0)
  sleep _sec # _sec 秒停止
  puts _sec  # _sec を表示
end

# 便宜上、ヒアドキュメントで記述します。
# 実用途では、標準入力/外部ファイルから読み込んだテキストデータを想定しています。
# 以下、意図的にエラーを混入しているので、実行して確認してみてください。
CmdList = <<EOD
	SubNoExist()
	SubEval("Ruby")
	SubEval(0)
	SubEval(10)
	SubEval(5)
	SubEval(1)
	SubEval(2)
EOD

# 入力データに100%の信用がないときは、例外処理を実装。
Parallel.each(CmdList.each_line, in_threads: 4) do |_cmd|
  _cmd.strip!
  begin
    eval(_cmd)
  rescue => e
    puts "#{_cmd} => #{e}"
  end
end

フィボナッチ数

フィボナッチ数を求める例[14]。適正なアルゴリズムを使用することで処理速度が改善される事例は多い。

# 例えば、以下のコードが、"fib.rb"に保存されているとき、
# $ ruby ./fib.rb 10
# と実行。

def RtnFibIntr(num = 0)
  if num == 0
    return [1, 2]
  end

  if (num & 1) == 0
    numHalf = (num / 2).to_i
    iPm = ((numHalf & 1) == 0 ? 1 : -1)
    f1, l1 = RtnFibIntr(numHalf)
      l2 = (l1 * l1) - (2 * iPm)
      f2 = (f1 * l1) - iPm
  elsif (num % 8) == 7
    f1, l1 = RtnFibIntr(num + 1)
      f2 = (2 * f1) - l1
      l2 = (3 * f2) - f1
  else
    f1, l1 = RtnFibIntr(num - 1)
      f2 = (3 * f1) - l1
      l2 = (2 * f2) - f1
  end

  return [f2, l2]
end

def SubFib(num = 0)
  if num > 0
    print num, "\t", RtnFibIntr(num - 1)[0], "\n"
  end
end

def main()
  if ARGV.length > 0
    i1 = ARGV[0].to_i
    SubFib(i1)
  end
end

main()

不向きな処理

ベンチマークテストで使用される以下のようなコードを実行したとき、処理速度が著しく低下することがある。

i1 = 1000000
while i1 <= 1010000
  i2 = i1 - 1
  i3 = 2
  while i3 <= i1
    if (i1 % i3) == 0
      break
    elsif i3 == i2
      puts i1.to_s
      break
    end
    i3 += 1
  end
  i1 += 1
end

(参考)上記コードをC言語で記述した例(#外部コマンド等の利用も参照のこと)。

// 本コードを「aaa.c」として保存したとき、
// コンパイル例
//   $ gcc ./aaa.c -o ./aaa.exe
// 実行例
//   $ ./aaa.exe

#include <stdio.h>

int main()
{
  unsigned int i1 = 1000000;
  while(i1 <= 1010000)
  {
    unsigned int i2 = i1 - 1;
    unsigned int i3 = 2;
    while(i3 <= i1)
    {
      if((i1 % i3) == 0)
      {
        break;
      }
      else if(i3 == i2)
      {
        printf("%u\n", i1);
        break;
      }
      i3 += 1;
    }
    i1 += 1;
  }
  return 0;
}

Rubyの周辺技術

Rubyで開発されたアプリケーション

Rubyを組み込んだアプリケーション

  • RPGツクールXP
  • RPGツクールVX
RPGツクールXP(2004年)とRPGツクールVX(2007年)に、Rubyをツクール専用にカスタマイズしたRGSSを搭載した。これにより、戦闘システム等を一から構築できた[15]

エピソード

ブロック構造構文の選択理由

Ruby ではブロック構造を end で終える構文が採用されているが、Matzは他の構文が採用される可能性があったことを述べている。当時、Emacs 上で end で終える構文をオートインデントさせた例はあまりなく、Ruby 言語用の編集モードにオートインデント機能を持たせられるかどうかが問題になっていたためである[注釈 3]。実際には数日の試行でオートインデント可能であることがわかり、現在の構文になった。C言語のような{ ... }を使った構文も検討されていたが、結局これは採用されなかった[17]

Matzが書いたコードの割合

「Quora (2020年9月8日)」より抜粋し補足[18]

Rubyにおける標準的な実装であるCRubyでは、はもう10年近く前(2010年頃)から主要な開発者としては脱落しています。言語デザイナーとしての活動がメインということですね。では、現在(2020年9月8日)のCRubyのソースコードに(往年の)私のコードがどのくらい残っているのか確認してみましょう。(中略)結果、現状のCコード509,802行(2020–09–08現在)のうち、私のコミットした行は、36,437行残ってました。1割も残っていませんが、正直なところ意外と多いなという印象です。一方、最近私がプログラマーとして関わっているmrubyで同様の方法で測定したところ、67,068行中、25,049行でした。mrubyでは他に代理コミットしてもらったぶんがありますから、それを加えると32,653行で、約半分ってところですかね。

Matzとゆかりのある地域

言語間競争とRuby

Python に満足していれば Ruby は生まれなかった

(1993年頃)PerlPython(0.9x?)は、Matzが理想とするオブジェクト指向モデルを実現するに至っていなかった[21]

「matz[注釈 4]がPythonに満足していればRubyは生まれなかったであろう」[22]

Ruby on Rails が Python ではなく Ruby で作られた理由 - 開発者のハンソンがRubyに恋をしたから

デイヴィッド・ハイネマイヤー・ハンソンが(2004年頃)Ruby on Railsを構築するのにPython(2.x?)を選ばなかった理由として「私の場合は、恋に落ちたのがRubyなのです。私はRubyに恋をしていますし、もう14年間もそうなのです。(中略)『最適なツール』などというものは存在しないのです。あなたの脳をちょうどいい具合に刺激するパズルがあるだけなのです。今日では、ほぼなんでも作ることができます。そして、それを使って、さらに何でも作れてしまうのです。これは素晴らしいことです。表現や言語、そして思考の多様性に乾杯しましょう!」と質問サイトのQuoraで本人が回答している[18]

「Rubyは死んだ」のか? 検索ワード頻度では分からない Ruby の生産性やビジネス上の価値

「Ruby Business Users Conference 2018 Winter」(2018年12月14日)より抜粋[23]

RubyとかRuby on Railsだと、簡単なWebアプリケーションをすぐ作ったり、あるいは、さまざまなジャンルで実際の適用例があるので、なにか困ったとき同じ問題に直面した人を探せたり、あるいはその問題を解決するRubyGemsを見つけられる。そういう点でいうと、トータルの生産性はかなり高いことがあるんですね。 なので、テクノロジーとしては、2010年代にどんどん新しく出てきた言語が持ってるあの機能がないとかこの機能がないとか、そういうカタログスペック上の欠点があるように思えても、トータルの生産性あるいは効率のよさを考えると、Ruby on Railsのビジネス上の価値は、実はそんなに下がっていないと思うんです。 先ほどのTIOBE Indexみたいなランキングは、技術者が新しいことを学ぶときに探すところで順位がついているので、ホットなトピックというんですか、新しく出てきて話題になっているものが上に来がちなんですよね。 だから、実際に仕事として、あるいは自分のプロダクトを作るときに、どんな言語を選択してどういうふうに開発したらいいのかを考えると、Rubyの持っているビジネス上の価値はそんなに下がっていないと思います。たとえ順位が下がって、表面上Rubyの人気が凋落したように見えても、ある意味「まだまだ大丈夫」が1つの見識だと思います。

どのプログラミング言語が最もよく使われているかを判断することは難しい。[注釈 5]

注釈

  1. 「ISO/IEC 30170」による厳密な言語仕様はない。2010年6月以降、言語仕様をテストするRubySpecという外部ライブラリの開発が行われていたが、Ruby 2.2.0リリース直後の2014年12月31日、開発が突然終了された。Ruby 2.2.0が「RubySpecのテストをパスしない」等、MRI側によるRubySpec軽視が一因とされている。【悲報】Ruby開発者が使わないので「RubySpec」開発終了”. 2025年9月16日閲覧。
  2. DRYは「Don't Repeat Yourself/同じことを繰り返さない」の意。Rubyのデータ型(オブジェクト)は、動的に型変換し再利用される(同じことを繰り返す)存在であって、静的に固定型で利用される(同じことを繰り返さない)存在ではない。
  3. Matzは1988年に Emacs に触れて以来、Emacsを使い続けている[16]
  4. 原文のママ。リファレンスマニュアル2.1.0ではmatz、3.4ではMatz(2025年8月現在)。
  5. TIOBE社はオランダにある会社で、同じオランダ出身のグイド・ヴァンロッサムが生みの親であるPythonは上位にランキングしている(2025年6月現在)。

出典

参考文献

関連項目

外部リンク

Related Articles

Wikiwand AI