[go: up one dir, main page]

コンテンツにスキップ

Ruby

出典: フリー教科書『ウィキブックス(Wikibooks)』
印刷用ページはサポート対象外です。表示エラーが発生する可能性があります。ブラウザーのブックマークを更新し、印刷にはブラウザーの印刷機能を使用してください。
Wikipedia
Wikipedia
ウィキペディアRubyの記事があります。

本書は、Rubyのチュートリアルです。 Rubyは、比較的習得が容易なオブジェクト指向スクリプティング言語です。 まつもとゆきひろによって開発されました。 Ruby on Railsの記述言語として選ばれたことで有名になりましたが、ウェブアプリケーション以外にもシステム管理やネットワークアプリケーションなどさまざまな用途に用いられます。

Rubyとは

Rubyの特徴

Rubyには、強力で多用途な言語とするための多くの特徴があります。その特徴とは以下の通りです:

オブジェクト指向のプログラミング
Rubyは完全なオブジェクト指向言語であり、Rubyのすべてがオブジェクトであることを意味します。このため、複雑なプログラムを簡単に作成することができ、メンテナンスも容易です。
動的な型付け
Rubyは動的型付け言語であり、コンパイル時に変数の型がチェックされることはありません。このため、プログラムを再コンパイルすることなく、変数の型を簡単に変更することができます。
インタプリタ型言語
Rubyはインタープリタ型言語であり、機械語にコンパイルされるのではなく、コードが1行ずつ実行されます。そのため、Rubyのプログラムの開発やデバッグが容易に行えます。
簡潔な構文
Rubyには簡潔な構文があり、コードを書くのが簡単です。そのため、初心者から経験豊富なプログラマーまで、幅広く利用することができます。
大規模なコミュニティ
Rubyには、言語やライブラリに貢献する開発者たちの大規模で活発なコミュニティがあります。そのため、Rubyプログラミングのヘルプやサポートを簡単に見つけることができます。

Rubyは強力で汎用性の高い言語であり、さまざまなタスクに適しています。Rubyの学習と使用は簡単で、開発者の大規模で活発なコミュニティがあります。強力で汎用性が高く、習得が容易な言語をお探しなら、Rubyは良い選択です。

初級編

Rubyのインストール方法

Rubyを利用するためには、公式ウェブサイトからインストーラーをダウンロードしてインストールします。

以下は、主要なプラットフォームへのインストール手順の概要です:

Windows
RubyInstaller( https://rubyinstaller.org/ ) のページからインストーラーをダウンロードします。
ダウンロードしたインストーラーを実行し、指示に従ってインストールを進めます。
macOS
macOSにはデフォルトでRubyがインストールされていますが、最新バージョンを利用する場合はRVM(Ruby Version Manager https://rvm.io/ ) や rbenvを使用することをお勧めします。
Linux
パッケージマネージャーを使用してインストールします。例えば、UbuntuやDebianでは次のコマンドでインストールできます:
sudo apt-get update
sudo apt-get install ruby

Hello Worldプログラムの作成

RubyでのHello Worldプログラムは非常に簡単です。以下は、コンソールに"Hello, World!"と出力するプログラムです:

hello.rb
puts "Hello, World!"

これをhello.rbという名前で保存し、ターミナルでruby hello.rbと実行すると、"Hello, World!"が表示されます。

$ ruby  hello.rb
Hello, World!
$

データ型と変数

Rubyでは、様々なデータ型が利用できます。文字列、数値、配列、ハッシュなどがその代表的なものです。動的型付け言語であるRubyでは、変数の型宣言が不要です。以下に、主要なデータ型と変数の宣言と代入について詳細を示します。

数値 (Numeric)
数値には、整数 (Integer) や浮動小数点数 (Float) が含まれます。整数は例えば42のように表され、浮動小数点数は例えば3.14のように表されます。
文字列 (String)
文字列は、ダブルクォーテーション (") またはシングルクォーテーション (') で囲まれた文字の並びです。例えば、"Hello"'Ruby'などが文字列です。
配列 (Array)
配列は、複数の要素を順序付けして格納するデータ構造です。要素は角かっこ [ ] で囲まれ、コンマで区切ります。例えば、[1, 2, 3]['a', 'b', 'c']などが配列です。
ハッシュ (Hash)
ハッシュは、キーと値のペアを格納するデータ構造です。波かっこ { } で囲まれ、キーと値はコロン : で区切ります。例えば、{'name' => 'Alice', 'age' => 30}などがハッシュです。

変数の宣言と代入

変数は代入演算子 = を使用して宣言および初期化されます。変数名はアルファベットまたはアンダースコア _ で始まり、その後にはアルファベット、数字、アンダースコアを組み合わせることができます。型宣言は不要であり、変数は動的に型が決まります。

以下は、変数の宣言と代入の例です。

name = "Alice"   # 文字列型の変数
age = 30         # 整数型の変数
scores = [85, 90, 75, 100]  # 配列型の変数
person = {'name' => 'Bob', 'age' => 25}  # ハッシュ型の変数

これらの例では、それぞれの変数に適切なデータ型の値が代入されています。

制御構造

Rubyには様々な制御構造が用意されており、以下のようなものがあります。

条件分岐
if/elsif/else
最も基本的な条件分岐構文
unless/else
if notと同等の意味
case/when/else
値の一致で分岐
case/in/else
パターンマッチ
ループ
while
条件が真の間ループを実行
until
条件が偽の間ループを実行(whileの逆)
for/in
反復可能オブジェクトの要素に対してループ
loop
無限ループ
break/next
ループから抜ける/次の反復へ進む
例外処理
begin/rescue/else/ensure
例外の捕捉と処理
raise
例外を発生させる
その他
times/upto/downto
ブロックを繰り返し実行
tap
参照渡しでブロックを実行
yield
ブロックを呼び出す

Rubyの制御構造はPerlやBashなどとよく似ていますが、独自の構文も存在します。 また、Rubyはブロックの概念があり、それを活用した制御構造があるのが特徴です。

制御構造の一例
# 条件分岐
name = 'Alice'
if name == 'Alice'
  puts 'こんにちは、Aliceさん'
elsif name == 'Bob'
  puts 'こんにちは、Bobさん'
else
  puts "あなたの名前は#{name}ですね"
end

# ifは式
puts if name == 'Alice'
       'こんにちは、Aliceさん'
     elsif name == 'Bob'
       'こんにちは、Bobさん'
     else
       "あなたの名前は#{name}ですね"
     end

# unless
file_exists = false
puts 'ファイルが存在しません' unless file_exists

# case/when
age = 25
case age
when 0...18
  puts '未成年者です'
when 18...65
  puts '働き盛りの年齢ですね'
else
  puts 'シニア世代かもしれません'
end

# パターンマッチング
puts case [2, 3, 5]
     in 0, 1, 2
       '最初の整数'
     in 0, 2, 4
       '最初の偶数'
     in 1, 3, 5
       '最初の奇数'
     in 2, 3, 5 => n
       "最初の素数、三番目は#{n}"
     else
       'どの数列でもない'
     end
# 出力: "最初の素数、三番目は5"

# 繰り返し
# while
i = 0
while i < 5
  puts "i = #{i}"
  i += 1
end

# until
j = 0
until j > 5
  puts "j = #{j}"
  j += 1
end

# for/in
array = [10, 20, 30, 40]
for item in array
  puts "要素は#{item}"
end

# loop
count = 1
loop do
  puts "回数は#{count}"
  count += 1
  break if count > 5
end

# 繰り返し制御
# break/next
numbers = [1, 2, 3, 4, 5]
numbers.each do |n|
  next if n.even?

  puts "奇数は#{n}"
  break if n == 5
end

# 例外処理
# begin/rescue/else/ensure
begin
  result = 10 / 0
rescue ZeroDivisionError => e
  puts "0で割ろうとしました: #{e.message}"
else
  puts "結果は#{result}"
ensure
  puts '計算処理が完了しました'
end

# each
[2, 3, 5, 7, 11].each { |i| puts i }

# times/upto/downto
5.times { puts 'Hello' }
1.upto(5) { |i| puts "uptoの値は#{i}" }
5.downto(1) { |i| puts "downtoの値は#{i}" }

# tap
string = 'Hello'
result = string.tap { |s| puts "tap内: #{s}" }
puts "tapの結果: #{result}"

# yield
def greet
  yield('Morning')
  yield('Evening')
end

greet { |time| puts "Good #{time}!" }

条件実行のイディオム

Rubyには条件実行を簡潔に記述するためのいくつかのイディオムがあります。

  1. 三項演算子
    条件式 ? 真の場合の値 : 偽の場合の値
    
    三項演算子を使うと、if...elseを1行で記述できます。
    allowed = user.admin? ? "Full access" : "Read only"
    
  2. ||= (OR代入演算子)
    値が nil または false の場合のみ右辺の値が代入されます。
    name = params[:name] || "Guest"
    # nameがnilまたは空文字列の場合、"Guest"が代入される
    
  3. &&= (AND代入演算子)
    値が真の場合のみ右辺の値が評価され、代入されます。
    obj &&= obj.method
    # objがnilやfalseでない場合のみ、obj.methodが評価される
    
  4. safe navigation 演算子 (&.)
    レシーバがnilの場合にメソッドが呼ばれず、nilを返します。
    name = params.dig(:user, :name)&.strip
    # params[:user]または params[:user][:name]がnilの場合にnilを返す
    

これらのイディオムを適切に使うことで、条件実行を簡潔で読みやすく記述できます。

反復実行のイディオム

Rubyには反復実行を簡潔に記述するためのいくつかのイディオムがあります。

  1. each と do...end
    [1, 2, 3].each do |n|
      puts n
    end
    
  2. ブロック付きイテレータ
    (1..5).map { |n| n**2 } # => [1, 4, 9, 16, 25]
    [1, 2, 3].select(&:even?) # => [2]
    
  3. times
    5.times { puts "Ruby!" } # "Ruby!"が5回出力される
    
  4. loop do...end
    loop do
      cmd = gets.chomp
      break if cmd == "exit"
      # 処理
    end
    
  5. each と相当する for-in-end
    [1, 2, 3].each { |n| puts n }
    
    for n in [1, 2, 3] do
      puts n
    end
    
    Rubyでは forよりは each の方が
    • each はループ変数がメソッドに閉じている。
    • eachは通常 self を返し、breakされると nil を返す
    • eachはメソッドチェインを構成しやすい
    などの理由で好まれます。
  6. while/until
    ary = [1, 2, 3]
    i = 0
    while i < ary.length
      puts ary[i]
      i += 1
    end
    
  7. Enumerable#each_with_index
    ["a", "b", "c"].each_with_index { |item, index| puts "#{index}: #{item}"}
    
  8. Enumerable#with_index
    ["a", "b", "c"].with_index { |item, index| puts "#{index}: #{item}"}
    

Rubyの反復構文はループだけでなく、ブロックやイテレータによる関数型のスタイルの記述も可能です。状況に応じて適切な方法を選ぶことで、簡潔でわかりやすいコードを書くことができます。

(中級編あるいはリファレンス編に移動予定)

条件分岐

条件分岐は、プログラムの中で特定の条件が満たされた場合に異なる処理を実行するための制御構造です。 Rubyには、if-elsif-else-end 構文、case-when-else-end 構文があります。

if-elsif-else-end

ifelsifelseキーワードを使用して条件分岐を記述します。以下はその基本的な構文です:

if 条件式
  # 条件が真の場合の処理
elsif 別の条件式
  # 条件が真の場合の処理
else
  # どの条件も満たされない場合の処理
end

例えば、次のように使用します:

age = 20

if age >= 20
  puts "You are an adult."
else
  puts "You are not an adult yet."
end

この場合、ageが20以上の場合は"You are an adult."が出力され、それ以外の場合は"You are not an adult yet."が出力されます。

case-when-else-end

Rubyのcase-when-else-end文は、条件に応じて異なる処理を行うための制御構造です。

以下に、基本的な構文と使い方を解説します。

case 
when 条件
  # 式が条件1に合致したときに実行されるコード
when 条件, 条件
  # 式が条件2または条件3に合致したときに実行されるコード
else
  # どの条件にも合致しない場合に実行されるコード
end

caseの後に評価したい式を置きます。その式の評価結果と、when節の各条件を比較し、合致する場合に対応するブロックが実行されます。else節は、どの条件にも合致しない場合のデフォルトの処理を提供します。

例えば、以下はcase-when-else-end文の使用例です。

grade = 'B'

puts case grade
     when 'A' then 'Excellent!'
     when 'B' then 'Good job!'
     when 'C' then 'You passed, but you could do better.'
     else 'Sorry, you failed.'
     end
     #=> Good job!

この例では、変数gradeの値に応じて異なるメッセージが出力されます。gradeが'A'の場合は"Excellent!"が出力され、'B'の場合は"Good job!"が出力されます。gradeが'C'の場合は"You passed, but you could do better."が出力され、どの条件にも一致しない場合は"Sorry, you failed."が出力されます。

case-when-else-end文は、複数の条件をテストする必要がある場合や、条件に基づいてさまざまな処理を行う必要がある場合に便利です。

パターンマッチング

パターンマッチングは、プログラムの中で特定のパターンに処理を実行するための制御構造です。 Rubyには、case-in-else-end 構文と=>演算子とin演算子があります。

case-in-else-end

Rubyのパターンマッチング(Pattern Matching)は、構造化された値の深いマッチングを可能にする機能です。これにより、構造をチェックし、一致した部分をローカル変数にバインドすることができます。

Rubyのパターンマッチングは、case/in式を使用して実装されています。

以下に、その基本的な構文と使い方を示します。

case 
in パターン
  # 式がパターン1に合致したときに実行されるコード
in パターン
  # 式がパターン2に合致したときに実行されるコード
else
  # どのパターンにも合致しない場合に実行されるコード
end

case式の中のinブロックは、パターンと一致するかどうかをチェックし、マッチした場合に対応するコードを実行します。else節は、どのパターンにも一致しない場合のデフォルトの処理を提供します。

以下は、パターンマッチングの使用例です。

puts case [2, 3, 5]
     in 0, 1, 2
       '最初の整数'
     in 0, 2, 4
       '最初の偶数'
     in 1, 3, 5
       '最初の奇数'
     in 2, 3, 5 => n
       "最初の素数、三番目は#{n}"
     else
       'どの数列でもない'
     end
# 出力: "最初の素数、三番目は5"

このコードは、パターンマッチングを利用して、与えられた配列の最初の要素に応じて異なる処理を行います。与えられた配列 [2, 3, 5] の最初の要素が特定のパターンに一致するかどうかをテストし、一致する場合に対応するメッセージを出力します。

具体的には、与えられた配列 [2, 3, 5] の最初の要素が各条件に一致するかどうかをテストします。

  • [2, 3, 5]0, 1, 2 に一致せず、次の条件へ進みます。
  • [2, 3, 5]0, 2, 4 に一致せず、次の条件へ進みます。
  • [2, 3, 5]1, 3, 5 に一致せず、次の条件へ進みます。
  • [2, 3, 5]2, 3, 5 => n に一致し、n7 が割り当てられます。

最終的に "最初の素数、三番目は7" というメッセージが出力されます。

else 節は、どの条件にも一致しない場合に実行されますが、この場合、どの数列でもないことを示すメッセージが出力されることになります。

=>演算子とin演算子

=>演算子とin演算子 は、パターンマッチングを簡潔に行う方法の1つです。通常、パターンマッチングは複数の行にわたるケース式や条件分岐を使用しますが、=>演算子とin演算子 は1行の式でパターンをマッチングする手法です。

具体的には、次のような形式で表現されます:

 => パターン

あるいは

 in パターン

この形式では、 の値が パターン にマッチするかどうかを評価します。 =>演算子とin演算子 でパターンマッチングに失敗すると、NoMatchingPatternKeyError例外が raise されます。

{a: 1, b: 2, c: 3} => { b: n }
puts n # 2
{a: 1, b: 2, c: 3} => { x: n }
# {:a=>1, :b=>2, :c=>3}: key not found: :x (NoMatchingPatternKeyError)
さまざまなパターン

パターンマッチングにはさまざまなパターンがあります:

  • 値パターン(Value pattern)
  • 配列パターン(Array pattern)
  • ハッシュパターン(Hash pattern)
  • バインディングパターン(Variable pattern)
  • その他のパターン(Alternative pattern)

これらのパターンを組み合わせて、複雑なデータ構造をマッチングできます。また、パターンマッチングではマッチした部分をローカル変数にバインドすることもできます。

反復

Rubyにおける反復(ループ)は、いくつかの方法で行うことができます。主な方法は以下の通りです。

eachメソッド

配列やハッシュなどのコレクションに対して使われる最も一般的な反復方法です。以下はその例です。

array = [1, 2, 3, 4, 5]
array.each do |item|
  puts item
end
for文

for文も使用できますが、Rubyではあまり一般的ではありません。

for i in 1..5
  puts i
end
while文

while文を使って条件が満たされている間、ブロック内のコードを実行します。

x = 0
while x < 5 do
  puts x
  x += 1
end
until文

until文は条件が満たされるまでループします。

x = 0
until x == 5 do
  puts x
  x += 1
end
timesメソッド

固定回数のループを行う場合に便利です。

5.times do |i|
  puts i
end

これらの方法を組み合わせて、さまざまな反復のニーズに対応できます。 ただし、Rubyではeachメソッドが最も一般的で、他の方法よりも好まれることが多いです。

メソッド

メソッドは、Rubyプログラムで特定の処理を実行するための手段を提供します。メソッドは、関数や手続きと同様の機能を持ち、再利用可能なコードブロックを定義します。Rubyでは、メソッドはオブジェクト指向プログラミングの基本的な概念であり、ほとんどの処理がメソッドとして定義されます。

メソッドの定義

メソッドは以下のような形式で定義されます:

def メソッド名(引数1, 引数2, ...)
  # メソッドの処理
end

ここで、defキーワードを使ってメソッドを定義し、メソッド名を指定します。メソッドが引数を取る場合は、引数のリストを指定します。メソッドの本体は、endキーワードで終了します。

以下は、簡単なメソッドの例です:

def greet(name)
  puts "Hello, #{name}!"
end

greet("Alice")

この例では、greetという名前のメソッドが定義されています。このメソッドはnameという引数を取り、それを使用して挨拶を表示します。greet("Alice")という呼び出しでは、"Hello, Alice!"というメッセージが表示されます。

Endlessなメソッドの定義

endキーワードを伴わず、代入に似た形式でも定義できます:

def メソッド名(引数1, 引数2, ...) = 

ここで、defキーワードと=を使ってメソッドを定義し、メソッド名を指定します。メソッドが引数を取る場合は、引数のリストを指定します。

以下は、簡単なメソッドの例です:

def greet(name) = puts "Hello, #{name}!"

greet("Alice")

戻り値

また、Rubyではメソッド内で最後に評価された式が自動的にメソッドの戻り値となります。明示的なreturn文は省略することができますが、必要に応じて使用することもできます。

ブロックを伴うメソッド

さらに、メソッドはオプションのブロックを受け取ることができます。ブロックを受け取るメソッドは、yieldキーワードを使ってブロックを実行します。

def greet(name)
  puts "Hello, #{name}!"
  yield if block_given?
end

このメソッドでは、引数として受け取ったnameに対して挨拶を出力し、その後にyieldを使ってブロックを実行しています。block_given?メソッドは、メソッドがブロックを受け取ったかどうかを判定します。

メソッドを呼び出す際には、通常の引数と一緒にブロックを渡すことができます。

greet("Alice") do
  puts "Nice to meet you!"
end

このコードは、以下を出力します。

Hello, Alice!
Nice to meet you!

配列とハッシュ

配列の操作

配列は複数の要素を格納するためのデータ構造であり、それぞれの要素は順序付けされています。Rubyでは、配列は角かっこ [ ] で囲まれ、コンマで区切られた要素のリストとして表現されます。

要素の追加、削除、取得
  • 要素の追加: <<演算子やpushメソッドを使用して要素を配列に追加することができます。
    fruits = ['apple', 'banana', 'orange']
    fruits << 'grape'
    fruits.push('kiwi')
    
  • 要素の削除: delete_atメソッドやpopメソッドを使用して配列から要素を削除することができます。
    fruits.delete_at(0)  # インデックス0の要素を削除
    fruits.pop  # 末尾の要素を削除
    
  • 要素の取得: インデックスを指定して配列から要素を取得することができます。
    puts fruits[0]  # => "banana"
    

ハッシュの操作

ハッシュは、キーと値のペアを格納するデータ構造であり、各要素はキーを使用して参照されます。Rubyでは、波かっこ { } を使用してハッシュを定義します。

キーと値の追加、削除、取得
  • キーと値の追加: ハッシュに新しいキーと値のペアを追加するには、単純に代入演算子 = を使用します。
    person = {'name' => 'Alice', 'age' => 30}
    person['gender'] = 'female'
    
  • キーと値の削除: deleteメソッドを使用して、指定したキーとそれに対応する値を削除することができます。
    person.delete('age')
    
  • キーと値の取得: ハッシュから特定のキーに関連付けられた値を取得するには、そのキーを指定します。
    puts person['name']  # => "Alice"
    

クラスとオブジェクト指向プログラミング

クラスの定義とインスタンス化

クラスは、オブジェクトの設計図として機能し、それぞれのオブジェクトはそのクラスのインスタンスです。Rubyでは、classキーワードを使用してクラスを定義します。

クラスの定義
class Person
  def initialize(name, age)
    @name = name
    @age = age
  end

  def introduce
    puts "My name is #{@name} and I'm #{@age} years old."
  end
end

この例では、Personという名前のクラスが定義されています。initializeメソッドは、新しいインスタンスが作成されるときに呼び出され、インスタンス変数 @name@age に値を設定します。introduceメソッドは、そのインスタンスの情報を出力します。

インスタンス化
alice = Person.new("Alice", 30)
bob = Person.new("Bob", 25)

これにより、Personクラスの新しいインスタンス alicebob が作成されます。それぞれのインスタンスは、initializeメソッドによって設定された名前と年齢を持ちます。

alice.introduce  # => "My name is Alice and I'm 30 years old."
bob.introduce    # => "My name is Bob and I'm 25 years old."

各インスタンスは、クラス内で定義されたメソッドを呼び出すことができます。

オブジェクト指向プログラミングの基礎

オブジェクト指向プログラミング(OOP)は、プログラムをオブジェクトとそれらの相互作用に基づいて構造化する方法です。OOPには、次のような重要な概念があります:

  • カプセル化: データとそれに関連するメソッドを1つのユニットにまとめ、外部からのアクセスを制限します。
  • 継承: 既存のクラスから新しいクラスを作成し、既存の機能を再利用します。
  • ポリモーフィズム: 同じ名前のメソッドが異なるクラスで異なる振る舞いをすることを許容します。

これらの概念を使用することで、コードをより柔軟で保守しやすくすることができます。

中級編

例外処理

begin-rescue-end文を使った例外処理の実装

例外処理は、予期しないエラーが発生した場合にプログラムの実行を中断せずに制御を継続するための仕組みです。Rubyでは、beginrescueendキーワードを使用して例外処理を実装します。

begin
  # 例外が発生する可能性のあるコード
  result = 10 / 0
rescue ZeroDivisionError => e
  # 例外が発生した場合の処理
  puts "Error occurred: #{e.message}"
end

上記の例では、beginブロック内でゼロ除算エラーが発生する可能性があります。rescueブロックでは、特定の例外(ここではZeroDivisionError)が発生した場合の処理を指定します。例外が発生した場合、その例外オブジェクトがrescueブロックの変数eに割り当てられ、messageメソッドを使ってエラーメッセージを取得します。

ファイル操作

ファイルの読み書き

ファイルの読み書きは、プログラムでよく行われる操作の1つです。Rubyでは、Fileクラスを使用してファイルの読み書きを行います。

# ファイルの書き込み
File.open("example.txt", "w") do |file|
  file.write("Hello, World!")
end

# ファイルの読み込み
File.open("example.txt", "r") do |file|
  content = file.read
  puts content  # => "Hello, World!"
end

ファイルポインタの移動

ファイルポインタを移動して、ファイル内の特定の位置から読み書きを行うこともできます。

# ファイルのオープン
File.open("example.txt", "r") do |file|

  # ファイルポインタの移動
  file.seek(7)

  # 指定位置からの読み込み
  content = file.read
  puts content  # => "World!"
}

モジュールとMix-in

モジュールの作成と利用

モジュールは、関連するメソッドや定数をまとめるための仕組みであり、クラスにインクルードすることでその機能を利用することができます。

module Greeting
  def say_hello
    puts "Hello!"
  end
end

class MyClass
  include Greeting
end

obj = MyClass.new
obj.say_hello  # => "Hello!"

上記の例では、Greetingモジュールを定義し、MyClassクラスにインクルードしています。その結果、MyClassのインスタンスであるobjからsay_helloメソッドが利用できるようになります。

Mix-inパターンの活用

Mix-inは、複数のクラスに同じメソッドや振る舞いを提供するためのデザインパターンです。モジュールを使ってMix-inを実現することができます。

module Debug
  def debug_info
    puts "#{self.class} - #{self.inspect}"
  end
end

class MyClass
  include Debug
end

obj = MyClass.new
obj.debug_info  # => "MyClass - #<MyClass:0x00007fb35a20b3b8>"

Debugモジュールは、debug_infoメソッドを提供し、このメソッドを含むクラスにMix-inします。これにより、クラスのインスタンスがデバッグ情報を表示するメソッドを利用できるようになります。

テスト

Mintest

Minitestは、Rubyプログラミング言語用の軽量なテストフレームワークです。Minitestは、Rubyの標準ライブラリに含まれており、Rubyのバージョン1.9以降で利用可能です。Minitestは、テスト駆動開発(Test-Driven Development、TDD)や振る舞い駆動開発(Behavior-Driven Development、BDD)などのソフトウェア開発手法を支援するために使用されます。

Minitestは、単体テストや統合テストなど、さまざまな種類のテストをサポートしています。また、アサーションを用いて期待される振る舞いを確認するための豊富な機能も提供しています。Minitestの特徴は、シンプルで使いやすいインターフェース、高速な実行速度、そしてRubyの標準ライブラリに含まれているため、追加の依存関係を導入せずに利用できることです。

開発者がMinitestを使用することで、安定性や品質を向上させることができます。また、テストコードの記述や実行が容易であるため、素早く効果的なテストを作成することができます。


実際のコードを観てみましょう。

下記は、逆ポーランド記法の式を評価する機能を配列を継承して実装した例で、コメントを合わせても30行のコードですが、各演算子の実装の確認など基礎的なテストケースを書くだけで、数倍のコード量になります。

Minitestの例
# frozen_string_literal: true

# RPNクラスは逆ポーランド記法(Reverse Polish Notation)の式を評価するためのクラスです。
# スタック構造を使用して式を解析し、結果を計算します。
# Rubyでは、Arrayクラスがpop,pushなどのスタックとしてのメソッドが完備されているので継承しました。
class RPN < Array

  # 与えられた逆ポーランド記法の式を評価し、結果を返します。
  # @param expression [String] 評価する式
  # @return [Numeric] 式の評価結果
  def eval(expression)
    expression.split.each do |token|
      case token
      when /\A-?\d+\z/, # 十進数
           /\A[+-]?0[Bb][01]+\z/, # 2進数
           /\A[+-]?0[Oo][0-7]+\z/, # 8進数
           /\A[+-]?0[Xx][0-9A-Fa-f]+\z/ # 10進数
        push Integer(token)
      when /\A-?\d+(\.\d+)?([eE][-+]?\d+)?\z/ # 浮動小数点数
        push(token.to_f)
      when *%w[+ - * / % ** & | ^ << >>] # 二項演算子
        left, right = pop(2)
        push left.send(token.to_sym, right)
      else
        raise RuntimeError, "Invalid operator: #{token}"
      end
    end

    # 最終的な結果はスタックの一番上に残る
    peek
  end

  # スタックの一番上の値を返しますが、スタックから削除しません。
  # @return [Numeric, nil] スタックの一番上の値。スタックが空の場合はnilを返します。
  alias peek last
end

require 'minitest/autorun'

class TestRPN < Minitest::Test
  def setup
    @rpn = RPN.new
  end
  
  def test_eval_with_single_operand
    assert_equal 5, @rpn.eval('5')
    assert_equal 5, @rpn.eval('0b101')
    assert_equal -5, @rpn.eval('-0b101')
    assert_equal 5, @rpn.eval('+0b101')
    assert_equal 127, @rpn.eval('0o177')
    assert_equal -127, @rpn.eval('-0o177')
    assert_equal 127, @rpn.eval('+0O177')
    assert_equal 195935983, @rpn.eval('0xbadbeef')
    assert_equal -195935983, @rpn.eval('-0xbadbeef')
    assert_equal 1.5, @rpn.eval('1.5')
    assert_equal 1e234, @rpn.eval('1e+234')
  end
  
  def test_eval_with_addition
    assert_equal 8, @rpn.eval('3 5 +')
    assert_equal 10, @rpn.eval('2 +')
  end
  
  def test_eval_with_subtraction
    assert_equal 7, @rpn.eval('10 3 -')
  end
  
  def test_eval_with_multiplication
    assert_equal 24, @rpn.eval('4 6 *')
  end
  
  def test_eval_with_division
    assert_equal 5, @rpn.eval('15 3 /')
  end
  
  def test_eval_with_remain
    assert_equal 2, @rpn.eval('11 3 %')
  end
  
  def test_eval_with_exponentiation
    assert_equal 8, @rpn.eval('2 3 **')
    assert_equal 1.4142135623730951, @rpn.eval('2 0.5 **')
    assert_equal 633825300114114700748351602688, @rpn.eval('2 99 **')
  end
  
  def test_eval_with_bitwidth_and
    assert_equal 8, @rpn.eval('12 10 &')
    assert_raises(NoMethodError) { @rpn.eval('12.0 10 &') }
    assert_raises(TypeError) { @rpn.eval('12 10.0 &') }
  end
  
  def test_eval_with_bitwidth_or
    assert_equal 14, @rpn.eval('12 10 |')
    assert_raises(NoMethodError) { @rpn.eval('12.0 10 |') }
    assert_raises(TypeError) { @rpn.eval('12 10.0 |') }
  end
  
  def test_eval_with_bitwidth_exclusive_or
    assert_equal 6, @rpn.eval('12 10 ^')
    assert_raises(NoMethodError) { @rpn.eval('12.0 10 ^') }
    assert_raises(TypeError) { @rpn.eval('12 10.0 ^') }
  end
  
  def test_eval_with_shift_left
    assert_equal 12288, @rpn.eval('12 10 <<')
    assert_raises(NoMethodError) { @rpn.eval('12.0 10 <<') }
    assert_equal 12288, @rpn.eval('12 10.1 <<')
  end
  
  def test_eval_with_shift_right
    assert_equal 15, @rpn.eval('123 3 >>')
    assert_raises(NoMethodError) { @rpn.eval('123.0 3 >>') }
    assert_equal 15, @rpn.eval('123 3.9 >>')
  end
  
  def test_eval_with_invalid_expression
    assert_raises(TypeError) { @rpn.eval('2 +') }
  end
  
  def test_peek_returns_top_element
    @rpn.eval('1 2 3 + +')
    assert_equal 6, @rpn.peek
  end
  
  def test_nan_or_zero_division
    @rpn.eval('0.0 0 /')
    assert @rpn.peek.nan?
    @rpn.eval('0 0.0 /')
    assert @rpn.peek.nan?
    @rpn.eval('-0.0 0 /')
    assert @rpn.peek.nan?
    assert_raises(ZeroDivisionError) { @rpn.eval('0 0 /') }
  end
  
  def test_inf_or_zero_division
    @rpn.eval('1.0 0 /')
    assert_equal Float::INFINITY, @rpn.peek
    @rpn.eval('-1 0.0 /')
    assert_equal -Float::INFINITY, @rpn.peek
    @rpn.eval('1 -0.0 /')
    assert_equal -Float::INFINITY, @rpn.peek
    @rpn.eval('-1 -0.0 /')
    assert_equal Float::INFINITY, @rpn.peek
    assert_raises(ZeroDivisionError) { @rpn.eval('110 0 /') }
  end
  
  def test_peek_returns_nil_for_empty_stack
    assert_nil @rpn.peek
  end
end

このコードは、逆ポーランド記法(Reverse Polish Notation)の式を評価するためのRPN(Reverse Polish Notation)クラスを定義しています。このクラスは、与えられた式をスタックを使用して解析し、計算します。以下に、このコードの機能と構造についての解説を行います。

RPNクラス
  • # frozen_string_literal: true: この行は、ファイル内の文字列リテラルが変更されないことを確認するためのリテラルです。
  • RPNクラスはArrayクラスを継承しており、スタックとしての機能を使用します。
  • evalメソッドは与えられた逆ポーランド記法の式を評価し、結果を返します。
  • peekメソッドは、スタックの一番上の値を返しますが、スタックから削除しません。
テストケース
  • TestRPNクラスはMinitest::Testを継承しており、RPNクラスのテストを定義します。
  • setupメソッドは各テストメソッドの前に実行され、テストで使用するRPNオブジェクトをセットアップします。
テストメソッド
  • test_eval_with_single_operandからtest_eval_with_invalid_expressionまでのメソッドは、evalメソッドが正しく動作することを確認するためのテストです。
  • test_peek_returns_top_elementpeekメソッドがスタックの一番上の値を返すことを確認するテストです。
  • test_nan_or_zero_divisiontest_inf_or_zero_divisionは、NaNやInfinityの扱い、ゼロ除算の例外処理をテストします。
  • これらのテストは、RPNクラスの各機能が正しく動作することを保証します。

このコードは、逆ポーランド記法の式の評価に関する機能を提供し、テストによってその正確性が検証されています。

RSpec

RSpecは、Rubyでのテストを行うためのフレームワークの1つです。RSpecを使用すると、テストをより明確に記述し、テスト結果を見やすく出力することができます。

Gemfile
# Gemfile
source 'https://rubygems.org'
gem 'rspec'
導入
# コマンドライン
bundle install

ユニットテストと統合テストの書き方

ユニットテストは、個々のコンポーネント(クラスやメソッドなど)が正しく機能するかをテストするものです。一方、統合テストは複数のコンポーネントが互いに連携して正しく動作するかをテストします。

spec/my_class_spec.rb
# spec/my_class_spec.rb (ユニットテスト)
require 'my_class'

RSpec.describe MyClass do
  describe '#method_name' do
    it 'returns something' do
      obj = MyClass.new
      expect(obj.method_name).to eq(something)
    end
  end
end
spec/integration_spec.rb
# spec/integration_spec.rb (統合テスト)
require 'app'

RSpec.describe 'Integration Test' do
  it 'checks something' do
    # 統合テストの実装
  end
end

RSpecを使ってユニットテストと統合テストを書くことで、コードの品質を向上させ、予期せぬバグを見つけるのに役立ちます。

上級編

高度なオブジェクト指向プログラミング

継承、ポリモーフィズム、カプセル化の活用

継承は、既存のクラスから新しいクラスを作成し、そのクラスが親クラスのすべての特性を引き継ぐことができる機能です。ポリモーフィズムは、同じ名前のメソッドが異なるクラスで異なる振る舞いをすることを指します。カプセル化は、データやメソッドをオブジェクト内部に隠蔽し、外部からのアクセスを制限することです。これらの概念を活用することで、より柔軟で効率的なコードを書くことができます。

class Animal
  def speak
    raise NotImplementedError, "Subclasses must implement the 'speak' method."
  end
end

class Dog < Animal
  def speak
    "Woof!"
  end
end

class Cat < Animal
  def speak
    "Meow!"
  end
end

dog = Dog.new
puts dog.speak  # => "Woof!"

cat = Cat.new
puts cat.speak  # => "Meow!"

上記の例では、Animalクラスが親クラスとして定義され、speakメソッドが定義されています。DogクラスとCatクラスはそれぞれAnimalクラスを継承し、speakメソッドをオーバーライドしています。これにより、DogクラスとCatクラスは同じ名前のメソッドを持ちつつも、それぞれ異なる振る舞いをします。

オブジェクト指向デザインパターンの理解と実装

オブジェクト指向デザインパターンは、再利用可能なソフトウェア設計のガイドラインです。これらのパターンは、特定の問題に対する解決策を提供し、コードの再利用性、拡張性、保守性を向上させます。代表的なデザインパターンには、Singleton、Factory、Observer、Decoratorなどがあります。

# Singleton パターンの例
require 'singleton'

class SingletonClass
  include Singleton

  def initialize
    @counter = 0
  end

  def increment_counter
    @counter += 1
  end

  def get_counter
    @counter
  end
end

instance1 = SingletonClass.instance
instance1.increment_counter
puts instance1.get_counter  # => 1

instance2 = SingletonClass.instance
puts instance2.get_counter  # => 1

上記の例では、Singletonパターンを使用して、特定のクラスのインスタンスが常に1つしか存在しないことを保証しています。Singletonモジュールをインクルードすることで、インスタンスが複数作成されることを防ぎます。

並行プログラミング

スレッド、プロセスの管理

Rubyにおける並行プログラミングは、スレッドとプロセスを使用して実現されます。スレッドはプロセス内で動作し、複数のスレッドが同時に実行されることで並行処理が可能になります。一方、プロセスは独立した実行単位であり、それぞれが独自のメモリ空間を持ちます。

以下では、Rubyにおけるスレッドとプロセスの管理について解説し、コード例を示します。

スレッド

RubyではThreadクラスを使用してスレッドを作成し、Thread.newメソッドを使って新しいスレッドを生成します。生成されたスレッドは並行して実行され、メインスレッドと同時に動作します。スレッドの制御にはjoinメソッドを使用して、メインスレッドがスレッドの終了を待機することができます。

threads = []

# スレッドを生成して配列に格納
threads << Thread.new do
  3.times do
    sleep 1
    puts "Thread 1 executing"
  end
end

threads << Thread.new do
  3.times do
    sleep 2
    puts "Thread 2 executing"
  end
end

# 全てのスレッドが終了するのを待機
threads.each(&:join)
puts "All threads finished"

上記の例では、2つのスレッドを生成し、それぞれが一定間隔でメッセージを出力しています。joinメソッドにより、メインスレッドが全てのスレッドの終了を待機します。

プロセス

Rubyでは、forkメソッドを使用して新しいプロセスをフォークし、子プロセス内で処理を実行することができます。プロセスの終了を待機するにはProcess.waitメソッドを使用します。

child_pid = fork do
  puts "Child process executing"
  sleep 3
  puts "Child process finished"
end

puts "Parent process waiting for child to finish"
Process.wait(child_pid)
puts "Parent process finished waiting"

上記の例では、親プロセスが子プロセスをフォークし、子プロセスが一定時間待機した後に終了します。親プロセスはProcess.waitメソッドで子プロセスの終了を待機します。

Rubyのスレッドとプロセスを使った並行プログラミングには、他にも多くの機能や制御方法がありますが、基本的な部分はこのようになります。

スレッド、プロセス以外の並行プログラミング

Rubyには、スレッドとプロセス以外にも並行プログラミングを行うためのさまざまな手段があります。以下にいくつかの方法を紹介します。

Fiber

Fiberは、イテレータと似たコンテキストを持つ軽量なスレッドのようなものです。複数のFiberを作成し、それらを切り替えながら処理を行うことで、非同期の並行処理を実現することができます。

fiber1 = Fiber.new do
  puts "Fiber 1 executing"
  Fiber.yield
  puts "Fiber 1 resumed"
end

fiber2 = Fiber.new do
  puts "Fiber 2 executing"
  Fiber.yield
  puts "Fiber 2 resumed"
end

fiber1.resume
fiber2.resume
fiber1.resume
fiber2.resume
Concurrent Ruby gem

Concurrent Ruby gemは、並行プログラミングを行うための機能を提供するGemです。ThreadPoolやFuture、Promise、Actorなどの機能を使って、複雑な並行処理を実現することができます。

require 'concurrent'

pool = Concurrent::FixedThreadPool.new(5)

future = Concurrent::Future.execute(executor: pool) do
  # 並行処理を実行するコード
  sleep 1
  "Future result"
end

puts "Waiting for future to complete..."
puts future.value
Celluloid gem

Celluloid gemは、アクターモデルをベースにした並行プログラミングを行うためのGemです。アクターモデルでは、個々のアクターがメッセージを送受信しながら処理を実行します。

require 'celluloid'

class MyActor
  include Celluloid

  def initialize
    puts "Actor initialized"
  end

  def process_message(message)
    puts "Received message: #{message}"
  end
end

actor = MyActor.new
actor.async.process_message("Hello from main thread!")

これらの手法を使うことで、Rubyでより効果的な並行プログラミングを実現することができます。選択肢はプロジェクトの要件や好みに応じて異なりますが、Rubyの並行処理に関する柔軟性と機能性は広範囲にわたっています。

並行処理の問題点と解決策

並行プログラミングでは、競合状態やデッドロックなどの問題が発生する可能性があります。競合状態は、複数のスレッドが同時に共有されたリソースにアクセスしようとすることで起こります。デッドロックは、複数のスレッドがお互いにリソースを解放するのを待ち合わせることで発生します。

これらの問題を回避するための解決策として、適切なロックの使用、スレッドセーフなデータ構造の選択、並行処理の慎重な設計などがあります。

並行処理の問題点と解決策について、コード例を示して説明します。

問題点: 競合状態

競合状態は、複数のスレッドが同時に共有されたリソースにアクセスしようとすることで発生します。

以下は、競合状態が発生する可能性のあるコード例です。

counter = 0

threads = 10.times.map do
  Thread.new do
    1000.times { counter += 1 }
  end
end

threads.each(&:join)

puts "Counter: #{counter}"

このコードでは、複数のスレッドがcounter変数にアクセスしています。しかし、counter += 1の操作は複数のスレッドで同時に実行される可能性があり、結果として期待通りにカウンターが増加しない可能性があります。

解決策: ロックの使用

競合状態を回避するために、ロックを使用して複数のスレッドが同時に共有されたリソースにアクセスできないようにします。Rubyでは、Mutexクラスを使用してスレッドセーフなロックを実装できます。

counter = 0
mutex = Mutex.new

threads = 10.times.map do
  Thread.new do
    1000.times do
      mutex.synchronize { counter += 1 }
    end
  end
end

threads.each(&:join)

puts "Counter: #{counter}"

このコードでは、Mutexを使用してcounter変数へのアクセスを同期化しています。 これにより、複数のスレッドが同時にcounter変数にアクセスすることができず、競合状態が回避されます。

問題点: デッドロック

デッドロックは、複数のスレッドがお互いにリソースを解放するのを待ち合わせることで発生します。

以下は、デッドロックが発生する可能性のあるコード例です。

mutex1 = Mutex.new
mutex2 = Mutex.new

thread1 = Thread.new do
  mutex1.lock
  sleep 1
  mutex2.lock
  mutex1.unlock
  mutex2.unlock
end

thread2 = Thread.new do
  mutex2.lock
  sleep 1
  mutex1.lock
  mutex2.unlock
  mutex1.unlock
end

thread1.join
thread2.join

このコードでは、thread1mutex1をロックし、同時にthread2mutex2をロックする可能性があります。その後、各スレッドは相手のロックを解放するのを待ち合わせることになり、デッドロックが発生します。

解決策: ロックの順序付け

デッドロックを回避するために、ロックを取得する順序を一貫させることが重要です。 この方法により、複数のスレッドが同じ順序でロックを取得しようとしても、デッドロックを引き起こすことがありません。

mutex1 = Mutex.new
mutex2 = Mutex.new

thread1 = Thread.new do
  mutex1.lock
  sleep 1
  mutex2.lock
  mutex1.unlock
  mutex2.unlock
end

thread2 = Thread.new do
  mutex1.lock # ロックの順序を変更
  sleep 1
  mutex2.lock
  mutex1.unlock
  mutex2.unlock
end

thread1.join
thread2.join

この修正では、thread2mutex1を先にロックするように変更されており、ロックの順序が一致しています。これにより、デッドロックが発生する可能性が低くなります。

以上が、並行処理の問題点とその解決策をコード例を交えて説明したものです。競合状態やデッドロックは並行プログラミングにおける一般的な問題であり、適切な対処が必要です。

Webアプリケーション開発

Ruby on Railsなどのフレームワークを使用したWebアプリケーションの開発

Ruby on Railsは、RubyでのWebアプリケーション開発を支援する人気のあるフレームワークです。Railsを使用することで、MVC(Model-View-Controller)アーキテクチャをベースにした効率的なWebアプリケーションを開発することができます。

# Railsアプリケーションの作成
rails new myapp

# モデルの作成
rails generate model User name:string email:string

# データベースマイグレーションの実行
rails db:migrate

# コントローラの作成
rails generate controller UsersController
config/routes.rb
# ルーティングの設定
Rails.application.routes.draw do
  resources :users
end

Railsでは、コマンドラインツールを使用してモデルやコントローラを簡単に生成し、ルーティングを設定することができます。これにより、短期間で効率的なWebアプリケーションを構築することができます。

MVCアーキテクチャの理解

MVC(Model-View-Controller)アーキテクチャは、アプリケーションの設計を3つの主要なコンポーネントに分割するアーキテクチャパターンです。

モデル (Model)
  • データやビジネスロジックを担当します。
  • アプリケーションの状態やデータを管理し、ビューに表示されるデータを提供します。
  • モデルは通常、データベースや外部サービスとのやり取りも担当します。
ビュー (View)
  • ユーザーインターフェース(UI)の表示を担当します。
  • ユーザーにデータを視覚的に提示し、ユーザーからの入力を受け付けます。
  • ビューは通常、モデルからデータを受け取り、それをユーザーに見やすい形で表示します。
コントローラ (Controller)
  • ユーザーからの入力を受け付け、それに基づいてモデルやビューの操作を制御します。
  • ユーザーの操作に応じてモデルの状態を変更し、ビューの更新をトリガーします。
  • コントローラはユーザーとアプリケーションの主要な仲介者であり、ビジネスロジックを含む場合があります。
相互関係
  • ユーザーがビューで何かしらの操作を行うと、それに対するイベントがコントローラに送信されます。
  • コントローラはそのイベントを受け取り、必要に応じてモデルを更新したり、新しいデータをビューに送ったりします。
  • モデルが変更されると、モデルはその変更をビューに通知します。
  • ビューは通知を受け取り、表示を更新します。

このように、MVCアーキテクチャは各コンポーネントが疎結合であり、変更が発生した際に一部のコンポーネントを変更しても他のコンポーネントに影響を与えにくい特徴があります。これにより、柔軟性が向上し、保守性が高まります。

RoRにおけるMVC

RoR(Ruby on Rails)は、MVC(Model-View-Controller)アーキテクチャの理念に基づいて設計されたWebアプリケーションフレームワークです。RoRにおけるMVCの役割と機能を説明します。

モデル (Model)
  • RoRのモデルは、アプリケーションのデータベースとのやり取りを担当します。
  • モデルはActive Recordとして実装されており、データベースのテーブルと対応するオブジェクトを表現します。
  • モデルはビジネスロジックを含み、データの検証や処理を行います。
class User < ApplicationRecord
  validates :name, presence: true
end
ビュー (View)
  • RoRのビューは、ユーザーにデータを表示するためのテンプレートを提供します。
  • HTMLやERB(Embedded Ruby)などのテンプレートエンジンを使用して、動的なコンテンツを生成します。
  • ビューは主にユーザーインターフェースの生成に使用されます。
<% @users.each do |user| %>
  <p><%= user.name %></p>
<% end %>
コントローラ (Controller)
  • RoRのコントローラは、ユーザーからのリクエストを受け取り、それに応じて適切な処理を行います。
  • リクエストの解析やデータの操作など、ビジネスロジックの一部もコントローラで処理されます。
  • ビューの表示を制御し、モデルへのデータの渡し方もコントローラで決定されます。
class UsersController < ApplicationController
  def index
    @users = User.all
  end
end
ルーティング (Routing)
  • RoRでは、URLとコントローラアクションの対応をルーティングで定義します。
  • ルーティングはconfig/routes.rbファイルに記述され、特定のURLパスへのリクエストを特定のコントローラアクションにマップします。
Rails.application.routes.draw do
  resources :users, only: [:index]
end
MVCの相互関係
  • ユーザーからのリクエストはルーティングで適切なコントローラアクションに送信されます。
  • コントローラは必要に応じてモデルからデータを取得し、適切なビューをレンダリングしてユーザーに表示します。
  • ビューはユーザーにデータを表示し、必要に応じてユーザーの入力を受け付けます。
  • ユーザーの入力やアクションによっては、再びコントローラへリクエストが送信され、その処理が繰り返されます。

Ruby on RailsはMVCアーキテクチャにより、アプリケーションの各コンポーネントが明確に分離され、保守性が向上し、柔軟性が高まります。

データベース連携

SQLデータベースとの接続

Railsでは、アクティブレコードと呼ばれるオブジェクト関係マッピング(ORM)ツールを使用して、データベースとのやり取りを簡単に行うことができます。アクティブレコードを使用することで、SQLクエリを直接記述せずに、Rubyオブジェクトとしてデータベースのテーブルを操作することができます。

# ユーザーデータの取得
users = User.all

# 新しいユーザーの作成
user = User.new(name: "Alice", email: "alice@example.com")
user.save

Railsでは、データベースへの接続設定を行うことで、アプリケーション内でアクティブレコードを使用してデータベースとやり取りすることができます。

ORM(Object-Relational Mapping)の活用

ORMは、オブジェクトとリレーショナルデータベースとの間のマッピングを行うためのツールです。ORMを使用することで、データベースのテーブルをオブジェクトとして操作することができ、SQLクエリの記述を最小限に抑えることができます。これにより、コードの可読性や保守性が向上します。

パフォーマンスチューニング

メモリ管理、コードの最適化

メモリ管理とコードの最適化は、Rubyプログラミングにおいて重要な要素です。効率的なメモリ管理を行うことで、アプリケーションのメモリ使用量を最小限に抑え、パフォーマンスを向上させることができます。また、コードの最適化により、処理速度を高速化し、リソースの効率的な利用を実現することができます。

メモリ管理のベストプラクティス
  • 不要なオブジェクトの解放: 不要なオブジェクトを定期的に解放することで、メモリリークを防止しましょう。
  • 大量のデータの処理: 大規模なデータ処理が必要な場合は、メモリを過剰に消費することがあります。代わりに、イテレータやジェネレータを使用してデータを効率的に処理しましょう。
  • メモリフラグメンテーション: メモリフラグメンテーションが発生すると、メモリが不連続に配置されるため、効率的なメモリ使用が妨げられます。適切なメモリ管理を行い、フラグメンテーションを最小限に抑えましょう。
コードの最適化のベストプラクティス
  • 効率的なアルゴリズムの選択: 適切なアルゴリズムを選択することで、処理速度を向上させることができます。時間計算量や空間計算量を考慮して、最適なアルゴリズムを選択しましょう。
  • 不要なループの削減: 不要なループや再帰呼び出しを削減することで、処理速度を向上させることができます。ループのネストを最小限に抑え、効率的なコードを作成しましょう。
  • 遅延読み込み: 大きなデータセットを扱う場合は、遅延読み込みを活用して、必要な時点でデータを取得するようにしましょう。これにより、不要なデータの読み込みを回避し、メモリ使用量を最小限に抑えることができます。
プロファイリングツールの使用

プロファイリングツールは、アプリケーションのパフォーマンスを詳細に分析し、ボトルネックを特定するための有用なツールです。プロファイリングツールを使用することで、どの部分が最もリソースを消費しているかや、どの部分が最も遅いかなどを把握することができます。

代表的なプロファイリングツール
  • RubyProf: Rubyのプロファイリングツールの1つであり、メソッドごとの実行時間や呼び出し回数などを詳細に分析することができます。
  • StackProf: スレッドごとのスタックトレースを収集し、アプリケーションのホットスポットを特定することができるプロファイリングツールです。
  • DTrace: オペレーティングシステムレベルでのパフォーマンス分析を行うための強力なツールであり、Rubyアプリケーションのボトルネックを特定するのに役立ちます。

これらのプロファイリングツールを使用して、アプリケーションのパフォーマンスを詳細に分析し、効率的な最適化を行いましょう。

プロファイリング手法と注意点
  • アプリケーション全体のプロファイリング: アプリケーション全体のプロファイリングを行うことで、全体のパフォーマンスを把握し、最も時間がかかる部分を特定します。この情報をもとに、重点的に最適化を行いましょう。
  • 繰り返し実行による平均化: プロファイリングを行う際には、複数回の実行を行い、結果を平均化することが重要です。これにより、ランダムな外部要因の影響を排除し、より正確なプロファイリング結果を得ることができます。
  • プロファイリングの結果の解釈: プロファイリング結果を解釈する際には、アプリケーションのコードや処理の特性を理解することが重要です。高負荷がかかっている部分やボトルネックを正確に特定し、効果的な最適化を行いましょう。
  • プロファイリングの定期的な実施: アプリケーションのコードや処理が変更されるたびに、プロファイリングを定期的に実施することで、パフォーマンスの変化や問題点を早期に検出し、適切な対策を講じることができます。
  • セキュリティとプライバシー: プロファイリングツールを使用する際には、セキュリティとプライバシーに配慮しましょう。機密情報や個人情報が含まれる可能性がある場合は、適切な対策を講じて情報漏洩を防止しましょう。
総括
メモリ管理、コードの最適化、およびプロファイリングツールの使用は、Rubyプログラミングにおいてアプリケーションのパフォーマンスを向上させるために重要なスキルです。効率的なメモリ管理とコードの最適化により、リソースの効率的な利用を実現し、プロファイリングツールを使用してボトルネックを特定し、効果的な最適化を行うことで、より高速で効率的なアプリケーションを開発することができます。

さらなるトピック

オブジェクト指向デザインパターンの活用

上級レベルでは、オブジェクト指向デザインパターンの理解と実装に焦点を当てましたが、実際のアプリケーション開発では、これらのパターンを活用して柔軟で効率的なコードを書くことが重要です。より実践的な例や、実際の問題に対する解決策を学ぶことで、より高度な開発スキルを身につけることができます。

セキュリティとエラーハンドリング

プロの開発者になるためには、セキュリティとエラーハンドリングの重要性を理解し、適切な対策を講じることが不可欠です。セキュリティホールや悪意のある攻撃からアプリケーションを保護し、エラーが発生した際にユーザーやシステムに影響を与えないようにするための方法を学びましょう。

テスト駆動開発(TDD)

テスト駆動開発(Test-Driven Development, TDD)は、コードを書く前にテストを書き、そのテストをパスするようなコードを実装する開発手法です。TDDを実践することで、より品質の高いコードを生産し、バグの早期発見やリファクタリングの容易化が可能となります。

コード品質とリファクタリング

コード品質は、プロの開発者にとって非常に重要な要素です。コードを読みやすく、保守しやすく、拡張しやすくするためのリファクタリングのテクニックやベストプラクティスを学び、コードベースを常に改善していきましょう。

これらのトピックを網羅的に学ぶことで、より高度なRubyプログラミングスキルを身につけることができます。プロの開発者としてのキャリアを築くために、着実にスキルを向上させていきましょう。

リファレンス編

変数

Rubyの変数は、オブジェクトに名前をつけるために使われます。

Rubyでは、変数は宣言する必要はありません。

オブジェクトを変数に代入するには

変数名 = オブジェクト

の様に代入演算子 = を使います。

変数へのオブジェクトの代入と参照
a = 1
p a           # => 1
a = "abc"
p a           # => "abc"
a = [1, 2, 3]
p a           # => [1, 2, 3]
b = a
p b           # => [1, 2, 3]
b[1] = 999
p a           # => [1, 999, 3]
# bはaと同じ配列を参照しているため、bの変更がaにも反映されます。
puts メソッドと p メソッド
putsメソッドはオブジェクトにto_sメソッドで適用した結果を表示します。

これに対し、pメソッドはオブジェクトにinspectメソッドで適用した結果を表示します。

putsとp
puts "Hello, world!" # => Hello, world!
p "Hello, world!"    # => "Hello, world!"

定数

Rubyにおける定数は、変更できない値のことです。定数は大文字で始まり、一度値を代入するとその後変更することができません。定数を宣言するには、変数名の先頭に大文字のアルファベットを付けます。

定数は、クラスやモジュールの定義内で宣言される場合、そのクラスやモジュールのスコープにのみ存在します。クラスやモジュール外で宣言された定数は、グローバルスコープに属します。

定数にアクセスするには、定数名を参照します。定数が未定義の場合、RubyはNameError例外を発生させます。

以下は、定数を宣言して参照する例です。

# クラス内で定数を宣言する
class MyClass
  MY_CONSTANT = 100
end

# クラス外で定数を参照する
puts MyClass::MY_CONSTANT   #=> 100

また、Rubyでは組み込み定数もいくつか存在します。例えば、Math::PIは円周率を表す定数です。

グローバル変数

グローバル変数は、プログラム中のどこからでもアクセス可能な変数です。グローバル変数は、$記号で始まる変数名を持ちます。一般的に、グローバル変数は、複数のメソッドで共有するデータを格納するために使用されます。ただし、グローバル変数は多用すべきではなく、できるだけ避けるべきです。

$global_variable = 10

def print_global
  puts "Global variable is #$global_variable"
end

print_global

このコードでは、$global_variableというグローバル変数が定義されています。そして、print_globalメソッド内でその値が参照されて表示されます。グローバル変数はプログラム中のどこからでも参照可能であるため、異なるメソッド間でデータを共有する場合に使用されます。しかし、グローバル変数の使用は避けるべきであり、できるだけローカル変数やインスタンス変数を使用することが推奨されます。

特殊変数

特殊変数は、Rubyがあらかじめ定義している変数で、プログラム中で直接代入することができません。これらの変数は、プログラムの実行中に自動的に設定され、Rubyの様々な機能で使用されます。

  • $0: 現在のプログラムファイル名
  • $~: 最後にマッチした正規表現
  • $&: 最後にマッチした文字列
  • $': 最後にマッチした文字列より後ろの文字列
  • $': 最後にマッチした文字列より後ろの文字列
  • $1, $2, $3...: 最後にマッチした正規表現の1番目、2番目、3番目...のキャプチャグループにマッチした文字列
  • $stdin: 標準入力
  • $stdout: 標準出力
  • $stderr: 標準エラー出力
  • $LOAD_PATH: ライブラリの検索パス
  • $:: ライブラリの検索パス($LOAD_PATHの別名)
puts "This program's name is #{$0}"
puts "The Ruby version is #{RUBY_VERSION}"
puts "The current line number is #{$.}"

オブジェクト

Rubyは完全なオブジェクト指向言語であり、すべてがオブジェクトです。オブジェクトは、データとそれに対する操作をカプセル化したものであり、オブジェクトを操作するためにメソッドを使用します。

以下は、オブジェクトについての基本的な説明です。

  • オブジェクトは、Rubyの世界で唯一の存在であり、それぞれが固有の状態と振る舞いを持っています。
  • オブジェクトは、何らかのデータ(文字列、数値、配列、ハッシュ、ファイルなど)を表します。例えば、整数、浮動小数点数、有理数や複素数などの数値を表すオブジェクトがあり、文字列オブジェクトはテキストを表します。
  • オブジェクトは、そのデータを操作するためのメソッドを提供します。メソッドは、オブジェクトの状態を変更することができます。例えば、数値オブジェクトには、加算や減算などの算術演算を行うためのメソッドがあります(演算子もメソッドです)。文字列オブジェクトには、文字列の結合や部分文字列の取得などのメソッドがあります。
  • オブジェクトは、クラスと呼ばれるテンプレートから作成されます。クラスは、オブジェクトのデータやメソッドを定義するための設計図です。オブジェクトは、クラスのインスタンスとして作成されます。
  • オブジェクトは、他のオブジェクトとやり取りすることができます。オブジェクトは、別のオブジェクトをメソッドの引数として受け取ることができ、戻り値としても返すことができます。

これらのオブジェクトのクラスを知ることは、そのオブジェクトがどのような振る舞いをするかを理解するために非常に重要です。

Objectクラス

Rubyにおけるすべてのオブジェクトは、Objectから継承されています。Objectには、すべてのオブジェクトに共通するメソッドが定義されています。

以下は、Objectに関する基本的な説明です。

  • Rubyのすべてのオブジェクトは、Objectのサブクラスであるため、Objectに定義されているメソッドはすべてのオブジェクトで利用できます。
  • Objectには、例外を発生させるraiseメソッドや、オブジェクトのクラスを取得するclassメソッド、オブジェクトが同一かどうかを判定するequal?メソッドなど、多くの便利なメソッドが定義されています。
  • Objectには、to_sメソッドも定義されており、すべてのオブジェクトはto_sメソッドを呼び出すことができます。to_sメソッドは、オブジェクトを文字列に変換します。このメソッドは、デバッグやログ出力などに活用されます。
  • Objectには、同一性比較に使用される==メソッドも定義されています。==メソッドは、2つのオブジェクトが同じかどうかを比較します。オブジェクトの同一性を比較する場合は、equal?メソッドを使用します。
  • Objectには、.initializeメソッドも定義されており、新しいオブジェクトを作成する際に呼び出されます。.initializeメソッドをオーバーライドすることで、新しいオブジェクトが初期化される際の処理をカスタマイズすることができます。

オブジェクトのクラスを調べる

オブジェクトのクラスを調べるには、Objectクラスのclassメソッドを使います。

puts "Hello, world!".class  # ==> String
puts 10.class               # ==> Integer
puts 3.14.class             # ==> Float
puts [2, 3, 5, 7, 11].class # ==> Array
puts ({"key1" => "value1", "key2" => "value2"}).class   # ==> Hash
puts /hello/.class          # ==> Regexp
puts (1..10).class          # ==> Range
puts :abc.class             # ==> Symbol
puts false.class            # ==> FalseClass
puts true.class             # ==> TrueClass
puts nil.class              # ==> NilClass
puts (lambda {|x, y| x + y }).class # ==> Proc
puts Complex(3, 4).class    # ==> Complex
puts Rational(22, 7).class  # ==> Rational
puts Time.now.class         # ==> Time
puts Struct.new("Cat", :sex, :age).class    # ==> Class
Objectクラスの代表的なメソッド
メソッド名 引数の数 説明
!= 1 オブジェクトと引数が等しくない場合にtrue、等しい場合にfalseを返す
! 0 オブジェクトがfalseまたはnilの場合にtrueを返す
== 1 オブジェクトと引数が等しい場合にtrue、等しくない場合にfalseを返す
=== 1 引数がオブジェクトと等しい場合にtrue、そうでない場合にfalseを返す
__id__ 0 オブジェクトのobject_idを返す
__send__ 1以上 メソッドを動的に呼び出す
class 0 オブジェクトのクラスを返す
clone 0 オブジェクトを複製して返す
dup 0 オブジェクトを浅く複製して返す
equal? 1 オブジェクトと引数が同じオブジェクトである場合にtrue、そうでない場合にfalseを返す
instance_eval 0または1 レシーバーオブジェクトに対してブロックを評価する
instance_of? 1 オブジェクトが指定したクラスのインスタンスである場合にtrue、そうでない場合にfalseを返す
instance_variable_defined? 1 指定されたインスタンス変数が定義されている場合にtrue、そうでない場合にfalseを返す
instance_variable_get 1 指定されたインスタンス変数の値を取得する
instance_variable_set 2 指定されたインスタンス変数に値を設定する
kind_of? 1 オブジェクトが指定したクラスのインスタンスである場合にtrue、そうでない場合にfalseを返す
method 1 指定されたメソッドオブジェクトを返す
nil? 0 オブジェクトがnil

式と演算子

Rubyにおける式と演算子について説明します。

Rubyにおける式とは、ある値を評価するためのコードのことを指します。例えば、以下のような式があります。

1 + 2

この式は、12を足した結果を評価するための式です。この式を実行すると、3という値が返されます。

演算子

演算子は、式を組み合わせたり、値を変更するために使用されます。Rubyにはさまざまな種類の演算子があります。以下はいくつかの例です。

数値演算子

数値演算子には、加算、減算、乗算、除算、剰余演算子などがあります。

以下はいくつかの例です。

operators.rb
# 整数
p 10 + 3 # 13: 加算
p 10 - 3 #  7: 減算
p 10 * 3 # 30: 乗算
p 10 / 3 #  3: 除算
p 10 % 3 #  1: 剰余演算

# 文字列
p "abc" + "xyz" # abcxyz: 連結
p "abc" * 3     # abcabcabc: 反復
p "%x" % 77     # 4d: 書式化

# 配列
p [1, 2, 3] + [7, 8, 9] # [1, 2, 3, 7, 8, 9]: 連結

# 有理数
p Rational(2, 3) + 3 # (11/3)
p Rational(2, 3) - 3 # (-7/3)
p Rational(2, 3) * 3 # (2/1)
p Rational(2, 3) / 3 # (2/9)

# 複素数
p (-2)**0.5 # (0.0+1.4142135623730951i)
p Complex(2, 3) + 3 # (5+3i)
p Complex(2, 3) - 3 # (-1+3i)
p Complex(2, 3) * 3 # (6+9i)
p Complex(2, 3) / 3 # ((2/3)+1i)

Rubyの四則演算子と剰余演算子は、通常の数値演算に使用されることが一般的ですが、オブジェクト指向プログラミング言語であるRubyでは、これらの演算子がオーバーロードされることがあります。

数値演算子の使用は、さまざまなデータ型に対して行われます。整数、文字列、配列、有理数、複素数など、Rubyは様々なデータ型をサポートしており、それぞれのデータ型に対して適切な演算が行われます。

例えば、文字列の場合、加算演算子は文字列の連結を行います。同様に、整数や有理数の場合は数値演算が行われますが、複素数の場合は複素数の演算が行われます。

また、Rubyでは四則演算子や剰余演算子などの演算子がオーバーロードされることがあります。これにより、ユーザー定義のクラスやオブジェクトに対して独自の演算を定義することができます。オブジェクト指向の特性を活かした柔軟な演算の定義が可能です。

数値演算子の適切な使用は、プログラムの正確性や効率性に直結します。Rubyの数値演算子を適切に理解し、適切に活用することで、より効率的で読みやすいコードを書くことができます。

比較演算子

比較演算子とComparableモジュールは、Rubyにおいてオブジェクトの比較を可能にする重要な機能です。

比較演算子には以下のようなものがあります:

  • ==: 等しい
  • !=: 等しくない
  • >: より大きい
  • <: より小さい
  • >=: 以上
  • <=: 以下

以下はいくつかの例です。

a = 1
b = 2

puts a == b # 等しい
puts a != b # 等しくない
puts a > b  # より大きい
puts a < b  # より小さい
puts a >= b # 以上
puts a <= b # 以下

これらの演算子を使って、オブジェクト同士を比較することができます。例えば、整数同士や文字列同士の比較などが挙げられます。

Comparableモジュール

Comparableモジュールは、比較可能なオブジェクトを定義するためのモジュールです。このモジュールをクラスにincludeすることで、そのクラスのインスタンス同士を比較可能にすることができます。

Comparableモジュールでは、<=>演算子(スペース船演算子)を実装する必要があります。この演算子は、二つのオブジェクトを比較して以下のような値を返します:

  • 自身が他のオブジェクトより小さい場合は負の整数
  • 自身が他のオブジェクトと等しい場合はゼロ
  • 自身が他のオブジェクトより大きい場合は正の整数

Comparableモジュールを使用することで、クラス内で<=>演算子を定義するだけで、そのクラスのインスタンス同士を比較することができます。これにより、コードの可読性を高め、一貫した比較処理を実現することができます。

例えば、数値や文字列など、大小関係を持つオブジェクトを扱う場合に便利です。また、minmaxなどのメソッドを利用して、比較可能なオブジェクトの最小値や最大値を簡単に取得することもできます。

class Person
  include Comparable

  attr_accessor :name, :age

  def initialize(name, age)
    @name = name
    @age = age
  end

  def <=>(other)
    @age <=> other.age
  end
end

p1 = Person.new("Alice", 25)
p2 = Person.new("Bob", 30)

puts p1 < p2   # true
puts p1 == p2  # false
puts p1 > p2   # false

Personクラスでは、<=>演算子を定義して、ageインスタンス変数を比較しています。そのため、Personクラスのインスタンスは、<, <=, ==, >, >=, between?演算子によって比較可能になります。

論理演算子

論理演算子には、AND、OR、NOT演算子があります。以下はいくつかの例です。

a = true
b = false

puts a && b  # false: AND
puts a and b # true: AND(低い優先度なのでputsと先に結合)
puts a || b  # true: OR
puts a or b  # true: OR(低い優先度)
puts !a      # false: NOT
  • && の優先度は || より強いです(論理と論理なので)。
  • || の優先度は and より強いです(単語版の方が弱いとおぼえてください)。
  • and の優先度は or より強いです(論理積と論理和なので)。

以下に、論理積 (AND)、論理和 (OR)、論理否定 (NOT) を表現するための真理値表を示します。

論理積 (AND)
A B A AND B
0 0 0
0 1 0
1 0 0
1 1 1
論理和 (OR)
A B A OR B
0 0 0
0 1 1
1 0 1
1 1 1
論理否定 (NOT)
A NOT A
0 1
1 0


上記の表で、A と B はそれぞれ真 (1) または偽 (0) の値を表し、AND や OR などの論理演算子によって表された結果は、真 (1) または偽 (0) のいずれかです。 また、論理否定では、NOT A は A の真偽を反転させた値となります。

短絡評価

Rubyの短絡評価とは、論理式の評価において、最初の式で結果が確定した場合に、その後の式を評価しないことを指します。つまり、式が最初の部分で真偽値が確定した場合、後続の式を評価せずに即座に結果を返します。

例えば、以下のようなコードがあったとします。

def check_name(name = nil)
    if name && name.length > 0
        puts "名前は#{name}です"
    else
        puts "名前が未入力です"
    end
end
check_name() # => "名前が未入力です"
check_name("") # => "名前が未入力です" 
check_name("John") # => "名前はJohnです"

このコードでは、namenil でないかつ文字列の長さが0より大きいかを同時にチェックしています。 具体的には、条件式 name && name.length > 0 によってチェックしています。

この条件式では、短絡評価 (short-circuit evaluation) が利用されています。短絡評価とは、条件式の全ての部分を評価せずに、最初に結果が確定した部分で評価を終了するという評価方法です。 具体的には、この場合はnamenilである場合、name.length > 0 の評価は行われず、条件式全体がfalseと判定されます。

このような短絡評価の利用により、コードのパフォーマンスを向上させることができます。また、namenil の場合に name.length を呼び出すことがなくなるため、エラーを避けることができます。

しかし、短絡評価を乱用すると、プログラムの可読性が低下したり、バグを引き起こしたりすることがあります。適切な場面でのみ利用するようにしましょう。

論理演算子(AND, OR)は演算子の姿をしていますが、内実は制御構造です。そのため論理演算子は演算子オーバーロード出来ません。

Rubyでは演算子もメソッド

「Rubyでは演算子もメソッド」とは、Rubyにおいて演算子 (たとえば +, -, *, / など) が単なる記号ではなく、それぞれが対応するメソッドとして定義されているということを指します。

例えば、+ 演算子は、2つの数値を足し合わせるために使われますが、実際には + メソッドとして定義されています。以下のように + メソッドを使って足し算を行うことができます。

a = 1
b = 2
c = a.+(b)  # a + b と同じ
puts c  # => 3

また、* 演算子も同様に * メソッドとして定義されています。以下のように、文字列に対して * メソッドを使って、指定した回数だけ繰り返した新しい文字列を作ることができます。

str = "hello"
new_str = str.*(3)  # str * 3 と同じ
puts new_str  # => "hellohellohello"

このように、Rubyでは演算子も単なる記号ではなく、それぞれが対応するメソッドとして定義されているため、オブジェクト指向の考え方に基づいた柔軟なプログラミングが可能になっています。

Rubyで演算子メソッドをオーバーロード

Rubyでは、クラスやモジュールで演算子に対応するメソッドを定義することができます。このようにして定義された演算子メソッドをオーバーロードと呼びます。

以下は、Vector2 クラスで + 演算子に対応するメソッドを定義する例です。このメソッドでは、Vector2 クラスのインスタンス同士を足し合わせることができます。

class Vector2
  attr_accessor :x, :y
  
  def initialize(x, y)
    @x = x
    @y = y
  end
  
  def +(other)
    Vector2.new(@x + other.x, @y + other.y)
  end
  
  def to_s()
    "[#{@x}, #{@y}]"
  end
end

v1 = Vector2.new(1, 2)
v2 = Vector2.new(3, 4)
v3 = v1 + v2  # => Vector2オブジェクト
puts v3  # => [4, 6]

このコードは、2次元ベクトルを表す Vector2 クラスを定義し、2つのベクトルを足し合わせるメソッドを実装しています。

まず、attr_accessor によって、xy のインスタンス変数に対する getter/setter メソッドが定義されています。これによって、外部から v1.x のようにしてインスタンス変数にアクセスしたり、v1.x = 10 のようにしてインスタンス変数に値を代入したりできます。

次に、initialize メソッドが定義されています。これは、new メソッドが呼ばれた時に、引数として渡された xy をインスタンス変数 @x@y に代入するためのコンストラクタです。

+ メソッドは、引数として与えられた other という別の Vector2 オブジェクトを足し合わせ、新しい Vector2 オブジェクトを作成して返します。これによって、v1 + v2 のようにして2つのベクトルを足し合わせることができます。

最後に、to_s メソッドが定義されています。これは、オブジェクトを文字列に変換するためのメソッドで、ベクトルの座標を [x, y] のような形式の文字列に変換して返します。

実際に、v1v2 を足し合わせた結果を v3 に代入し、puts v3v3 を表示しています。v3 の値は [4, 6] となります。

このように演算子メソッドをオーバーロードすることで、プログラムの読みやすさや柔軟性を高めることができます。ただし、意図しない挙動を招くこともあるため、注意して使いましょう。

演算子オーバーロード

Rubyでは、演算子をオーバーロードすることが出来ます。 オーバーロード可能な演算子には、組み込みクラスの演算子も含まれます。

class String
  def /(s) = split(s)
end

ary = "The quick brown fox jumps over the lazy dog" / " "
p ary # => ["The", "quick", "brown", "fox", "jumps", "over", "the", "lazy", "dog"]

str = ary * " ! " 
p str # => "The ! quick ! brown ! fox ! jumps ! over ! the ! lazy ! dog"

このコードは、Rubyの String クラスに、新しいメソッド / を追加しています。

/ メソッドは、文字列を引数 s で分割するためのメソッドです。 実装は split メソッドを呼び出すだけで、文字列オブジェクト自身を self で表しています。

まず、/ メソッドによって、文字列 "The quick brown fox jumps over the lazy dog" がスペース " " で分割され、配列 ary に格納されます。 ここで ary は、["The", "quick", "brown", "fox", "jumps", "over", "the", "lazy", "dog"] という値を持ちます。

次に、* メソッドによって、配列 ary が感嘆符 "!" で連結され、文字列 str が作成されます。 ここで str は、"The ! quick ! brown ! fox ! jumps ! over ! the ! lazy ! dog" という値を持ちます。

このように、/* メソッドを利用することで、簡単に文字列の分割や連結を行うことができます。 ただし、他の開発者がこのようなメソッドを利用しているコードを読む場合、このような振る舞いを予想できない可能性があるため、注意が必要です。

/* を、それぞれ文字列の分割と結合に割り当てる例は、Pikeに見ることが出来ます。

演算子の優先順位と結合方向

演算子の優先順位

演算子には優先順位があります。

例えば、以下のようなコードがあった場合、演算子の優先順位に従って評価されます。

a = 2 + 3 * 4 ** 2 / 2

ここで、**は先に評価されます。次に、*と/が同じ優先順位なので、左から右に評価されます。最後に+が-よりも優先度が高いので、+が評価されます。

a = 2 + ( ( 3 * ( 4 ** 2 ) ) / 2 )

つまり、aの値は50になります。

演算子の結合方向

同じ優先順位の演算子が続いた場合、演算子の結合方向により実行順序が決まります。

例えば、以下のコードがある場合、

a = 2 ** 3 ** 2

**右結合なので、3 ** 2が先に評価されます。そして、2 ** 9が評価されて、aは512になります。

a = 2 ** ( 3 ** 2 )

以下は、Rubyの演算子の優先順位と結合方向の表です。

Rubyの演算子の優先順位と結合方向
演算子 説明 優先順位 結合方向
:: クラスやオブジェクトのネスト 1 左結合
[] 配列やハッシュから要素を取得する 2 左結合
+ 単項演算子(正) 3 右結合
! 論理否定
~ ビット否定
** べき乗 4 右結合
- 単項演算子(負) 5 右結合
* 乗算 6 左結合
/ 除算
% 剰余算
+ 加算 7 左結合
- 減算
<< ビットシフト(左) 8 左結合
>> ビットシフト(右)
& ビット積 9 左結合
| ビット和 10 左結合
^ ビット排他的論理和
> 大なり 11 左結合
>= 以上
< 未満
<= 以下
<=> 比較 12 左結合
== 一致
=== 厳密に一致
!= 不一致
=~ パターンマッチング
!~ アンマッチ
&& 論理積 13 左結合
|| 論理和 14 左結合
.. 範囲を生成する(終端を含む) 15 左結合
... 範囲を生成する(終端を含まない)
? : 条件演算子 16 右結合
= 代入 17 右結合
op= 代入演算子
not 論理否定 18 右結合
and 論理積 19 左結合
or 論理和
優先順位が高い演算子から順に、結合方向の左右を記載しています。
表中の "op= " は、代入演算子の一般形を表しています。具体的な代入演算子には、"+= "、"-= "、"*= "、"/= "、"%= "、"||= "、"&&= "、"||= "、"|= "、"&= "、"^= "などがあります。
コード例
a = 5
b = 10
puts "a = #{a}"
puts "b = #{b}"
puts "-" * 20

# 算術演算子
puts "算術演算子:"
puts "a + b = #{a + b}"
puts "a - b = #{a - b}"
puts "a * b = #{a * b}"
puts "b / a = #{b / a}"
puts "b % a = #{b % a}"
puts "a ** 2 = #{a ** 2}"
puts "-" * 20

# 比較演算子
puts "比較演算子:"
puts "a == b : #{a == b}"
puts "a != b : #{a != b}"
puts "a > b : #{a > b}"
puts "a < b : #{a < b}"
puts "b >= a : #{b >= a}"
puts "a <= b : #{a <= b}"
puts "-" * 20

# 論理演算子
puts "論理演算子:"
puts "a == 5 && b == 10 : #{a == 5 && b == 10}"
puts "a == 5 || b == 5 : #{a == 5 || b == 5}"
puts "! (a == 5) : #{!(a == 5)}"
puts "-" * 20

# 代入演算子
puts "代入演算子:"
puts "a += b : #{a += b}"
puts "a -= b : #{a -= b}"
puts "a *= b : #{a *= b}"
puts "a /= b : #{a /= b}"
puts "a %= b : #{a %= b}"
puts "a **= 2 : #{a **= 2}"
puts "-" * 20

# 三項演算子
puts "三項演算子:"
puts "a > b ? 'a is greater than b' : 'a is less than or equal to b'"
puts "-" * 20

# ビット演算子
puts "ビット演算子:"
puts "a & b : #{a & b}"
puts "a | b : #{a | b}"
puts "a ^ b : #{a ^ b}"
puts "~a : #{~a}"
puts "a << 1 : #{a << 1}"
puts "a >> 1 : #{a >> 1}"
puts "-" * 20

# 演算子メソッド
puts "演算子メソッド:"
puts "a == b : #{a.==(b)}"
puts "a != b : #{a.!=(b)}"
puts "a > b : #{a.>(b)}"
puts "a < b : #{a.<(b)}"
puts "b >= a : #{b.>=(a)}"
puts "a <= b : #{a.<=(b)}"
puts "a <=> b : #{a.<=>(b)}"
puts "a === b : #{a.===(b)}"
puts "a =~ b : #{a.=~(b)}"
puts "-" * 20

# その他の演算子
puts "その他の演算子:"
puts "defined? a : #{defined?(a)}"
puts "(1..10).each do |i| puts i end"
puts ":symbol.to_s : #{:symbol.to_s}"
puts "%w(one two three) : #{%w(one two three)}"
puts "-" * 20
実行結果
a = 5
b = 10
--------------------
算術演算子:
a + b = 15
a - b = -5
a * b = 50
b / a = 2
b % a = 0
a ** 2 = 25
--------------------
比較演算子:
a == b : false
a != b : true
a > b : false
a < b : true
b >= a : true
a <= b : true
--------------------
論理演算子:
a == 5 && b == 10 : true
a == 5 || b == 5 : true
! (a == 5) : false
--------------------
代入演算子:
a += b : 15
a -= b : 5
a *= b : 50
a /= b : 5
a %= b : 5
a **= 2 : 25
--------------------
三項演算子:
a > b ? 'a is greater than b' : 'a is less than or equal to b'
--------------------
ビット演算子:
a & b : 8
a | b : 27
a ^ b : 19
~a : -26
a << 1 : 50
a >> 1 : 12
--------------------
演算子メソッド:
a == b : false
a != b : true
a > b : true
a < b : false
b >= a : false
a <= b : false
a <=> b : 1
a === b : false
a =~ b :
--------------------
その他の演算子:
defined? a : local-variable
(1..10).each do |i| puts i end
:symbol.to_s : symbol
%w(one two three) : ["one", "two", "three"]
--------------------

制御構造

Rubyには、if-else-elsif-end、unless-else-end、case-when-else-end、case-in-else-end、while文、until文、for文、そしてbegin-rescue-else-ensure-endのような例外処理構文など、様々な制御構造があります。 これらの制御構造を使うことで、プログラムの実行を条件に応じたループや分岐によって制御することができます。 制御構造を正しく理解し、適切に使用することで、効率的で洗練されたプログラムを書くことができます。

また広義の制御構造にはブロックを伴う each や loop などのメソッドも含まれ、ここではそれらについても解説します。

書式概観
# if-elsif-else-end
if 条件式1 then
  処理1
elsif 条件式2 then
  処理2
else
  処理3
end

# if修飾子
 if 条件式

# unless-else-end
unless 条件式 then
  処理1
else
  処理2
end

# unless修飾子
 unless 条件式

# case-when-else-end
case 対象のオブジェクト
when 条件式1 then
  処理1
when 条件式2 then
  処理2
when 条件式3 then
  処理3
else
  処理4
end

# while文
while 条件式 do
  処理
end

# while修飾子
 while 条件式

# until文
until 条件式 do
  処理
end

# until修飾子
 until 条件式

# for文
for 変数 in 範囲 do
  処理
end

# loopメソッド
loop do
  処理
end

# eachメソッド
コレクション.each do |変数|
  処理
end

# timesメソッド
回数.times do |変数|
  処理
end

条件分岐

Rubyの条件分岐には、if-elsif-else-end、unless-else-end、そしてcase-when-else-endがあります。 また、if, unless には修飾子構文もあります。

if-elsif-else-end

if-elsif-else-endは、条件に応じて処理を分岐するためのもっとも基本的な構文です。

if 条件式1
  処理1
elsif 条件式2
  処理2
else
  処理3
end

例えば、ある数値変数numが0未満なら「negative」、0なら「zero」、0より大きいなら「positive」と表示するプログラムは以下のように書けます。

num = -3

if num < 0
  puts "negative"
elsif num == 0
  puts "zero"
else
  puts "positive"
end
if修飾子

if修飾子は、条件式が肯定されたとき式を評価する構文です。

 if 条件式

例えば、ある数値変数numが0未満なら「negative」と表示するプログラムは以下のように書けます。

num = -3

puts "negative" if num < 0
unless-else-end

unless-else-endは、if-elsif-else-endと似たような構文ですが、条件式が否定されたときに処理が実行されます。

unless 条件式
  処理1
else
  処理2
end

例えば、ある数値変数numが0以上なら「non-negative」、0未満なら「negative」と表示するプログラムは以下のように書けます。

num = -3

unless num >= 0
  puts "negative"
else
  puts "non-negative"
end
unless修飾子

unless修飾子は、if修飾子に似ていますが、条件式が否定されたときに式を評価する構文です。

 unless 条件式

例えば、ある数値変数numが0以上でなければ「negative」と表示するプログラムは以下のように書けます。

num = -3

puts "negative" unless num >= 0
case-when-else-end

case-when-else-endは、複数の値に応じて処理を分岐するための構文です。if-elsif-else-endと比較して、1つのオブジェクトに対して複数の条件式を一度に評価できるという利点があります。

case オブジェクト
when 条件式1
  処理1
when 条件式2
  処理2
else
  処理3
end

例えば、ある文字変数cに対して、それが母音なら「vowel」、子音なら「consonant」、数字なら「number」、それ以外なら「other」と表示するプログラムは以下のように書けます。

c = 'a'

case c
when 'a', 'e', 'i', 'o', 'u'
  puts "vowel"
when '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'
  puts "number"
when 'b', 'c', 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'm', 'n', 'p', 'q', 'r', 's', 't', 'v', 'w', 'x', 'y', 'z'
  puts "consonant"
else
  puts "other"
end
when へのオブジェクト

Rubyの case 文では、比較に用いるオブジェクトの型に応じて条件分岐することができます。具体的には、case 文の式の型が以下のいずれかである場合に、対応する条件分岐が行われます。

  • 整数型 (Integer) の場合
  • 文字列型 (String) の場合
  • 正規表現型 (Regexp) の場合
  • クラスオブジェクト (Class) の場合

以下に、それぞれの場合の例を示します。

整数型の場合
case num
when 0
  puts "num is zero"
when 1
  puts "num is one"
when 2
  puts "num is two"
else
  puts "num is something else"
end
文字列型の場合
case str
when "foo"
  puts "str is foo"
when "bar"
  puts "str is bar"
when /baz/
  puts "str includes baz"
else
  puts "str is something else"
end
正規表現型の場合
case str
when /foo/
  puts "str includes foo"
when /bar/
  puts "str includes bar"
when Regexp.new("baz")
  puts "str includes baz"
else
  puts "str is something else"
end
クラスオブジェクトの場合
case obj
when Integer
  puts "obj is an instance of Integer"
when String
  puts "obj is an instance of String"
when Array
  puts "obj is an instance of Array"
else
  puts "obj is an instance of something else"
end

これらの例では、case 文の式の型によって、比較に用いるオブジェクトの型や正規表現パターンが異なることに注意してください。また、どの条件にも一致しない場合に実行される else 節は省略可能です。

Rubyのif,unless,caseは式
Rubyの ifunlesscase は、制御フロー構文としての使い方に加えて、式としても使うことができます。

例えば、if 式を使ってみましょう。通常の if は、条件が true ならば処理を実行します。

if x > 10
  puts "x is greater than 10"
end

この if 文を式として使うこともできます。具体的には、以下のように if の後ろに式を書き、式の値が true であれば、if の条件式の評価値が式の値となります。

a = if x > 10
      "x is greater than 10"
    else
      "x is less than or equal to 10"
    end
puts a

同様に、unless も式として使うことができます。

a = unless x > 10
      "x is less than or equal to 10"
    else
      "x is greater than 10"
    end
puts a

また、case 式もあります。case 式は、複数の条件に対する分岐処理を行う場合に使われます。以下は、case 式の例です。

a = case x
    when 1
      "one"
    when 2
      "two"
    else
      "other"
    end
puts a
case 式では、when 句を使って、複数の条件式を書くことができます。case 式の評価値は、when 句の条件式と一致する最初のものの右辺の式の値となります。

パターンマッチング

Rubyのパターンマッチングは、バージョン2.7で導入された新機能であり、構造化された値に対してパターンをマッチングすることができます。これにより、より複雑なデータ構造を柔軟に操作できるようになります。

以下に、Rubyのパターンマッチングの基本的な構文と機能について説明します。

基本的な構文
case 
in パターン1
  # パターン1にマッチする場合の処理
in パターン2
  # パターン2にマッチする場合の処理
else
  # どのパターンにもマッチしない場合の処理
end
パターン

パターンは以下のようなものがあります:

  • 値パターン(Value pattern)
  • 配列パターン(Array pattern)
  • ハッシュパターン(Hash pattern)
  • 検索パターン(Find pattern)
  • 変数キャプチャ(Variable capture)
  • その他のパターン(Alternative pattern)
変数バインディング

パターンマッチングによって、マッチした部分をローカル変数にバインドすることができます。

case [1, 2]
in Integer => a, Integer
  puts "マッチしました: #{a}"
else
  puts "マッチしませんでした"
end


ガード節

ifを使用して、パターンがマッチした場合に追加の条件を指定することができます。

case [1, 2]
in a, b if b == a * 2
  puts "マッチしました"
else
  puts "マッチしませんでした"
end
パターンの合成

複数のパターンを組み合わせてマッチングを行うこともできます。

case [1, 2]
in [1, Integer]
  puts "マッチしました"
else
  puts "マッチしませんでした"
end
例外の扱い

パターンマッチングが失敗した場合には、例外が発生します。これは、NoMatchingPatternErrorとして知られています。

メソッドの利用

カスタムクラスに対してパターンマッチングを行う場合、deconstructdeconstruct_keysメソッドを実装することでマッチングを行うことができます。

パターンマッチングの利点
  • より読みやすいコードを書くことができます。
  • より安全なコードを書くことができます。
  • より柔軟なデータ操作が可能になります。

以上が、Rubyのパターンマッチングについての基本的な説明です。この機能は、プログラミングのパラダイムをより関数型プログラミング寄りのスタイルに変えることができます。

反復

Rubyの反復構文は、特定の条件が満たされるまでコードのブロックを繰り返すことができる文のセットです。最も一般的な反復文は、while、until、for、loop です。

  • while文は、ある条件が真である限り、コードのブロックを実行します。
  • until文は、ある条件が偽である限り、コードのブロックを実行します。
  • for文は、要素のコレクションを繰り返し処理し、各要素に対してコードのブロックを実行します。

また、while, until には修飾子構文もあります。

これらの基本的な反復処理文に加えて、Ruby には break、next、redo、lazy などの反復処理文があります。これらの文により、コードの実行の流れを制御することができます。

while文
# このコードでは、1から10までの数字を表示します。
while i <= 10 do
  puts i
  i += 1 
end

while文の構文は以下の通り:

構文
while 条件 do
  # 処理
end

条件とは、ループの実行を継続するためにtrueと評価されなければならないブール値の式です。 処理の部分は、条件が真である限って実行されるコードのブロックです。

まえの例では、条件は「i <= 10」です。これは、iの値が10以下である限り、ループが実行され続けることを意味します。 処理の部分は、puts i行です。この行はiの値をコンソールに出力します。

while文は、特定の条件が満たされる限り、コードのブロックを繰り返すことができる強力なツールです。

while修飾子

while修飾子は、if修飾子に似ていますが、条件式が肯定されている間は式を評価し続ける構文です。

 while 条件式

例えば、ある数値変数numが0未満の間、num に 2 を足し続けるプログラムは以下のように書けます。

num = -3

num += 2 while num < 0
until文
# このコードでは、10から1までの数字を表示します。
i = 10 
until i <= 1 do
  puts i
  i -= 1 
end

until文の構文は以下の通り:

構文
until 条件 do
  # 処理
end

条件とは、ループの実行を継続するためにfalseと評価されなければならないブール値の式です。 処理の部分は、条件が偽である限って実行されるコードのブロックです。

まえの例では、条件はi <= 1である。これは、iの値が1以上である限り、ループが実行され続けることを意味します。 処理の部分は、puts i行です。この行はiの値をコンソールに出力します。

until文は、特定の条件が満たされない限り、コードのブロックを繰り返すために使用できる強力なツールです。

until修飾子

until修飾子は、while修飾子に似ていますが、条件式が否定されている間は式を評価し続ける構文です。

 until 条件式

例えば、ある数値変数numが0以上でない間、num に 2 を足し続けるプログラムは以下のように書けます。

num = -3

num += 2 until num >= 0
for文
# このコードでは、配列の要素を順に表示します。
array = [2, 3, 5, 7, 11] 
for element in array
  puts element 
end

puts element #=> 11 ## forループを抜けてもスコープは終わらない

for文の構文は以下の通り:

構文
for ループ変数 in コレクション
  # ループ変数にまつわる処理
end

ループ変数は、コレクション内の現在の要素のプレースホルダーです。 コレクションは、配列、範囲、その他の反復可能なオブジェクトのいずれでもかまいません。 ループ変数にまつわる処理の部分は、コレクション内の各要素に対して実行されるコードのブロックです。

この例では、コレクションは配列[2, 3, 5, 7, 11]です。ループ変数はelementです。 ループ変数にまつわる処理の部分は、puts element行です。この行は、elementの値をコンソールに表示します。

for文は、要素のコレクションを繰り返し処理するために使用できる強力なツールです。

反復制御文

Rubyには、反復制御を行う文が5つあります。

  • break : break文は、現在のループを終了させる。
  • next : 現在のループの繰り返しをスキップする。
  • redo : 現在のループの繰り返しを再び実行する。
  • retry: 例外処理をリトライする。
  • return : メソッドを終了します。現在のループの繰り返しも終了します。

反復制御文は、Rubyのコードをより効率的に、より読みやすくするために使用できる強力なツールです。

break

break文は、現在のループを終了させます。

(1..).step(12) do |i|
  break if i >= 50
  puts i
end
#=> 1
#   13
#   25
#   37 
#   49

このコードは、1から始まり、12ずつ増加する無限の数列を生成し、その数列の各要素をdo..endブロック内で処理しています。各要素は変数iに割り当てられ、puts文を使って出力されます。

しかし、break if i >= 50の部分によって、i50以上になった場合、ループが終了します。つまり、ループは50未満の値のときだけ実行されます。

このコードは、1, 13, 25, 37, 49といった50未満の数列を出力します。そして、i50以上の値になった時点でループが終了します。

next

next文は、ループの現在の反復をスキップさせます。

1.upto(10) do |i|
  next if i == 5
  puts i
end
#=> 1
#   2
#   3
#   4
#   6
#   7
#   8
#   9
#   10

このコードは、1から10までの数列を生成し、その数列の各要素をdo..endブロック内で処理しています。各要素は変数iに割り当てられ、puts文を使って出力されます。

next if i == 5の部分によって、i5の場合、それ以降の処理をスキップして次の要素の処理に進みます。つまり、5は出力されません。

したがって、このコードは1, 2, 3, 4, 6, 7, 8, 9, 10という数列を出力します。5がスキップされ、それ以外の値が出力されます。

redo

redo文は、現在のループの繰り返しを再度実行します。

count = 0
(1..10).each do |i|
  count += 1
  redo if i == 5 and count < 20
  puts "#{count}: #{i}"
end
#=> 1: 1
#   2: 2
#   3: 3
#   4: 4
#   20: 5
#   21: 6
#   22: 7
#   23: 8
#   24: 9 
#   25: 10
retry

例外が発生した場合に、その処理をやり直します。

begin
  # 何らかの処理
rescue SomeException
  retry
end
return

メソッド内でループを含む場合、returnを使ってループからも抜けることができます。以下はその例です。

def example_method
  (1..10).each do |i|
    return if i == 5
    puts i
  end
end

example_method

この場合、returnが呼び出されると、example_method自体が終了し、その場でループも終了します。

これらのキーワードやメソッドを使うことで、ループの挙動を制御することができます。

反復メソッド

Rubyでは、反復処理を用いて演算を行うメソッドを反復処理メソッドと呼びます。 反復処理とは、コードのブロックを指定された回数だけ、あるいは特定の条件が満たされるまで繰り返す処理のことです。

コレクションのイテレーションメソッド

Rubyには、ArrayやHashなどのコレクションオブジェクトを簡単にイテレーションできる多数のメソッドがあります。以下はいくつかの例です:

# eachメソッド
array = [1, 2, 3]
array.each { |x| puts x }

# mapメソッド
array = [1, 2, 3]
new_array = array.map { |x| x * 2 }

# selectメソッド
array = [1, 2, 3]
new_array = array.select { |x| x.even? }

# reduceメソッド
array = [1, 2, 3]
sum = array.reduce(0) { |total, x| total + x }

# each_with_indexメソッド
array = [1, 2, 3]
array.each_with_index { |value, index| puts "#{index}: #{value}" }

上記の例では、以下のように動作しています。

eachメソッド
配列の各要素に対して、ブロック内の処理を1回ずつ実行します。この例では、配列の各要素を出力しています。
mapメソッド
配列の各要素に対して、ブロック内の処理を1回ずつ実行し、その結果を新しい配列に格納します。この例では、配列の各要素を2倍にして新しい配列を作成しています。
selectメソッド
配列の各要素に対して、ブロック内の条件がtrueになる要素だけを抽出して、新しい配列に格納します。この例では、配列の偶数要素だけを抽出して新しい配列を作成しています。
reduceメソッド
配列の各要素に対して、ブロック内の演算を順番に適用して、最終的な結果を返します。この例では、配列の各要素を加算して合計を求めています。
each_with_indexメソッド
配列の各要素とそのインデックスに対して、ブロック内の処理を1回ずつ実行します。この例では、配列の各要素とそのインデックスを出力しています。

これらのメソッドを使用することで、コレクションの要素に簡単にアクセスできます。また、each_with_indexを使用することで、要素のインデックスにも簡単にアクセスできます。

暗黙のブロック引数

Rubyには、様々なメソッドやイテレータで使用される暗黙のブロック引数があります。これらは _1, _2 などの形式で表され、ブロック内で引数を明示的に書く必要がなくなります。代表的なものを以下に示します。暗黙のブロック引数はRuby 2.7からサポートされました。

  1. each メソッドなどのイテレータでの使用:
    numbers = [1, 2, 3, 4, 5]
    numbers.each { |num| puts num } # 通常の引数を使用する場合
    
    numbers.each { puts _1 } # `_1` を使用することで、暗黙の引数として要素を取得
    
    hash = { a: 1, b: 2, c: 3 }
    hash.each { |key, value| puts "#{key}: #{vale}" }
    
    hash.each { puts "#{_1}: #{_2}" }
    
  2. map メソッドでの使用:
    doubled = numbers.map { |num| num * 2 } # 通常の引数を使用する場合
    
    doubled = numbers.map { _1 * 2 } # `_1` を使用することで、暗黙の引数として要素を取得して計算
    

このように、暗黙のブロック引数は通常の引数の代替として使われ、ブロック内でのコードをより簡潔にすることができます。ただし、可読性の観点から、コードが複雑になりすぎないよう注意が必要です。

整数のイテレーションメソッド

Rubyには、数値を連続して扱うための便利なメソッドがあります。その中でもupto、downto、stepはよく使われるものです。これらのメソッドについて解説します。

# uptoの例
1.upto(5) do |i|
  puts i
end
# 実行結果: 1 2 3 4 5

# downtoの例
5.downto(1) do |i|
  puts i
end
# 実行結果: 5 4 3 2 1

# stepの例
1.step(10, 2) do |i|
  puts i
end
# 実行結果: 1 3 5 7 9

上記の例では、以下のように動作しています。

uptoメソッド
整数オブジェクトから別の整数オブジェクトまで、1ずつ増加させながら、ブロック内の処理を繰り返します。この例では、1から5までの整数を順に出力しています。
downtoメソッド
整数オブジェクトから別の整数オブジェクトまで、1ずつ減少させながら、ブロック内の処理を繰り返します。この例では、5から1までの整数を順に出力しています。
stepメソッド
開始値から終了値まで、指定されたステップで数値を増加させながら、ブロック内の処理を繰り返します。この例では、1から10までの奇数を順に出力しています。stepメソッドで

は、第2引数に指定した数値が増加量として使用されます。この例では、2ずつ増加しているため、1, 3, 5, 7, 9という結果になっています。

loopメソッド

過去の編集で、loopをloop文と紹介されていましたが、loopはKernelオブジェクトのメソッドで構文ではありません。

loopメソッドの使い方は以下の通り:

loop do
  # 処理
end

処理の部分は、無限に実行されるコードのブロックです。

# このコードは、永久に "This will print forever!" を表示します。
loop do 
 puts "This will print forever!" 
end

この例では、処理パートは puts "This will print forever!"です。 この行は、"This will print forever!"というメッセージをコンソールに無限に出力します。

loopとEnumrator
enum = [1, 2, 3].each

puts enum.class  #=> Enumerator

loop do
  puts enum.next
end
    #=> 1
    #   2
    #   3

enum[1, 2, 3].eachから生成されたEnumeratorです。Enumeratorは、eachメソッドをブロックなしで呼び出すことで生成され、配列内の各要素を反復処理することができます。

その後、loop構文が使われています。loop構文は、無限ループを作成するためのもので、ループ内のコードを繰り返し実行します。

ループ内では、enum.nextが呼び出されています。これにより、Enumeratorから次の要素が取得され、その要素がputsメソッドを使って出力されます。最初は1が出力され、次に2、そして3が出力されます。

しかし、3を出力した後、Enumeratorからはもう要素がないため、StopIteration例外が発生します。この例外がloop構文によって自動的に捕捉され、ループが終了します。

反復メソッドと反復制御文

break、next、redo文もこれらのメソッドと一緒に使うことができます。

たとえば、次のようなものです:

  • break : break 文は、ループの現在の繰り返しを終了させます。
array = [1, 2, 3]
array.each do |element|
  puts element
  break if element == 2
end

このコードでは、配列の要素のうち、要素2までを表示します。

  • next : next文は、ループの現在の繰り返しをスキップする。
array = [1, 2, 3]
array.each do |element|
  puts element
  next if element == 2
end

このコードでは、配列の要素を、要素2をスキップして表示します。

  • redo : redo文は、ループの現在の繰り返しを再度実行します。
array = [1, 2, 3]
array.each do |element|
  puts element
  redo if element == 2
end

このコードは、配列の要素を表示しますが、要素2を無限に表示します。

イテレーションとブロック

Rubyのイテレーションメソッドは、繰り返し処理を行うためのメソッドで、通常はブロックを引数に取ります。ブロックは、繰り返し処理を行うためのコードブロックで、メソッドに渡されたオブジェクトの要素に対して、一つずつ実行されます。

イテレーションメソッドに渡されるブロックには、引数を指定することができます。引数は、ブロック内で処理する要素を表します。引数を指定する方法には、以下の2つがあります。

パイプ(| |)で囲んだ引数を指定する方法
array = [1, 2, 3]
array.each do |x|
  puts x
end
# 出力結果: 1, 2, 3
ブロック引数を指定する方法
array = [1, 2, 3]
array.each { puts _1 }
# 出力結果: 1, 2, 3

このように、イテレーションメソッドは、ブロックを引数に取り、ブロック内で要素に対する処理を実行するため、とても柔軟性が高く、コードの再利用性を高めることができます。

eachメソッドを使ったハック
普段あまり使うことはありませんが、配列のeachメソッドは戻値を返します。

通常は配列自身ですが、breakでループを中断すると nil を返します。 この特徴を利用すると、ZigやPythonのループと結合したelseの様に「ループを完走したときだけ実行する処理」を記述できます。

100以下の素数を求めるコード
primes = []
(2..100).each do |i|
  primes.push(i) if primes.each do |prime|
    break if i % prime == 0
  end
end
p primes
実行結果:
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]
素数集合(primes)から1つ素数(prime)を取出し、候補(i)をそれで割り、割切れたら break します。
break すると each は nil を返し、素数集合への候補の追加は行われません。

例外処理

Rubyにおける例外処理は、プログラムが想定外のエラーに遭遇した場合に、プログラムの強制終了を防ぐために使用されます。例外処理を使用すると、プログラムはエラーが発生しても継続して実行されるため、エラーに含まれる情報をログファイルに記録したり、エラーメッセージを表示したりすることができます。

以下は、Rubyで例外処理を行うための基本的な書式です。

begin
  # 例外が発生する可能性のある処理
rescue [エラークラス => 変数]
  # エラークラスが発生した場合に実行する処理
else
  # 例外が発生しなかった場合に実行する処理
ensure
  # 必ず実行される処理
end

例外処理の流れは、まずbeginブロック内で例外が発生する可能性のある処理を書きます。そして、rescueブロックで発生しうる例外の種類を指定し、例外が発生した場合に行う処理を書きます。elseブロックは、例外が発生しなかった場合に実行する処理を書く部分です。ensureブロックには、必ず実行される処理を書きます。

以下は、例外処理を用いたコードの例です。

def divide(a, b)
  begin
    result = a / b
  rescue ZeroDivisionError => e
    puts "Error: #{e.message}"
    result = nil
  else
    puts "Result is #{result}"
  ensure
    puts "Process finished"
  end
  return result
end

puts divide(10, 0)
puts divide(10, 2)

この例では、divideメソッド内で引数bが0である場合にZeroDivisionErrorが発生する可能性があるため、beginブロック内で計算を行っています。また、rescueブロックではZeroDivisionErrorを補足して、エラーメッセージを表示しています。elseブロックでは、計算結果を表示しています。ensureブロックでは、処理が必ず実行されるように設定しています。

このコードを実行すると、以下のような結果が得られます。

Error: divided by 0
Process finished
nil
Result is 5
Process finished
5

修飾子

Rubyでは、制御構造の修飾子版も提供されています。これは一行で短く書くことができ、可読性を向上させる場合があります。

制御構造の修飾子版には、以下のものがあります:

if修飾子
puts "Hello" if flag

この場合、"Hello"はflagが真の場合にのみ出力されます。

unless修飾子
puts "Hello" unless cond

この場合、"Hello"はcondが偽の場合にのみ出力されます。

while修飾子
x = 0
puts x += 1 while x < 5

この場合、ループが続行される条件が満たされている間、xの値がインクリメントされ、その値が出力されます。

until修飾子
x = 0
puts x += 1 until x == 5

この場合、ループが続行される条件が満たされるまで、xの値がインクリメントされ、その値が出力されます。

rescue修飾子

Rubyのrescue修飾子は、単一行の式での例外処理を行うために使用されます。通常のbegin..rescue..endブロックと異なり、修飾子版はコードを短く保ち、よりコンパクトに書くことができます。

rescue修飾子の構文は以下の通りです:

1 rescue 2

式1が例外を発生させた場合、rescue修飾子はその例外をキャッチし、代わりに式2を評価します。

以下はrescue修飾子を使用した例です:

result = risky_operation() rescue default_value

この例では、risky_operationというメソッドを呼び出しますが、もし例外が発生した場合には、デフォルト値を代入することでエラーハンドリングを行っています。

rescue修飾子は、例外が発生する可能性のある単一の式を簡潔に記述する場合に便利ですが、複雑な例外処理を行う場合や、複数の式に対する例外処理を行う場合には、通常のbegin..rescue..endブロックを使用する方が適しています。

これらの修飾子版は、単純な条件の場合や短いコードブロックの場合に便利です。 ただし、複雑な制御構造や複数の条件がある場合は、通常の制御構造を使用した方が可読性が高くなります。

メソッド

Rubyのメソッドとは、ある一定の処理をまとめて名前をつけたもので、複数の場所で同じような処理を書く必要がある場合に便利です。

以下は、Rubyでメソッドを定義する方法の例です。

def メソッド名(仮引数)
  処理 
end

上記のコードでは、defキーワードを使ってメソッドを定義します。メソッド名は自分で決定し、仮引数は必要に応じて任意の数を指定できます。また、endキーワードでメソッドの終わりを示します。

このように、トップレベルで定義されたメソッドは、スクリプト中どこならでも呼び出すことができ、関数と呼ばれことがあります。


以下は Ruby における関数定義における引数に関する機能の例です。

# 固定順位引数
def greet(name)
  puts "Hello, #{name}!"
end

greet("Alice")   # => "Hello, Alice!"
greet("Bob")     # => "Hello, Bob!"

# 固定順位引数のデフォルト値
def greet_with_default(name="world")
  puts "Hello, #{name}!"
end

greet_with_default   # => "Hello, world!"
greet_with_default("Alice")   # => "Hello, Alice!"

# キーワード引数
def greet_with_keyword(name:)
  puts "Hello, #{name}!"
end

greet_with_keyword(name: "Alice")   # => "Hello, Alice!"
greet_with_keyword(name: "Bob")     # => "Hello, Bob!"

# キーワード引数のデフォルト値
def greet_with_keyword_default(name: "world")
  puts "Hello, #{name}!"
end

greet_with_keyword_default   # => "Hello, world!"
greet_with_keyword_default(name: "Alice")   # => "Hello, Alice!"

# ブロック引数
def greet_with_block(name)
  puts "Hello, #{name}!"
  yield
end

greet_with_block("Alice") { puts "Glad to see you!" }
# => "Hello, Alice!"
# => "Glad to see you!"

# 残余引数
def greet_with_remainder(*names)
  names.each { |name| puts "Hello, #{name}!" }
end

greet_with_remainder("Alice", "Bob", "Charlie")
# => "Hello, Alice!"
# => "Hello, Bob!"
# => "Hello, Charlie!"

greet 関数は固定順位引数を使用しています。greet_with_default 関数では、name のデフォルト引数が指定されています。greet_with_keyword 関数では、キーワード引数を使用しています。greet_with_keyword_default 関数では、キーワード引数のデフォルト引数が指定されています。greet_with_block 関数では、ブロック引数を使用しています。最後に、greet_with_remainder 関数は残余引数を使用しています。

これらの機能を駆使することで、より柔軟かつ読みやすいコードを作成することができます。

固定順位引数

例えば、以下のようなメソッドを定義してみましょう。

def say_hello(name)
  puts "Hello, #{name}!" 
end

このメソッドは、引数に渡した名前に対して「Hello, 〇〇!」というメッセージを出力します。引数の「name」には、任意の名前を指定することができます。

このメソッドを呼び出すためには、以下のように書きます。

say_hello("John") #=> Hello, John!

引数に指定した名前が、「name」の部分に代入され、putsメソッドによってメッセージが出力されます。

固定順位引数のディフォルト値

また、Rubyには引数を指定しないデフォルト値を設定する方法もあります。以下は、デフォルト値を設定したメソッドの例です。

def say_hello(name = "World")
  puts "Hello, #{name}!" 
end

この場合、引数を指定しなかった場合は「World」という名前が、引数を指定した場合は指定した名前が代入されます。例えば、以下のように呼び出します。

say_hello         #=> Hello, World! 
say_hello("Mary") #=> Hello, Mary!

キーワード引数

キーワード引数では、引数名を指定することができます。これにより、引数の並び順を気にすることなく、任意の引数を指定することができます。

def person(name:, age:)
  puts "名前は#{name}、年齢は#{age}才です。"
end

person(name: "山田太郎", age: 30) #=> 名前は山田太郎、年齢は30才です。

キワード引数の実引数でに順序は任意です。

キーワード引数のディフォルト値

キーワード引数もディフォルト値を設定することができます。引数を指定しなかった場合は、初期値が使われるようになります。

def person(name: "名無し", age: 0)
  puts "名前は#{name}、年齢は#{age}才です。"
end

person(name: "山田太郎") #=> 名前は山田太郎、年齢は0才です。

ブロック引数

ブロック引数とは、メソッドの引数としてブロックを受け取るための仕組みです。 ブロックを受け取るメソッドの引数リストの最後に、"&ブロック名"という形式の引数を指定することで、ブロックをメソッド内で扱うことができます。

以下に例を示します。

def my_method(&block)
  block.call
end

my_method do
  puts "Hello, World!"
end
# => "Hello, World!"

上記の例では、my_methodメソッドにブロックを渡しています。my_methodメソッド内では、ブロックを扱うための引数として"&block"を指定しており、ブロックを呼び出すために"block.call"を用いています。

yield

yieldは、メソッドに渡されたブロックを実行するためのキーワードです。yieldを使用することで、メソッド内でブロックを実行することができます。

以下に例を示します。

def my_method
  yield
end

my_method do
  puts "Hello, World!"
end
# => "Hello, World!"

上記の例では、my_methodメソッド内でyieldを用いてブロックを実行しています。my_methodメソッドが呼び出されると、引数として渡されたブロックが実行されます。

ブロック呼び出しへの引数

ブロック引数の呼び出しやyieldは引数を渡すこともできます。

ブロック引数の呼び出しへの引数
def my_method(name, &block)
  block.call(name)
end

my_method("John") do |name|
  puts "Hello, #{name}!"
end
# => "Hello, John!"

上記の例では、my_methodメソッドにブロックを渡し、ブロック引数に"name"を指定しています。 ブロック内で引数を使用するために、block.callで"name"を渡しています。 ブロック内では、引数として渡された"name"を使用して文字列を出力しています。

Procクラスは[]をオーバーロードしており。

def my_method(name, &block)
  block[name]
end

と書くことも出来ます。

yield への引数
def my_method(name)
  yield(name)
end

my_method("John") do |name|
  puts "Hello, #{name}!"
end
# => "Hello, John!"

上記の例では、my_methodメソッドに引数として"name"を渡し、yield"name"を引数として渡しています。 ブロック内では、引数として渡された"name"を使用して文字列を出力しています。

ブロック引数と yield どちらを使うべきか?

再帰を行う場合、通常はブロック引数を使用する方が適切です。再帰的な関数では、ブロック引数を使用することで、再帰呼び出しのたびに異なるブロックを渡すことができます。これにより、再帰呼び出しのたびに異なる処理を行うことが可能になります。

例えば、再帰的なメソッドを定義する際には、ブロック引数を使用して次の再帰呼び出し時に実行される処理を指定します。

def recursive_method(n, &block)
  if n > 0
    # ブロックを実行
    block[n]
    # 再帰呼び出し
    recursive_method(n - 1, &block)
  end
end

recursive_method(5) { |num| puts num }

このように、ブロック引数を使うことで、再帰呼び出し時に実行される処理を柔軟に指定することができます。これにより、再帰メソッドがより汎用的で柔軟なものになります。

ブロック引数の有無

Rubyでは、メソッド内でブロックが渡されたかどうかを判定する方法があります。これを行うには、block_given?メソッドを使用します。このメソッドは、メソッド内でブロックが渡された場合にtrueを返し、それ以外の場合にfalseを返します。

以下は、block_given?メソッドを使用してブロックが渡されたかどうかを判定する例です。

def method_with_block
  if block_given?
    puts "ブロックが渡されました"
    # yield が使えます
  else
    puts "ブロックが渡されていません"
    # yield は使えません
  end
end

method_with_block { puts "Hello, block!" }  # => ブロックが渡されました
method_with_block  # => ブロックが渡されていません

このように、block_given?メソッドを使うことで、メソッド内でブロックが渡されたかどうかを簡単に判定することができます。

ブロック引数を省略した時の戻り値

ブロック引数を受け取るメソッドの呼び出しで、ブロック引数を省略した場合の戻り値は、Enumeratorオブジェクトとする慣習があります。

例えば、配列をeachメソッドで呼び出した場合、ブロックが省略されるとEnumeratorオブジェクトが返ります。

array = [2, 3, 5, 7]

enum = array.each
puts enum.class # => Enumerator

begin
  puts enum.next while true
rescue StopIteration => e
  puts e.inspect
end
    # => 2
    #    3
    #    5
    #    7
    #    #<StopIteration: iteration reached an end>

enum2 = 10.upto(15)

loop do
  puts enum2.next
end
    # => 10
    #    11
    #    12
    #    13
    #    14
    #    15
# 一見無限ループになりそうですが
# loop は StopIteration をキャッチすると静かに繰り返しを終了します。

このように、eachメソッドがブロック引数を省略された場合、戻り値はイテレータとして機能するEnumeratorオブジェクトとなります。このオブジェクトは、コレクションの各要素を順番に処理するために使用されます。

典型的なブロック引数処理
  def inorder_traversal(node = @root, &block)
    return to_enum(__method__, node) unless block

    def core(node, &block)
      return if node.nil?

      core(node.left, &block)
      yield node.value
      core(node.right, &block)
    end
    core(node, &block)
    self
  end

残余引数

Rubyにおける「残余引数(rest argument)」とは、メソッド定義の中で最後の引数として指定され、そのメソッドに渡された全ての追加の引数を1つの配列として受け取る機能です。残余引数は、メソッドが可変長の引数を受け取ることを可能にします。

残余引数は、メソッド定義の引数の前にアスタリスク * を付けて指定されます。これにより、メソッドが受け取る引数の数を動的に決定することができます。

以下は、残余引数を使用する例です。

rest-args.rb:
def greet(greeting, *names)
  names.each { |name| puts "#{greeting}, #{name}!" }
end

greet("Hello", "Alice", "Bob", "Charlie")
# 出力:
# Hello, Alice!
# Hello, Bob!
# Hello, Charlie!

この例では、greet メソッドは greeting という通常の引数と、*names という残余引数を持っています。greet メソッドは最初の引数を挨拶の文字列として受け取り、残りの引数を配列 names として受け取ります。その後、names 配列内の各要素に対して、挨拶と名前を組み合わせたメッセージを出力します。

残余引数を使用することで、メソッドが任意の数の引数を受け取る柔軟性が向上し、可読性の高いコードを記述することができます。

戻り値

メソッド内で最後に評価される値が、そのメソッドの戻り値になります。例えば、次のようなメソッドを考えてみましょう。

def add(a, b)
  return a + b 
end

このメソッドは2つの引数を受け取り、それらの和を返します。return キーワードを使用して、明示的に戻り値を指定しています。このように return を使わずに値を返すと、メソッドの最後に評価された値が自動的に戻り値として返されます。

def add(a, b)
  a + b 
end

このように、明示的に return を書かなくても、メソッド内で最後に評価された値が自動的に戻り値となります。

さらに、1つの式からなる関数は = を使い end を伴わない構文で定義できます。

def add(a, b) = a + b

メソッドを呼び出す際には、その戻り値を変数に代入したり、別のメソッドの引数として渡したりすることができます。 戻り値が必要な場合や、逆に必要ではない場合など、メソッドの設計時にはしっかりと考慮して実装する必要があります。

スコープ

Rubyには4つのスコープがあります。以下にそれぞれのスコープとコード例を示します。

ローカルスコープ
変数が定義されたブロック内でしか使用できません。
def local_scope
  x = 10
  puts x # output: 10
end

puts x # undefined local variable or method `x' for main:Object (NameError)
インスタンススコープ
インスタンス変数は同じインスタンスの中であればどこでも使用できます。
class Person
  def initialize(name)
    @name = name
  end
  
  def greet
    puts "Hello, #{@name}!"
  end
end

person = Person.new("Alice")
person.greet # Hello, Alice!
puts @name # undefined method `name' for main:Object (NameError)
クラススコープ
クラス変数はそのクラス及びサブクラスのどこからでも使用できます。
class Counter
  @@count = 0
  
  def initialize
    @@count += 1
  end
  
  def self.count
    @@count
  end
end

counter1 = Counter.new
counter2 = Counter.new

puts Counter.count # 2
グローバルスコープ
グローバル変数はどこからでも使用できます。ただし、他のスコープで同じ名前の変数が定義されている場合はその変数が使用されます。
$global_var = "I'm global"

def check_global_var
  puts $global_var
end

check_global_var # I'm global

def set_global_var
  $global_var = "I'm changed globally"
end

set_global_var
check_global_var # I'm changed globally

以上がRubyの4つのスコープです。

メソッドの引数リストを囲む括弧の省略
メソッドの引数リストを囲む括弧 p() は、曖昧さのない場合は省略可能です。

上の例は、puts(s)と書く代わりに、put s と書く事ができます。

コード
s = 'Hello World!'
puts s
実行結果
Hello World!

再帰的呼び出し

Rubyでは、メソッド内で再帰を行うことができます。以下は、再帰を行うシンプルな例です。

def countdown(num)
  if num > 0
    puts num
    countdown(num - 1)
  else
    puts "Blast off!"
  end
end

countdown(5)

このコードでは、countdownメソッドが自分自身を呼び出して、カウントダウンを実行しています。呼び出し結果は以下の通りです。

5 
4 
3 
2 
1 
Blast off!

このように、再帰を使うと、同じ処理を繰り返し実行することができます。

クロージャー

Rubyにはクロージャーという概念があります。クロージャーとは、変数とブロックを組み合わせたもので、後からブロック内で変数が参照された場合でも、当時の変数の値を保持している特殊なオブジェクトです。

以下は、クロージャーを使った例です。

def greeting_proc(name)
  Proc.new { puts "Hello, #{name}!" }
end

hello = greeting_proc("Alice")
hello.call # => "Hello, Alice!"

changing_name = "Bob"
hello.call # => "Hello, Alice!" (value of name is retained)

上記の例では、greeting_procメソッドがブロックを返し、そのブロックがhello変数に代入されます。このブロック内で参照される変数nameの値が保持され、hello.callを複数回実行しても、最初に指定した名前が表示されます。

また、changing_nameという変数を新しく定義しても、クロージャー内で参照されるnameの値には影響がなく、hello.callを実行すると、"Hello, Alice!"が表示されます。

クラス

Rubyにおけるクラスは、オブジェクト指向プログラミングにおいて重要な役割を持ちます。クラスは、同じ構造や属性を持つオブジェクトの集合を定義するもので、それぞれのオブジェクトはクラスのインスタンスであると考えることができます。

クラス定義とインスタンスの生成とメソッドの呼び出し

# クラス定義
class MyClass
  # クラス変数
  @@class_variable = "Class Variable"

  # インスタンス変数
  attr_accessor :instance_variable

  # コンストラクター
  def initialize(arg)
    @instance_variable = arg
  end

  # クラスメソッド
  def self.class_method
    puts @@class_variable
  end

  # インスタンスメソッド
  def instance_method
    puts @instance_variable
  end
end

# クラスの利用
my_object = MyClass.new("Instance Variable")
MyClass.class_method #=> "Class Variable"
my_object.instance_method #=> "Instance Variable"
my_object.instance_variable = "New Instance Variable"
my_object.instance_method #=> "New Instance Variable"

クラス定義はclassキーワードを使い、インスタンス変数を作成するためにattr_accessorを使っています。コンストラクターはinitializeメソッドを使い、引数を受け取ってインスタンス変数を初期化します。

クラスメソッド、インスタンスメソッドはそれぞれdef self.method_namedef method_nameの形式で定義されます。また、クラス変数は@@で始められ、インスタンス変数は@で始められます。

このように、Rubyのクラスを使用することで、簡単にオブジェクト指向プログラミングを実現することができます。

アクセス修飾子

Rubyには3種類のアクセス修飾子があります。

public
どこからでもアクセス可能なメソッドや変数を定義するときに使用します。
クラスの外部からもアクセス可能です。
protected
自クラスまたはそのサブクラスであればアクセス可能なメソッドや変数を定義するときに使用します。
クラスの外部からはアクセスできません。
private
同じオブジェクト内でしかアクセスできないメソッドや変数を定義するときに使用します。
クラスの外部からはアクセスできません。

以下は、それぞれのアクセス修飾子を使用した例です。

class Animal
  def swim # public
    puts "I can swim!"
  end

  protected

  def run # protected
    puts "I can run!"
  end

  private

  def fly # private
    puts "I can fly!"
  end
end

class Cat < Animal
  def can_run
    run # protectedなのでAnimalクラスのサブクラス内から呼び出し可能
  end

  def can_fly
    fly # privateなので呼び出し不可能
  end
end

cat = Cat.new
cat.swim # publicなので呼び出し可能
cat.can_run # protectedなので呼び出し可能
cat.can_fly # privateなので呼び出し不可能

上記の例では、Animalクラスに swimrunfly メソッドを定義しています。 swimメソッドは publicrun メソッドは protectedfly メソッドは private となっています。

また、Cat クラスを Animal クラスのサブクラスとして定義し、can_runメソッドで、Animalクラスで protected として定義した run メソッドを呼び出しています。一方で、can_fly メソッドで private として定義した fly メソッドを呼び出しているため、エラーが発生しています。

クラスの継承

# 親クラス
class Animal
  def speak
    puts "動物が鳴いています"
  end
end

# 子クラス
class Dog < Animal
  def speak
    puts "わんわん"
  end
end

# 子クラス
class Cat < Animal
  def speak
    puts "にゃーにゃー"
  end
end

# 子クラスのインスタンス化
dog = Dog.new
cat = Cat.new

# 子クラスのメソッドを呼び出し
dog.speak #=> わんわん
cat.speak #=> にゃーにゃー

# 親クラスのメソッドも呼び出し可能
dog.superclass #=> Animal
dog.superclass.superclass #=> Object(親クラスの親クラス)

この例では、Animal という親クラスを作成し、 DogCat という子クラスを作成しています。 DogCat はそれぞれ speak メソッドを持っており、それぞれ異なる出力をします。

DogCatAnimal クラスを継承しています。このため、 DogCatAnimal クラスの speak メソッドを呼び出すことができます。

演算子オーバーロード

以下はRubyにおける演算子オーバーロードの例です。

class Person
  attr_accessor :name, :age

  def initialize(name, age)
    @name = name
    @age = age
  end

  def +(other)
    Person.new("#{self.name} #{other.name}", self.age + other.age)
  end

  def ==(other)
    self.name == other.name && self.age == other.age
  end
end

person1 = Person.new("John", 30)
person2 = Person.new("Doe", 40)

# カスタム演算子 '+' の利用
person3 = person1 + person2
puts person3.name #=> John Doe
puts person3.age #=> 70

# カスタム演算子 '==' の利用
puts person1 == person3 #=> false
person4 = Person.new("John Doe", 70)
puts person3 == person4 #=> true
  • この例では、Personクラスで+演算子と==演算子をオーバーロードしています。
  • +演算子は2つのPersonオブジェクトを受け取り、新しいPersonオブジェクトを返します。
  • ==演算子は2つのPersonオブジェクトを比較し、nameプロパティとageプロパティが両方とも同じ場合にtrueを返します。

クラスへの動的なメソッドの追加

class MyClass
  def initialize(name)
    @name = name
  end
end

my_object = MyClass.new("Bob")

class MyClass
  def greet()
    puts "Hello, #{@name}!"
  end
end

my_object.greet #=> "Hello, Bob!"
  • 最初に、「MyClass」というクラスを定義します。
  • 次に、MyClassのインスタンスを生成し変数my_objectに代入しています。
  • その後、件のMyClassに新しいメソッドgreet()を定義しています。
  • 最後の行で、「my_object.greet」というコードが実行され、オブジェクトの「greet」メソッドが呼び出されます。これにより、「Hello, Bob!」という文字列が出力されます。

注目すべきは:

  • クラス定義の後でもメソッドの追加はできる
  • インスタンス化とメソッドの追加定義が前後しても、メソッド呼び出しは可能である

の2点です。

特異メソッド

Rubyでは、オブジェクトに固有のメソッドを定義することができます。これを特異メソッドと呼びます。通常、メソッドはクラス定義内で定義されますが、特異メソッドは特定のオブジェクトに対して定義されます。

特異メソッドの定義

特異メソッドを定義するには、以下のように書きます。

オブジェクト名.define_singleton_method(シンボル) do |引数|
  # メソッドの中身
end

例えば、以下のように書くことで、特定のオブジェクトにhelloメソッドを定義することができます。

str = "Hello, world!"
str.define_singleton_method(:hello) do
  puts "Hello!"
end

これで、strオブジェクトにhelloメソッドが定義されました。呼び出すには以下のようにします。

str.hello  #=> "Hello!"
特異クラスと特異メソッド

Rubyでは、オブジェクトに対して特異クラスというものが存在します。これは、そのオブジェクトだけが持つクラスのことです。この特異クラスに対して特異メソッドを定義することで、そのオブジェクトだけにメソッドを追加できます。

特異クラスは、以下のように記述します。

class << オブジェクト名
  # メソッドの定義
end

例えば、以下のように記述することで、上記のstrオブジェクトに対してメソッドを定義することができます。

class << str
  def goodbye
    puts "Goodbye!"
  end
end

これで、strオブジェクトにgoodbyeメソッドが定義されました。呼び出すには以下のようにします。

str.goodbye  #=> "Goodbye!"
まとめ

以上が、Rubyの特異メソッドについてのチュートリアルです。オブジェクトに対して固有のメソッドを定義することで、便利なプログラミングをすることができます。

クラス定義の例

都市間の大圏距離

Go/メソッドとインターフェースの都市間の大圏距離を求めるメソッドを追加した例を、Rubyに移植しました。

都市間の大圏距離
class GeoCoord
    attr_accessor :longitude, :latitude

    def initialize(longitude, latitude)
        @longitude, @latitude = longitude, latitude
    end
    def to_s() 
	    ew, ns = "東経", "北緯"
	    long, lat = @longitude, @latitude
	    ew, long = "西経", -long if long < 0.0
	    ns, lat = "南緯", -lat if lat < 0.0
	    "(#{ew}: #{long}, #{ns}: #{lat})"
    end
    def distance(other)
    	i, r = Math::PI / 180, 6371.008
	    Math.acos(Math.sin(@latitude * i) * Math.sin(other.latitude * i) +
		    Math.cos(@latitude * i) * Math.cos(other.latitude * i) * Math.cos(@longitude * i - other.longitude * i)) * r
	end
end
def GeoCoord(longitude, latitude)
    GeoCoord.new(longitude, latitude)
end

Sites = {
    "東京駅":                   GeoCoord(139.7673068, 35.6809591),
    "シドニー・オペラハウス":   GeoCoord(151.215278, -33.856778),
    "グリニッジ天文台":         GeoCoord(-0.0014, 51.4778),
}

Sites.each{|name, gc|
    puts "#{name}: #{gc}"
}

keys, len = Sites.keys, Sites.size
keys.each_with_index{|x, i|
    y = keys[(i + 1) % len]
	puts "#{x} - #{y}: #{Sites[x].distance(Sites[y])} [km]"
}
実行結果
東京駅: (東経: 139.7673068, 北緯: 35.6809591)
シドニー・オペラハウス: (東経: 151.215278, 南緯: 33.856778)
グリニッジ天文台: (西経: 0.0014, 北緯: 51.4778)
東京駅 - シドニー・オペラハウス: 7823.269299386704 [km]
シドニー・オペラハウス - グリニッジ天文台: 16987.2708377249 [km]
グリニッジ天文台 - 東京駅: 9560.546566490015 [km]

このコードは、GeoCoordというクラスを定義し、そこに緯度と経度を持つ「地球上の位置」を表現しています。それを利用して、Sitesというディクショナリに都市名と位置を紐付けています。

このコードのメインの処理は、各都市に対して、その距離を計算して出力しています。すなわち、都市の位置情報から、地球上での距離を求めることができます。

この「地球上の距離を求める」処理は、distanceメソッドで定義されており、球面三角法を使って計算されています。具体的には、2点の緯度経度から、その2点間の地表面上の距離を求める数式を用いています。

また、都市名と位置情報を紐付けたSitesに対しては、各都市の位置を出力しています。それぞれの都市の位置は、東経か西経か、北緯か南緯かで表され、丸括弧でくくられ、「東京駅」の場合は (東経: 139.7673068, 北緯: 35.6809591)という文字列が出力されます。

包含と継承

JavaScript/クラス#包含と継承を、Rubyに移植しました。

包含と継承
class Point
  def initialize(x = 0, y = 0)
    @x, @y = x, y
  end
  def inspect() = "x:#{@x}, y:#{@y}"
  def move(dx = 0, dy = 0)
    @x, @y = @x + dx, @y + dy
    self
  end
end

class Shape
  def initialize(x = 0, y = 0)
    @location = Point.new(x, y)
  end
  def inspect() = @location.inspect()
  def move(x, y)
    @location.move(x, y)
    self
  end
end

class Rectangle < Shape 
  def initialize(x = 0, y = 0, width = 0, height = 0)
    super(x, y)
    @width, @height = width, height
  end
  def inspect() = "#{super()}, width:#{@width}, height:#{@height}"
end

rct = Rectangle.new(12, 32, 100, 50)

p ["rct=", rct]
p ['rct.instance_of?( Rectangle ) => ', rct.instance_of?(Rectangle)]
p ['rct.instance_of?( Shape ) => ', rct.instance_of?(Shape)]
p ['rct.instance_of?( Point ) => ', rct.instance_of?(Point)]
rct.move(11, 21)
p ["rct=", rct]
実行結果
["rct=", x:12, y:32, width:100, height:50]
["rct.instance_of?( Rectangle ) => ", true]
["rct.instance_of?( Shape ) => ", false]
["rct.instance_of?( Point ) => ", false]
["rct=", x:23, y:53, width:100, height:50]

このコードは、Rubyでオブジェクト指向プログラミングを用いた簡単な図形クラスの例です。

Pointクラスは、座標を表現するためのクラスであり、xyの2つのインスタンス変数を持っています。 コンストラクタで、xyの初期値が設定できます。inspectメソッドは、オブジェクトを文字列に変換するために使用されます。moveメソッドにより、(dx, dy)だけ点を移動することができます。

Shapeクラスは、点の座標を持つ図形を表すためのクラスであり、initializeメソッド内で、Pointクラスのオブジェクトを作成して、点の座標を設定します。inspectメソッドは、点の座標を返します。 moveメソッドによって、図形を(dx, dy)だけ移動することができます。

Rectangleクラスは、Shapeクラスを継承しています。コンストラクタで、左上の点の座標と幅と高さを設定できます。inspectメソッドは、左上の点の座標と、幅と高さを表現する文字列を返します。

最後の行では、Rectangleクラスのインスタンスrctを作成し、その後、rctRectangleクラス、Shapeクラス、Pointクラスのどれに対してインスタンスかを調べるようにしています。また、rctの位置を(11, 21)だけ移動させます。

高階関数

Rubyの高階関数とは、関数を引数や戻り値として扱うことができる関数のことを指します。これにより、コードの再利用性が向上し、より柔軟なプログラミングが可能になります。

例えば、以下のような高階関数があります。

def manipulate_array(arr, func)
  arr.map(&func)
end

この関数は、配列と関数を受け取り、配列の各要素に対してその関数を適用した新しい配列を返します。このように、関数自体を引数として渡せることで、どのような処理でも適用することができます。

また、RubyのProcオブジェクトを使うことで、無名関数を定義して高階関数に渡すこともできます。例えば、以下のようなコードです。

add_num = Proc.new {|x| x + 1 }
puts manipulate_array([1, 2, 3], add_num)
# => [2, 3, 4]

このように、Procオブジェクトを定義して関数に渡すことで、より柔軟なプログラミングが可能になります。

lambda

Rubyのlambdaとは、無名関数(関数を名前を付けずに定義すること)の一種であり、関数をオブジェクトとして扱うことができます。

以下は、lambda式の基本構文です。

lambda { |引数| 処理 }

lambdaには引数を渡すことができ、渡された引数を処理に渡すことができます。また、処理の中で return を使うことができ、lambdaの戻り値として処理結果を返すことができます。以下が、lambdaを利用した簡単な例です。

# lambda式を変数に代入する
add = lambda { |x, y| x + y }

# lambda式の引数の数
puts add.arity #=> 2

# lambda式を直接呼び出す
puts lambda { |x, y| x + y }.call(2, 3) #=> 5

# lambda式を変数に代入したものを呼び出す
puts add.call(2, 3) #=> 5

# ブラケット記法による呼び出し
puts add[2, 3] #=> 5

Ruby 1.9 からは

-> (引数) { 処理 }

のような構文が追加され

# lambda式を変数に代入する
add = -> (x, y) { x + y }

# lambda式の引数の数
puts add.arity #=> 2

# lambda式を直接呼び出す
puts -> (x, y) { x + y }[2, 3] #=> 5

とも書けるようになりました。

以上が、Rubyのlambdaの基本的な使い方です。

Procとlambdaの違い

RubyのProcとlambdaは、両方とも無名関数を定義するための方法ですが、いくつかの違いがあります。

引数の扱い方 Procの場合、渡された引数の数が関係なく、無視されます。一方、lambdaの場合は、正確な数の引数が期待されます。引数が足りない場合はエラーが発生します。

return文の扱い方 Procの場合、return文は元の呼び出し元に戻りますが、そのブロック内の後続のコードも実行されます。lambdaの場合、return文はただちにブロックから返され、コードの後続部分は実行されません。

以下は、Procとlambdaの振る舞いの違いを示す例です。

def proc_test
  p = Proc.new { return "Proc" }
  p.call
  return "proc_test method"
end

def lambda_test
  l = lambda { return "Lambda" }
  l.call
  return "lambda_test method"
end

puts proc_test # 出力結果: Proc
puts lambda_test # 出力結果: lambda_test method

この例では、Procはブロック内でreturn文を呼び出していますが、lambdaは呼び出していません。そのため、Procはreturn文で元の呼び出し元に戻って、その後のコードも実行されなくなっています。一方、lambdaはreturn文を呼び出すことがないので、lambda_testメソッドが正常に終了します。

並行処理

Rubyには以下のような並行処理の仕組みが提供されています。

Threadクラス
Threadクラスを使用することで、Rubyでのマルチスレッドプログラミングが可能になります。Thread.newメソッドを呼び出すことで新しいスレッドを作成し、その中で並列に実行したい処理をブロックとして渡します。スレッド間でのデータ共有については、QueueやMutexといった同期プリミティブを使用することができます。
Fiberクラス
Fiberクラスを使用することで、Rubyでのコルーチンを実現できます。コルーチンは一般的なスレッドとは異なり、スケジューラを持っておらず、明示的に切り替える必要があります。Fiber.yieldメソッドを呼び出すことで一旦コルーチンの実行を中断し、Fiber.resumeメソッドを呼び出すことで再開します。
Processクラス
Processクラスを使用することで、Rubyでのプロセス間通信が可能になります。プロセス同士はメモリを共有せず、別々のプロセスとして実行されます。プロセス間で通信を行うには、パイプや共有メモリなどの仕組みを使用する必要があります。

なお、Rubyには以上の他にも、ThreadクラスやFiberクラスと同様に、GVL(Global VM Lock)によってスレッドセーフに実行されるイベントマシンであるEventMachineや、並行処理のためのツールキットであるCelluloidなどが存在します。

スレッド(Thread)

Rubyにおけるスレッドは、軽量な並列処理を実現するための機能であり、プログラム内で独立して動作する変数やメソッドの集合を表します。スレッドは、複数のタスクを同時に処理することで、CPUを最大限活用することができます。

スレッドは、Threadクラスを使用して作成することができます。Thread.newメソッドを使用して、新しいスレッドを作成した後、startメソッドを呼び出すことでスレッドを開始します。また、Thread.currentメソッドを使用することで、現在のスレッドを取得することができます。

以下は、スレッドを使用した簡単な例です。

t1 = Thread.new {
  # スレッド1の処理
  puts "Thread 1 started"
  sleep(2)
  puts "Thread 1 ended"
}

t2 = Thread.new {
  # スレッド2の処理
  puts "Thread 2 started"
  sleep(1)
  puts "Thread 2 ended"
}

t1.join
t2.join

この例では、スレッド1とスレッド2が開始されます。それぞれのスレッドは、sleepメソッドを使用して一定時間待機した後、メッセージを出力します。joinメソッドを呼び出すことで、スレッドが完了するまでプログラムの実行をブロックします。

Rubyにおけるスレッドには、マルチスレッドに関する問題がいくつか存在します。例えば、同一のリソースにアクセスする複数のスレッドによる競合状態や、デッドロックに陥ってしまう可能性があります。これらの問題を回避するため、適切なロック処理や排他制御が必要な場合があります。

ファイバー(Fiber)

Rubyのファイバー(Fiber)は、擬似並行処理を実現するための仕組みです。標準的なスレッドと異なり、Rubyのファイバーはコルーチン的な動作をし、明示的に制御を渡す必要があります。

ファイバーは、Ruby 1.9以降から標準機能として組み込まれています。ファイバーを利用することで、特定の処理を中断したあと再開したり、独自のスケジューリングに基づいた処理を実現したりすることができます。

ファイバーは以下のように作成できます。

fiber = Fiber.new do
  puts "start"
  Fiber.yield
  puts "end"
end

このコードでは、Fiber.newメソッドにブロックを渡してファイバーを作成します。puts "start"を実行した後、Fiber.yieldで処理を中断しています。この時点でプログラムは停止しており、puts "end"はまだ実行されていません。

ファイバーを再開するには、Fiber#resumeメソッドを呼び出します。

fiber.resume

このコードを実行すると、puts "start"が表示された後にプログラムが停止します。その後、再びfiber.resumeを実行すると、puts "end"が表示されます。

このように、ファイバーを利用することで、特定の処理を中断してから再開することができます。また、ファイバーを複数作成し、独自のスケジューリングに基づいた処理を行うことができます。

以下は、複数のファイバーを作成し、それぞれの処理を交互に実行する例です。

f1 = Fiber.new do
  loop do
    puts "1"
    Fiber.yield
  end
end

f2 = Fiber.new do
  loop do
    puts "2"
    Fiber.yield
  end
end

f1.resume
f2.resume
f1.resume
f2.resume
# ...

このように、ファイバーを利用することで、擬似的な並行処理を実現することができます。しかし、ファイバーはスレッドと異なり、OSレベルでのスケジューリングを行わないため、適切な制御を行わないとデッドロックや競合状態などの問題が生じることがあります。注意して使用する必要があります。

プロセス(Process)

Rubyには、プロセスを管理するための Process モジュールが用意されています。このモジュールを使用することで、現在のプロセスや子プロセスを管理することができます。以下に、Processモジュールの主な機能をコードを交えて解説します。

現在のプロセス情報の取得

Process モジュールに用意されている、 Process.pid メソッドを使用することで、現在のプロセスのIDを取得することができます。以下は、 Process.pid を使用して現在のプロセスIDを取得し、表示する例です。

puts "現在のプロセスID: #{Process.pid}"
プロセスの生成

Process モジュールには、新しいプロセスを生成するためのメソッドが複数用意されています。ここでは、代表的な2つのメソッドを紹介します。

Process.spawn メソッド

Process.spawn メソッドを使用することで、外部プログラムを実行する新しいプロセスを生成することができます。以下は、 ls コマンドを実行する新しいプロセスを生成する例です。

pid = Process.spawn("ls")
fork メソッド

fork メソッドを使用することで、現在のプロセスの子プロセスを生成することができます。 fork メソッドは、呼び出し元のプロセスからコピーされた新しいプロセスを生成します。以下は、 fork メソッドを使用して子プロセスを生成し、子プロセス内で puts メソッドを呼び出す例です。

pid = fork
if pid.nil?
  puts "子プロセスのID: #{Process.pid}"
else
  puts "親プロセスのID: #{Process.pid}, 子プロセスのID: #{pid}"
end
プロセスの終了

生成したプロセスを適切に終了することも重要です。以下は、プロセスを終了するためのメソッドを紹介します。

Process.kill メソッド

Process.kill メソッドを使用することで、指定したシグナルを送信してプロセスを終了することができます。以下は、 kill メソッドを使用して、先程生成した ls コマンドを実行しているプロセスを終了する例です。

pid = Process.spawn("ls")
puts "新しいプロセスのID: #{pid}"
sleep 1
Process.kill("TERM", pid)
puts "プロセスを終了しました"
まとめ

このように、Rubyの Process モジュールを使用することで、プロセスの生成や管理を容易に行うことができます。プロセスを扱う場合は、適切な終了処理も行うようにしましょう。

ライブラリ編

プログラミング言語Rubyの大きな魅力の1つは、豊富なライブラリが用意されていることです。Rubyには標準添付ライブラリとgemと呼ばれるパッケージ管理システムを通じて配布されるライブラリがあり、開発者はこれらのライブラリを活用することで、効率的で堅牢なプログラミングが可能になります。

ライブラリ編では、Rubyにおけるライブラリの概念と、主要なライブラリの機能およびユースケースを解説します。ライブラリのインストール方法から、具体的なコード例に至るまでカバーし、Rubyでのライブラリ活用の第一歩を支援します。

リテラル表記

Rubyでは、数値、文字列、正規表現、シンボル、配列、ハッシュ、範囲などのオブジェクトを直接記述する方法としてリテラル表記が用意されています。

  1. 数値(Numeric)
    整数と浮動小数点数のリテラルは通常の数値表記で記述します。有理数は接尾辞rを補い、複素数は虚数単位iを伴い表記します。
    42 
    0o177
    0xbad
    3.14
    123e45
    55/7r
    3+4i
    
  2. 文字列(String)
    文字列リテラルは"'で囲んで記述します。
    "Hello, World!"
    'Ruby is fun'
    
  3. 正規表現(Regexp)
    正規表現リテラルは/で開始し/で終了する形式で記述します。
    /ruby/
    /^start/
    
  4. シンボル(Symbol)
    シンボルリテラルはコロン:から始まる名前で表します。
    :symbol
    :name
    
  5. 配列(Array)
    配列リテラルは[]の中に要素をカンマ区切りで記述します。
    [1, 2, 3, 4, 5]
    ["apple", "banana", "orange"]
    
  6. ハッシュ(Hash)
    ハッシュリテラルは{}の中にキーと値をハッシュロケット=>で対応付けて記述します。
    {"name" => "Jane", "age" => 25}
    {:name => "John", :age => 30}
    
    キーがシンボルの場合は、短縮表記があります。
    {name: "John", age: 30}
    
  7. 範囲(Range)
    範囲リテラルは開始値と終了値を..または...で囲んで記述します。
    (1..5) # 1から5まで  
    ("a"..."d") # "a"から"c"まで
    

これらのリテラル表記は、対応するクラスのオブジェクトを直接生成するための簡潔な記法となっています。また、配列やハッシュの中でもリテラルを入れ子にすることができます。オブジェクトの初期化を手軽に記述できるため、Rubyのコードの可読性が高まります。

数値(Numeric)

RubyのNumericは、数値を表す抽象クラスです。具体的な数値の型、例えば整数(Integer)や浮動小数点数(Float)などは、このNumericクラスのサブクラスとして実装されています。

Numericクラスは、数値演算や比較などの基本的な操作を提供します。例えば、加算、減算、乗算、除算、比較演算子(<、<=、==、>、>=)などがこのクラスで定義されています(ただし複素数に大小関係は定義できないので大小比較演算子も定義されません)。 また、単項の加算と減算を表す+@や-@といったメソッドも定義されています。

このクラスは、Rubyの数値型の共通の振る舞いを定義するための基盤となっています。そのため、IntegerやFloatなどの具体的な数値型が共通のメソッドや振る舞いを持てるように、Numericクラスでこれらのメソッドが定義されています。 数値演算や比較などの一般的な操作に加えて、Numericクラスは他の数値型間での演算を可能にするためのメソッドも提供しています。例えば、coerceメソッドを使うことで、異なる数値型同士での演算が可能になります。

整数(Integer)

Rubyには整数を扱うためのIntegerクラスがあります。

Integerクラスは、以下のような数値リテラルで整数値を表現することができます。

123    #=> 123
0b1111 #=> 15
0o777  #=> 511
0xFF   #=> 255

また、整数の四則演算や比較演算子なども、Integerクラスで定義されています。

1 + 2 #=> 3
3 - 4 #=> -1
5 * 6 #=> 30
7 / 8 #=> 0
9 % 4 #=> 1

1 == 1 #=> true
2 < 3  #=> true
4 > 5  #=> false

浮動小数点数(Float)

Rubyには浮動小数点数を扱うためのFloatクラスがあります。 Floatクラスは、IEEE 754のbinary64をRubyのクラス化したものです。

以下のような数値リテラルで浮動小数点数を表現することができます。

1.23   #=> 1.23
1.2e3  #=> 1200.0
-4.56  #=> -4.56
4E10   #=> 40000000000.0

また、浮動小数点数の四則演算や比較演算子なども、Floatクラスで定義されています。

1.2 + 3.4 #=> 4.6
5.6 - 7.8 #   -2.1999999999999997
9.0 * 1.2 #   10.8
3.4 / 5.6 #   0.6071428571428571

1.2 == 1.2 #=> true
2.3 < 4.5  #   true
6.7 > 8.9  #   false

1.0 / 0.0  #=> Infinity
1.0 / -0.0 #   -Infinity
0.0 / 0.0  #   NaN
coerceメソッドによる型強制

Rubyのcoerceメソッドは、数値演算の際に異なる型のオブジェクトを同じ型に変換するために使用されます。一般的には、coerceメソッドは二項演算子(たとえば+-*/など)が呼び出されたときに使用されます。

例えば、1 + 2.0の場合、整数型の1と浮動小数点数型の2.0が加算されます。このとき、Rubyは1Float型に変換してから加算を行います。この変換はcoerceメソッドによって行われます。

具体的には、1 + 2.0の場合、Rubyは以下のように処理を行います:

  1. 整数型の1Float型の2.0に合わせるために、1.coerce(2.0)を呼び出します。
  2. coerceメソッドは、[1.0, 2.0]という配列を返します。これは1Float型に変換された結果です。
  3. Rubyは、1.0 + 2.0(1.coerce(2.0).reduce(&:+))を計算し、結果の3.0を返します。

このように、coerceメソッドは異なる型のオブジェクトを同じ型に変換して演算を行う際に使用されます。

有理数(Rational)

RubyのRationalクラスは、有理数を表現するためのクラスです。 有理数は、整数のペアとして表現されます:a/b(b>0)、ここでaは分子であり、bは分母です。 数学的には、整数aは有理数a/1と等価です。

以下は、Rationalクラスのインスタンスを作成する方法の例です。

p 1r              #=> (1/1)
p 3/4r            #   (3/4)
p Rational(1)     #   (1/1)
p Rational(3, 4)  #   (3/4)
p Rational(0.3)   #   (5404319552844595/18014398509481984)
p Rational('0.3') #   (3/10)
p Rational(3)/10  #   (3/10)
p 3.to_r/10       #   (3/10))

Rationalクラスのインスタンスは、有理数の四則演算や比較演算を行うことができます。

a = 2/3r
b = 4/5r

p a + b  #=> (22/15)
p a - b  #   (-2/15)
p a * b  #   (8/15)
p a / b  #   (10/9)

p a == b  #=> false
p a != b  #   true
p a > b   #   false 
p a >= b  #   false
p a <=> 0.0/0.0 #=> nil

複素数(Complex)

RubyのComplexクラスは、複素数を表現するためのクラスです。 このクラスを使用すると、複素数を直交座標形式または極座標形式で作成し、演算を行うことができます。

直交座標形式の複素数は、実数部と虚数部のペアで表されます。以下は、複素数を直交座標形式で作成する方法の例です。

c1 = 3 + 4i
C2 = Complex.rect(3, 4)
c3 = Complex(3, 4)
c4 = '3 + 4i'.to_c
c5 = 3 + 4 * Complex::I
  1. c1 = 3 + 4i: この行では、虚数単位 i を使って直接複素数を表現しています。3 は実部であり、4 は虚部です。
  2. c2 = Complex.rect(3, 4): ここでは、Complex クラスの rect メソッドを使用して複素数を作成しています。これは、実部と虚部を指定する方法です。
  3. c3 = Complex(3, 4): この行では、Complex クラスのコンストラクタを使用して複素数を作成しています。引数として実部と虚部を指定します。
  4. c4 = '3 + 4i'.to_c: ここでは、文字列 '3 + 4i' を複素数に変換しています。to_c メソッドは、文字列が複素数の形式である場合に使用できます。
  5. c5 = 3 + 4 * Complex::I: 最後の行では、Complex::I 定数を使って複素数を作成しています。Complex::I は虚数単位を表します。

極座標形式の複素数は、絶対値(大きさ)と偏角で表されます。以下は、複素数を極座標形式で作成する方法の例です。

Complex.polar(5, Math::PI / 4)    # 絶対値が5、偏角がπ/4の複素数を作成

これらの方法を使って、異なる形式の入力を扱い、複素数を生成することができます。

直交座標形式と極座標形式の相互変換

c = Complex(3, 4)
p c.real      # 3
p c.imaginary # 4
p c.rect      # [3, 4]
p c.polar     # [5.0, 0.9272952180016122]
z = p Complex.polar(10, Math::PI / 4)
p z.real      # 7.0710678118654755
p z.imag      # 7.071067811865475

Complex クラスでは、複素数の演算もサポートされています。例えば、加算や乗算、除算などが可能です。

a = Complex(2, 3)
b = Complex(4, 5)

c = a + b   # 6 + 8i
d = a - b   # -2 - 2i
e = a * b   # -7 + 22i
f = a / b   # 0.5609756097560976 + 0.0487804878048781i

また、複素数同士の比較も可能です。 == 演算子を用いることで、2つの複素数が等しいかを比較することができ、 != 演算子を用いることで、2つの複素数が等しくないを比較することができます。

a = Complex(2, 3)
b = Complex(2, 3)

puts a == b  # true
puts a != b  # false

複素数の一致・不一致は定義されますが、大小関係はないので <, >, <=, >=<=> は定義されていません

複素数の演算や比較において、実数と複素数の演算や比較も可能です。 この場合、実数は複素数の虚数部が0のものとして扱われます。例えば、以下のような計算が可能です。

a = Complex(2, 3)
b = 4

c = a + b   # 6 + 3i
d = a * b   # 8 + 12i
e = a > b   # true

Numericと、そのサブクラスのメソッド

Numericと、そのサブクラスのメソッドはの実装は均質ではなく、整数だけがビット演算が可能であったり、複素数だけが大小の比較が出来なかったりします。

このため実装状況を調べたいのですが、手でテストケースを書くのも手数がかかるので、自動的に表にまとめるプログラムを書いてみました。

メソッドの実装状況表を書く
def build_table(types)
  headers = types.map(&:to_s)
  objects = types.map do |t|
    eval("#{t}.new")
  rescue StandardError
    eval("#{t}(0)")
  end

  # メソッドの和集合を求める
  all_methods = objects.map(&:methods).flatten.sort.uniq
  all_methods -= Object.methods
  all_methods += Comparable.methods
  all_methods.sort!
  all_methods.uniq!

  method_str_width = all_methods.map(&:to_s).map(&:length).max

  # ヘッダー
  head = ['Method'.rjust(method_str_width), *headers.map { _1.center 8 }].join(' ')

  # 各オブジェクトについて、メソッドが存在するかどうかを確認し表を構築する
  table = ''
  index = 0
  all_methods.each do |method|
    next unless objects.any? { |obj| obj.methods.include? method }

    table << "#{head}\n#{'-' * head.length}\n" if (index % 10).zero?
    table << method.to_s.rjust(26) << ' '
    table << objects.map do |obj|
      (obj.methods.include?(method) ? 'o' : '-').center(8)
    end.join(' ')
    table << "\n"
    index += 1
  end

  puts table
end

build_table([Numeric, Integer, Float, Rational, Complex])
実行結果
                    Method Numeric  Integer   Float   Rational Complex 
-----------------------------------------------------------------------
                         !    o        o        o        o        o    
                        !=    o        o        o        o        o    
                        !~    o        o        o        o        o    
                         %    o        o        o        o        -    
                         &    -        o        -        -        -    
                         *    -        o        o        o        o    
                        **    -        o        o        o        o    
                         +    -        o        o        o        o    
                        +@    o        o        o        o        o    
                         -    -        o        o        o        o    
                    Method Numeric  Integer   Float   Rational Complex 
-----------------------------------------------------------------------
                        -@    o        o        o        o        o    
                         /    -        o        o        o        o    
                         <    o        o        o        o        -    
                        <<    -        o        -        -        -    
                        <=    o        o        o        o        -    
                       <=>    o        o        o        o        o    
                        ==    o        o        o        o        o    
                       ===    o        o        o        o        o    
                        =~    o        o        o        o        o    
                         >    o        o        o        o        -    
                    Method Numeric  Integer   Float   Rational Complex 
-----------------------------------------------------------------------
                        >=    o        o        o        o        -    
                        >>    -        o        -        -        -    
                        []    -        o        -        -        -    
                         ^    -        o        -        -        -    
                    __id__    o        o        o        o        o    
                  __send__    o        o        o        o        o    
                       abs    o        o        o        o        o    
                      abs2    o        o        o        o        o    
                  allbits?    -        o        -        -        -    
                     angle    o        o        o        o        o    
                    Method Numeric  Integer   Float   Rational Complex 
-----------------------------------------------------------------------
                  anybits?    -        o        -        -        -    
                       arg    o        o        o        o        o    
                  between?    o        o        o        o        -    
                bit_length    -        o        -        -        -    
                      ceil    o        o        o        o        -    
                       chr    -        o        -        -        -    
                     clamp    o        o        o        o        -    
                     class    o        o        o        o        o    
                     clone    o        o        o        o        o    
                    coerce    o        o        o        o        o    
                    Method Numeric  Integer   Float   Rational Complex 
-----------------------------------------------------------------------
                      conj    o        o        o        o        o    
                 conjugate    o        o        o        o        o    
   define_singleton_method    o        o        o        o        o    
               denominator    o        o        o        o        o    
                    digits    -        o        -        -        -    
                   display    o        o        o        o        o    
                       div    o        o        o        o        -    
                    divmod    o        o        o        o        -    
                    downto    -        o        -        -        -    
                       dup    o        o        o        o        o    
                    Method Numeric  Integer   Float   Rational Complex 
-----------------------------------------------------------------------
                  enum_for    o        o        o        o        o    
                      eql?    o        o        o        o        o    
                    equal?    o        o        o        o        o    
                     even?    -        o        -        -        -    
                    extend    o        o        o        o        o    
                      fdiv    o        o        o        o        o    
                   finite?    o        o        o        o        o    
                     floor    o        o        o        o        -    
                    freeze    o        o        o        o        o    
                   frozen?    o        o        o        o        o    
                    Method Numeric  Integer   Float   Rational Complex 
-----------------------------------------------------------------------
                       gcd    -        o        -        -        -    
                    gcdlcm    -        o        -        -        -    
                      hash    o        o        o        o        o    
                         i    o        o        o        o        -    
                      imag    o        o        o        o        o    
                 imaginary    o        o        o        o        o    
                 infinite?    o        o        o        o        o    
                   inspect    o        o        o        o        o    
             instance_eval    o        o        o        o        o    
             instance_exec    o        o        o        o        o    
                    Method Numeric  Integer   Float   Rational Complex 
-----------------------------------------------------------------------
              instance_of?    o        o        o        o        o    
instance_variable_defined?    o        o        o        o        o    
     instance_variable_get    o        o        o        o        o    
     instance_variable_set    o        o        o        o        o    
        instance_variables    o        o        o        o        o    
                  integer?    o        o        o        o        o    
                     is_a?    o        o        o        o        o    
                    itself    o        o        o        o        o    
                  kind_of?    o        o        o        o        o    
                       lcm    -        o        -        -        -    
                    Method Numeric  Integer   Float   Rational Complex 
-----------------------------------------------------------------------
                 magnitude    o        o        o        o        o    
                    method    o        o        o        o        o    
                   methods    o        o        o        o        o    
                    modulo    o        o        o        o        -    
                      nan?    -        -        o        -        -    
                 negative?    o        o        o        o        -    
                      next    -        o        -        -        -    
                next_float    -        -        o        -        -    
                      nil?    o        o        o        o        o    
                   nobits?    -        o        -        -        -    
                    Method Numeric  Integer   Float   Rational Complex 
-----------------------------------------------------------------------
                  nonzero?    o        o        o        o        o    
                 numerator    o        o        o        o        o    
                 object_id    o        o        o        o        o    
                      odd?    -        o        -        -        -    
                       ord    -        o        -        -        -    
                     phase    o        o        o        o        o    
                     polar    o        o        o        o        o    
                 positive?    o        o        o        o        -    
                       pow    -        o        -        -        -    
                      pred    -        o        -        -        -    
                    Method Numeric  Integer   Float   Rational Complex 
-----------------------------------------------------------------------
                prev_float    -        -        o        -        -    
           private_methods    o        o        o        o        o    
         protected_methods    o        o        o        o        o    
             public_method    o        o        o        o        o    
            public_methods    o        o        o        o        o    
               public_send    o        o        o        o        o    
                       quo    o        o        o        o        o    
               rationalize    -        o        o        o        o    
                      real    o        o        o        o        o    
                     real?    o        o        o        o        o    
                    Method Numeric  Integer   Float   Rational Complex 
-----------------------------------------------------------------------
                      rect    o        o        o        o        o    
               rectangular    o        o        o        o        o    
                 remainder    o        o        o        o        -    
  remove_instance_variable    o        o        o        o        o    
               respond_to?    o        o        o        o        o    
                     round    o        o        o        o        -    
                      send    o        o        o        o        o    
           singleton_class    o        o        o        o        o    
          singleton_method    o        o        o        o        o    
    singleton_method_added    o        o        o        o        o    
                    Method Numeric  Integer   Float   Rational Complex 
-----------------------------------------------------------------------
         singleton_methods    o        o        o        o        o    
                      size    -        o        -        -        -    
                      step    o        o        o        o        -    
                      succ    -        o        -        -        -    
                     taint    o        o        o        o        o    
                  tainted?    o        o        o        o        o    
                       tap    o        o        o        o        o    
                      then    o        o        o        o        o    
                     times    -        o        -        -        -    
                      to_c    o        o        o        o        o    
                    Method Numeric  Integer   Float   Rational Complex 
-----------------------------------------------------------------------
                   to_enum    o        o        o        o        o    
                      to_f    -        o        o        o        o    
                      to_i    -        o        o        o        o    
                    to_int    o        o        o        o        o    
                      to_r    -        o        o        o        o    
                      to_s    o        o        o        o        o    
                  truncate    o        o        o        o        -    
                     trust    o        o        o        o        o    
                   untaint    o        o        o        o        o    
                   untrust    o        o        o        o        o    
                    Method Numeric  Integer   Float   Rational Complex 
-----------------------------------------------------------------------
                untrusted?    o        o        o        o        o    
                      upto    -        o        -        -        -    
                yield_self    o        o        o        o        o    
                     zero?    o        o        o        o        o    
                         |    -        o        -        -        -    
                         ~    -        o        -        -        -

文字列(String)

Rubyでは、文字列(String)はダブルクォート(")で囲まれた文字列リテラルで表します。シングルクォート(')で囲まれた文字列リテラルは、単なる文字列であり、式の展開やエスケープシーケンスの展開が行われません。

name = "Alice"
puts "Hello, #{name}!" #=> "Hello, Alice!"
puts 'Hello, #{name}!' #=> "Hello, \#{name}!"

文字列中の式の展開

Rubyでは、ダブルクォートで囲まれた文字列リテラルの中で式を展開することができます。式を展開する場合は、#{}を使います。式を#{}で囲んで文字列中に埋め込むことができます。

x = 10
puts "xの値は#{x}です" #=> "xの値は10です"
puts "xの二乗は#{x ** 2}です" #=> "xの二乗は100です"

また、式を展開する場合は、式が評価されてから文字列中に埋め込まれます。そのため、式の中で定義された変数などの値も展開されます。

x = 10
puts "xの値は#{x}です" #=> "xの値は10です"
x = 20
puts "xの値は#{x}です" #=> "xの値は20です"

なお、シングルクォートで囲まれた文字列リテラルでは、式の展開は行われず、文字列中にそのまま#{...}が表示されます。

x = 10
puts 'xの値は#{x}です' #=> "xの値は\#{x}です"

エスケープシーケンス

以下がRubyで使用できる主なエスケープシーケンスです。

エスケープシーケンス
エスケープシーケンス 意味
\\ バックスラッシュ
\' シングルクォート
\" ダブルクォート
\a ベル音(BEL)
\b バックスペース
\f 改ページ(FF)
\n 改行
\r キャリッジリターン(CR)
\t 水平タブ
\v 垂直タブ
\e エスケープ文字(ESC)
\s 空白
\nnn 8進数表記の文字コード(nは0〜7の数字)
\xnn 16進数表記の文字コード(nは0〜9またはA〜F)
\unnnn Unicode文字コード(nは0〜9またはA〜F)
\Unnnnnnnn Unicode文字コード(nは0〜9またはA〜F)

なお、Rubyではダブルクォートで囲まれた文字列リテラル内でエスケープシーケンスが展開されますが、シングルクォートで囲まれた文字列リテラル内では展開されません。

文字リテラル

Rubyにおける「文字リテラル」は、?を前置した1文字で表します。

p ?a         #==> "a"
p ?字        #==> "字"
p ?😈       #==> "😈"
p ?😈.class #==> String

Rubyに文字型はなく、文字リテラルは長さ1文字の文字列です。

エンコーディング

Ruby では、文字列のエンコーディングを指定することができます。通常、UTF-8 エンコーディングが使われますが、別のエンコーディングを指定することもできます。

文字列のエンコーディングを指定するには、文字列の前に encoding: を付けて指定します。例えば、UTF-8 エンコーディングの文字列を扱う場合は以下のように書きます。

str = "こんにちは".encoding("UTF-8")

別のエンコーディングを指定する場合は、指定したいエンコーディング名を文字列で渡します。

str = "こんにちは".encoding("Shift_JIS")

上記のコードでは、文字列 "こんにちは" が Shift_JIS エンコーディングの文字列として扱われます。

文字列のエンコーディングを指定することで、文字列の変換時にエラーが発生することを防ぐことができます。

正規表現(Regexp)

Rubyにおける「正規表現クラス」と「正規表現リテラル」は、文字列をパターンとして表現するための機能です。

まず、「正規表現クラス」は Regexp クラスを使用して表現されます。以下のように、Regexp.new メソッドを使用して正規表現を作成することができます。

pattern = Regexp.new("Hello, world!")

また、正規表現オプションを指定する場合は、第2引数にオプションを渡すことができます。

pattern = Regexp.new("hello, world!", Regexp::IGNORECASE)

次に、「正規表現リテラル」はスラッシュ / で囲まれた文字列で表現されます。以下のように、スラッシュで正規表現を作成することができます。

pattern = /Hello, world!/

また、正規表現オプションを指定する場合は、スラッシュの後ろにオプションを付けることができます。

pattern = /hello, world!/i

正規表現クラスと正規表現リテラルはどちらも同じ機能を持っており、どちらを使用しても正規表現を表現することができます。 ただし、リテラル表現の方が簡潔に表現することができ、可読性が高くなります。

Rubyで使用できる正規表現
正規表現 ユースケース
/pattern/ 文字列に一致するパターンを検索します。
/./ 任意の一文字に一致します。
/^pattern/ 文字列の先頭に一致するパターンを検索します。
/pattern$/ 文字列の末尾に一致するパターンを検索します。
/[abc]/ a、b、cのいずれかに一致するものを検索します。
/[^abc]/ a、b、c以外のものに一致するものを検索します。
/pattern*/ 0回以上の繰り返しに一致するパターンを検索します。
/pattern+/ 1回以上の繰り返しに一致するパターンを検索します。
/pattern?/ 0回または1回の繰り返しに一致するパターンを検索します。
/pattern{n}/ n回の繰り返しに一致するパターンを検索します。
/pattern{n,}/ n回以上の繰り返しに一致するパターンを検索します。
/pattern{n,m}/ n回以上、m回以下の繰り返しに一致するパターンを検索します。
/(pattern)/ 一致する部分をグループ化します。
/(?<name>pattern)/ 一致する部分を名前でグループ化します。
/(?#comment)/ 正規表現内のコメントを書くことができます。
/\Apattern/ 文字列の先頭に一致するパターンを検索します。
/pattern\z/ 文字列の末尾に一致するパターンを検索します。
/\bpattern/ 単語の先頭に一致するパターンを検索します。
/pattern\b/ 単語の末尾
/pattern/i 大文字小文字を区別せずに文字列に一致するパターンを検索します。
/pattern/m 複数行の文字列に一致するパターンを検索します。
/pattern/x 正規表現内で空白を無視します。
/pattern/o 正規表現を一度だけコンパイルします。

シンボル(Symbol)

Rubyの「シンボル(Symbol)」は、一度定義されたら変更できないイミュータブルなオブジェクトで、文字列に似たデータ型です。シンボルは、コロン(:)の後ろに名前を付けて表します。例えば、:apple:bananaなどがシンボルの例です。

シンボルは、主にキーとして使われることが多く、文字列と比較して以下のようなメリットがあります。

  • シンボルは一度生成されたら変更できないため、ハッシュのキーとして使うときに、キーの値が変更される心配がありません。
  • 同じ名前のシンボルは必ず同じオブジェクトIDを持つため、検索が高速になります。
  • 文字列よりもメモリ消費量が少ないため、大量のデータを扱う場合や、ハッシュのキーとして多用する場合に有効です。

シンボルを使う場合、通常はコロン(:)をつけて定義しますが、:"apple"のようにダブルクォーテーション(")で囲って文字列から生成することもできます。また、文字列からシンボルを生成するには、to_symメソッドまたはinternメソッドを使います。

例えば、以下のようにハッシュのキーとしてシンボルを使うことができます。

fruits = { apple: 100, banana: 200, orange: 300 }
puts fruits[:banana]  # => 200

配列(Array)

Rubyの配列(Array)は、複数の要素を順序付けて格納するためのデータ構造です。配列は、異なるデータ型の要素を混在させることができます。例えば、整数、文字列、オブジェクトなどを含めることができます。

Rubyの配列は、角かっこ [ ] で囲まれた要素のコンマ区切りのリストで表されます。以下は、Rubyの配列の例です。

numbers = [1, 2, 3, 4, 5]
fruits = ["apple", "banana", "orange"]
mixed_array = [1, "hello", true, 3.14]

配列内の要素は、0から始まるインデックスを使用してアクセスできます。例えば、numbers配列の最初の要素にアクセスするには、numbers[0]とします。

Rubyの配列は動的で、サイズを事前に宣言する必要はありません。要素を追加、削除、置換することができます。また、Rubyには多くの配列関連のメソッドが組み込まれており、配列を操作するための便利な機能が提供されています。

Rubyの配列には、さまざまな操作が可能です。以下にいくつかの一般的な操作を示します。

  1. 要素の追加と削除: <<演算子やpushメソッドを使用して要素を配列の末尾に追加できます。また、popメソッドを使用して末尾の要素を削除できます。他にも、unshiftメソッドを使って先頭に要素を追加したり、shiftメソッドを使って先頭の要素を削除したりすることもできます。
    numbers << 6
    # numbersは[1, 2, 3, 4, 5, 6]
    
    fruits.push("grape")
    # fruitsは["apple", "banana", "orange", "grape"]
    
    numbers.pop
    # numbersは[1, 2, 3, 4, 5]
    
    fruits.unshift("kiwi")
    # fruitsは["kiwi", "apple", "banana", "orange", "grape"]
    
    fruits.shift
    # fruitsは["apple", "banana", "orange", "grape"]
    
  2. 要素の取得と変更: インデックスを使用して特定の要素にアクセスし、値を取得したり変更したりできます。
    puts numbers[2] # 3を出力
    
    fruits[1] = "pear"
    # fruitsは["apple", "pear", "orange", "grape"]
    
  3. 配列の結合と分割: +演算子を使用して配列を結合したり、concatメソッドを使用したりして配列を結合できます。また、sliceメソッドを使用して配列を部分的に分割することもできます。
    combined_array = numbers + fruits
    # combined_arrayは[1, 2, 3, 4, 5, "apple", "pear", "orange", "grape"]
    
    part_of_fruits = fruits.slice(1, 2)
    # part_of_fruitsは["pear", "orange"]
    
  4. 配列と繰り返し:
    Rubyの配列は、繰り返し処理とともにしばしば使われるデータ構造です。配列の要素を1つずつ処理する場合には、以下のような繰り返し処理がよく用いられます。
    fruits = ["apple", "banana", "orange"]
    
    fruits.each do |fruit|
      puts fruit
    end
    
    このコードでは、eachメソッドを用いて、fruits配列の要素を順番に取り出しています。そして、ブロック引数fruitに格納された各要素をputsメソッドで出力しています。
    また、配列の要素をインデックスで処理する場合には、以下のような繰り返し処理がよく用いられます。
    fruits = ["apple", "banana", "orange"]
    
    fruits.each_with_index do |fruit, i|
      puts "#{i+1}: #{fruit}"
    end
    
    このコードでは、each_with_indexメソッドを用いて、fruits配列の要素とインデックスを順番に取り出しています。そして、ブロック引数fruitに格納された各要素とブロック引数iに格納された各インデックスを使って、putsメソッドで出力しています。
  5. 要素の検索とフィルタリング: include?メソッドを使用して特定の要素が配列に含まれているかどうかを確認したり、selectメソッドを使用して条件に一致する要素のみを抽出したりすることができます。
    puts numbers.include?(3) # trueを出力
    
    even_numbers = numbers.select { |num| num.even? }
    # even_numbersは[2, 4]
    
  6. 配列操作メソッド: Rubyには、配列を操作するための便利なメソッドが豊富に用意されています。その中でも、mapfilterreduceは特によく使われるメソッドです。
    numbers = [1, 2, 3, 4, 5]
    
    doubled_numbers = numbers.map do |n|
      n * 2
    end
    
    p doubled_numbers #=> [2, 4, 6, 8, 10]
    
    even_numbers = numbers.filter do |n|
      n.even?
    end
    
    p even_numbers #=> [2, 4]
    
    sum = numbers.reduce do |result, n|
      result + n
    end
    
    puts sum #=> 15
    
    # ブロックの代わりにシンボルを使って、次のように書くこともできます。
    
    puts numbers.reduce(&:+) #=> 15
    

これらはRubyの配列を操作するための基本的な操作の一部です。Rubyの配列にはさらに多くの機能がありますが、これらの基本操作から始めることができます。

メソッドチェイン

Rubyには、複数のメソッドを連鎖して呼び出す「メソッドチェイン」という機能があります。メソッドチェインを使うことで、1つのオブジェクトに対して複数のメソッドを簡潔につなげて処理することができます。

例えば、以下のような配列があるとします。

fruits = ["apple", "banana", "orange"]

この配列に対して、mapメソッドで各要素を大文字にし、さらにjoinメソッドでカンマ区切りの文字列に変換する場合、通常の記法では以下のように書くことができます。

fruits = ["apple", "banana", "orange"]

upcase_fruits = fruits.map do |fruit|
  fruit.upcase
end

result = upcase_fruits.join(",")
puts result #=> "APPLE,BANANA,ORANGE"

しかし、メソッドチェインを使うと以下のように簡潔に書くことができます。

fruits = ["apple", "banana", "orange"]

result = fruits.map {|fruit| fruit.upcase}.join(",")
puts result #=> "APPLE,BANANA,ORANGE"

さらにブロックにシンボルを使うと以下のように簡潔に書くことができます。

fruits = ["apple", "banana", "orange"]

result = fruits.map(&:upcase).join(",")
puts result #=> "APPLE,BANANA,ORANGE"

このように、複数のメソッドを簡潔につなげて処理することができます。ただし、メソッドチェインは読みやすさを損なうことがあるため、適切に使うようにしましょう。

tapメソッド

tapメソッドは、オブジェクト自体を受け取り、ブロックをそのオブジェクトのコンテキストで実行し、最終的にそのオブジェクトを返すメソッドです。主な目的は、オブジェクトの特定の状態をデバッグやログ出力などで調査することです。例えば、以下のように使用できます。

some_object.tap{|obj|p obj}.method1.method2.method3

このコードでは、some_object に対して method1 から method3 までのメソッドを呼び出す前に、tapブロック内でその状態を出力しています。tapメソッドは常にそのオブジェクトを返すため、メソッドチェインを中断することなく、そのまま次のメソッド呼び出しを行うことができます。

これらの機能を組み合わせることで、効果的にコードを記述し、デバッグやログ出力などの目的に役立てることができます。

ハッシュ(Hash)

Rubyの「ハッシュ(Hash)」は、キー(key)と値(value)のペアを保持するデータ構造であり、連想配列やマップとも呼ばれます。

ハッシュは波括弧({})で囲まれたキーと値のペアのリストで表され、キーと値の間には=>を使って対応付けます。例えば、以下のように定義することができます。

my_hash = { "key1" => "value1", "key2" => "value2" }

この場合、my_hashは、"key1"というキーに対応する"value1""key2"というキーに対応する"value2"という値を持ちます。

ハッシュは、キーを使って値を取得することができます。例えば、上記のmy_hashから値を取得するには、以下のように書きます。

my_hash["key1"]  # => "value1"

また、Hashクラスのeachメソッドを使うことで、ハッシュ内の全てのキーと値に対して繰り返し処理を行うことができます。以下は、my_hashの全てのキーと値を出力する例です。

my_hash.each do |key, value|
  puts "#{key}: #{value}"
end

出力結果は以下のようになります。

key1: value1
key2: value2

このように、ハッシュはRubyで非常に重要なデータ構造の一つであり、プログラムで様々な用途に利用されます。

シンボルをキーとした特別なハッシュリテラル

例えば、以下のようなハッシュを考えます。

my_hash = { :key1 => "value1", :key2 => "value2" }

このハッシュでは、キーとしてシンボルを使っています。ただし、この記法は少し冗長であり、Rubyでは以下のように短縮して書くことができます。

my_hash = { key1: "value1", key2: "value2" }

このように、キーをシンボルで表す場合は、コロン(:)をキー名の前に付けることで簡潔に表現できます。これにより、より見やすく、読みやすいコードを書くことができます。

また、シンボルを使うことで、文字列を使う場合に比べて、ハッシュの検索速度が速くなることがあります。これは、シンボルはRuby内部で一度だけ生成され、それ以降は同じオブジェクトを使い回すため、検索時にオブジェクトの比較を高速に行えるためです。

なお、シンボルは、文字列と異なりイミュータブルなオブジェクトであり、文字列と同様に、to_symメソッドを使って文字列からシンボルを生成することもできます。

範囲(Range)

Rubyの「範囲(Range)」は、ある値から別の値までの連続する要素を表すオブジェクトです。Rubyでは、範囲は..または...の範囲演算子を使って作成することができます。以下に例を示します。

range1 = 1..5    # 1から5までの範囲を表すRangeオブジェクトを作成
range2 = 1...5   # 1から5未満までの範囲を表すRangeオブジェクトを作成

..を使うと、範囲に終端の値が含まれますが、...を使うと、範囲に終端の値は含まれません。

範囲は主に繰り返し処理に使われ、以下のように使うことができます。

range = 1..5
range.each { |i| puts i }  # 1から5までの値を1つずつ表示する

このコードでは、eachメソッドを使って、範囲内の値を1つずつ取り出し、ブロック内で処理しています。

また、範囲を使うことで、配列の一部を切り出すこともできます。例えば、以下のように範囲を使って、配列から一部の要素を取り出すことができます。

array = [1, 2, 3, 4, 5]
sub_array = array[1..3]  # 2番目から4番目までの要素を取り出す
puts sub_array  # => [2, 3, 4]

このように、範囲はRubyで様々な場面で使われ、プログラミングをより効率的に行うための重要な要素の1つです。

ファイル (File)

Rubyにはファイル操作を行う標準ライブラリ File があります。これを使用することで、ファイルの読み込み・書き込み、存在確認・削除、ディレクトリの作成・削除などが簡単に行えます。

以下は、ファイル操作の基本的な使い方の例です。

# ファイルの読み込み
File.open('sample.txt', 'r') do |file|
  puts file.read
end

# ファイルの書き込み
File.open('output.txt', 'w') do |file|
  file.write('Hello, world!')
end

# ファイルの存在確認
puts File.exist?('sample.txt') #=> true

# ファイルの削除
File.delete('output.txt')

# ディレクトリの作成
Dir.mkdir('mydir')

# ディレクトリの削除
Dir.delete('mydir')

数学(Math)

RubyのMathモジュールは、数学関数を提供するライブラリです。このモジュールに含まれる関数を使用することで、三角関数、指数関数、対数関数などの計算を簡単に行うことができます。

例えば、sin関数を使用する場合は、以下のように記述します。

Math.sin(30 * Math::PI / 180)  #=> 0.5

この例では、30度の正弦(sin)を計算しています。Math::PIは円周率を表しており、角度をラジアンに変換しています。

他にも、cos関数、tan関数、exp関数、log関数などがあります。詳細は公式ドキュメントを参照してください。

Rubyの標準ライブラリとMix-in

Rubyの標準ライブラリには、多数のクラスやモジュールが含まれています。これらのクラスやモジュールは、Rubyプログラムで頻繁に使用される共通機能を提供しています。例えば、文字列(String)クラス、配列(Array)クラス、ファイル操作(File)クラスなどがあります。

また、RubyではMix-inという概念もあります。Mix-inとは、あるクラスに他のモジュールを取り込んで機能を追加することです。これにより、クラスを継承することなく、複数のモジュールから機能を組み合わせて利用することができます。

例えば、Enumerableモジュールは、Rubyの多くのクラスにEnumerableという機能を提供するためのモジュールです。Enumerableモジュールには、eachメソッドやmapメソッド、selectメソッドなどが含まれており、これらのメソッドを利用することで、配列やハッシュなどのオブジェクトに対して繰り返し処理を簡単に行うことができます。

以下に、標準ライブラリからいくつかの代表的なクラスやMix-inを挙げます。

  • Stringクラス:文字列を扱うためのクラス
  • Arrayクラス:配列を扱うためのクラス
  • Hashクラス:ハッシュを扱うためのクラス
  • Fileクラス:ファイルを扱うためのクラス
  • Enumerableモジュール:繰り返し処理を行うためのモジュール
  • Comparableモジュール:比較演算を行うためのモジュール
  • Kernelモジュール:基本的なメソッドを提供するためのモジュール

これらのクラスやMix-inは、Rubyプログラムの中で頻繁に使用されるため、覚えておくと便利です。詳細は公式ドキュメントを参照してください。

列挙(Enumerator)

RubyのEnumeratorクラスは、反復処理を行うためのクラスです。配列やハッシュなどのオブジェクトに対して繰り返し処理を行うことができます。

例えば、以下のようなコードを考えてみましょう。

array = [1, 2, 3, 4, 5]
array.each do |num|
  puts num
end

この場合、eachメソッドを使用して、配列の要素を順番に取り出していることがわかります。しかし、この方法では要素の途中で処理を中断したり、要素を複数回利用することができません。

ここでEnumeratorクラスを使用すると、以下のように書くことができます。

array = [1, 2, 3, 4, 5]
enum = array.to_enum
loop do
  num = enum.next
  puts num
end

この場合、to_enumメソッドを使用して、Enumeratorオブジェクトを生成しています。そして、nextメソッドを使用して、順番に要素を取り出しています。この方法を使用することで、要素の途中で処理を中断することができますし、要素を複数回利用することもできます。

Enumeratorを使ったジェネレータ

def primegen
  n = 2
  sieve = []

  Enumerator.new do |y|
    loop do
      if sieve[n].nil? || sieve[n] == 0
        y << n
        sieve[n * n] = n
      else
        factor = sieve[n]
        sieve[n + factor] = factor
        sieve[n] = 0
      end
      n += 1
    end
  end.lazy
end

p primegen.first(10) #=> [2, 3, 5, 7, 11, 13, 15, 17, 19, 21]

primegen.each_with_index do |prime, index|
  break if index > 5
  puts "#{index}: #{prime}"
end
# => 0: 2
#    1: 3
#    2: 5
#    3: 7
#    4: 11
#    5: 13

%記法

Rubyの%記法は、文字列や正規表現、配列、シンボルなどを作成するための簡潔な書き方のことを指します。%記法を使うことで、コードの可読性を高めることができます。

以下に、Rubyの%記法の使い方を表にまとめました。

%記法
記号 役割
%q シングルクォートで囲んだ文字列を作成する %q(Hello, world!) => "Hello, world!"
%Q ダブルクォートで囲んだ文字列を作成する %Q(Hello, #{name}!) => "Hello, Alice!"
%r 正規表現リテラルを作成する %r(\d+-\d+-\d+) => /\\d+-\\d+-\\d+/
%w 単語の配列を作成する %w(apple banana orange) => ["apple", "banana", "orange"]
%W ダブルクォートで展開される単語の配列を作成する %W(#{fruit1} #{fruit2} #{fruit3}) => ["apple", "banana", "orange"]
%s シンボルを作成する %s(hello) => :hello
%i シンボルの配列を作成する %i(apple banana orange) => [:apple, :banana, :orange]

上の例ではパッターンを丸括弧()で囲みましたが、波括弧{}・角括弧[]で囲むことも出来、括弧でない文字(例えば!)で始めたら、同じ文字(この場合は!)で閉じることが出来ます。

# {} を使う例
puts %q{Hello, #{name}!}  # => Hello, Ruby!

# [] を使う例
puts %w[apple banana orange]  # => ["apple", "banana", "orange"]
puts %i[foo bar baz]  # => [:foo, :bar, :baz]

# ! を使う例
puts %Q!It's raining outside!  # => It's raining outside
puts %w!apple banana orange!  # => ["apple", "banana", "orange"]

# | を使う例
puts %Q|Hello, #{name}!|  # => Hello, Ruby!
puts %i|foo bar baz|  # => [:foo, :bar, :baz]

これらの例では、%記法の丸括弧 ()の部分を、波括弧 {}、角括弧 []、感嘆符 !、縦棒 | に置き換えています。%記法では、括弧以外にも様々な文字を使うことができますが、適切な文字を選んで使用することが重要です。

標準クラス階層の一覧化

def print_subclasses(klass, indent = 0)
  subclasses = klass.subclasses
  return if subclasses.empty?

  subclasses.to_a.sort{|a,b| a.to_s <=> b.to_s}.each do |subclass|
    puts "#{"\t" * indent}#{subclass}"
    print_subclasses(subclass, indent + 1)
  end
end

# <code>Object</code>から再帰的にサブクラスを表示
print_subclasses(Object.superclass)
このRubyのコードは、与えられたクラスのサブクラスを再帰的に表示するものです。以下はコードの解説です:
  1. print_subclassesメソッドは、与えられたクラス(klass)のサブクラスを再帰的に表示します。indentはインデントレベルを表し、デフォルト値は0です。
  2. subclassesメソッドは、指定されたクラスの直接のサブクラスを返します。
  3. sortメソッドは、サブクラスをアルファベット順に並べ替えます。
  4. eachメソッドは、サブクラスを1つずつ処理し、その名前を表示します。
  5. print_subclassesメソッドを再帰的に呼び出すことで、サブクラスのサブクラスも同様に処理されます。
  6. Object.superclassは、すべてのクラスの祖先であるObjectクラスから始まります。
  7. 最後に、print_subclasses(Object.superclass)を呼び出すことで、すべてのクラスの階層構造を表示します。

次は、Rubyの標準クラス階層を再帰的に表示するものです。各クラスはその直接のサブクラスとしてインデントされて表示されます。以下に各部分の解説を示します:

  • ObjectクラスはRubyのほとんどのクラスの祖先となる基本クラスです。
    • ARGF.class: コマンドライン引数やファイルからの入力を扱うクラスです。
    • Array: 配列を表すクラスです。
    • Binding: メソッド呼び出しのコンテキストを保持するオブジェクトを表すクラスです。
    • Complex::compatible: 複素数を表すクラスです。
    • DidYouMean以下のクラス群: ユーザーの入力ミスに対する修正候補を提案する機能を提供するモジュールです。
    • Dir: ディレクトリを扱うクラスです。
    • Encoding以下のクラス群: 文字エンコーディングを扱うクラスです。
    • Enumerator以下のクラス群: 列挙可能なオブジェクトを生成するためのクラスです。
    • ErrorHighlight以下のクラス群: エラーメッセージをハイライトする機能を提供するモジュールです。
    • Exception以下のクラス群: 例外を扱うためのクラスです。
    • FalseClass: 真でない値を表すクラスです。
    • Fiber: コルーチンを表すクラスです。
    • File::Stat: ファイルの状態を表すクラスです。
    • Gem以下のクラス群: RubyGemsを扱うためのクラスです。
    • Hash: ハッシュを表すクラスです。
    • IO以下のクラス群: 入出力を扱うクラスです。
    • MatchData: 正規表現のマッチング結果を表すクラスです。
    • Method: メソッドオブジェクトを表すクラスです。
    • Module以下のクラス群: モジュールを表すクラスです。
    • Monitor以下のクラス群: スレッドセーフな処理を実現するためのクラスです。
    • NameError::message: 名前エラーのメッセージを表すクラスです。
    • NilClass: nilを表すクラスです。
    • Numeric以下のクラス群: 数値を表すクラスです。
      • Integer整数を表すクラスです。
      • Complex複素数を表すクラスです。
      • Rational有理数を表すクラスです。
    • ObjectSpace::WeakMap: 弱参照を使ったハッシュマップを表すクラスです。
    • Proc: プロシージャやラムダを表すクラスです。
    • Process::Status: プロセスのステータスを表すクラスです。
    • Ractor以下のクラス群: Rubyの並行処理を実現するためのクラスです。
    • Random::Base以下のクラス群: 乱数生成を行うためのクラスです。
    • Range: 範囲を表すクラスです。
    • Regexp: 正規表現を表すクラスです。
    • RubyVM以下のクラス群: Rubyの仮想マシンを扱うクラスです。
    • String: 文字列を表すクラスです。
    • Struct: 構造体を表すクラスです。
    • Symbol: シンボルを表すクラスです。
    • Thread以下のクラス群: スレッドを扱うクラスです。
    • ThreadGroup: スレッドグループを表すクラスです。
    • Time以下のクラス群: 時刻を表すクラスです。
    • TracePoint: トレースポイントを表すクラスです。
    • TrueClass: 真の値を表すクラスです。
    • UnboundMethod: バインドされていないメソッドを表すクラスです。
    • Ractor::MovedObject: 移動されたオブジェクトを表すクラスです。

これはRubyの標準ライブラリで定義されたクラス階層を表しており、各クラスがどのような階層構造にあるかを示しています。

結果
Object
	ARGF.class
	Array
	Binding
	Complex::compatible
	DidYouMean::ClassNameChecker
	DidYouMean::DeprecatedMapping
	DidYouMean::Formatter
	DidYouMean::KeyErrorChecker
	DidYouMean::MethodNameChecker
	DidYouMean::NullChecker
	DidYouMean::PatternKeyNameChecker
	DidYouMean::RequirePathChecker
	DidYouMean::SpellChecker
	DidYouMean::TreeSpellChecker
	DidYouMean::VariableNameChecker
	Dir
	Encoding
	Encoding::Converter
	Enumerator
		Enumerator::ArithmeticSequence
		Enumerator::Chain
		Enumerator::Lazy
	Enumerator::Generator
	Enumerator::Producer
	Enumerator::Yielder
	ErrorHighlight::DefaultFormatter
	ErrorHighlight::Spotter
	Exception
		ErrorHighlight::Spotter::NonAscii
		NoMemoryError
		ScriptError
			LoadError
				Gem::LoadError
					Gem::ConflictError
					Gem::MissingSpecError
						Gem::MissingSpecVersionError
			NotImplementedError
			SyntaxError
		SecurityError
		SignalException
			Interrupt
		StandardError
			ArgumentError
				Gem::Requirement::BadRequirementError
				UncaughtThrowError
			EncodingError
				Encoding::CompatibilityError
				Encoding::ConverterNotFoundError
				Encoding::InvalidByteSequenceError
				Encoding::UndefinedConversionError
			FiberError
			IOError
				EOFError
			IndexError
				KeyError
				StopIteration
					ClosedQueueError
					Ractor::ClosedError
			LocalJumpError
			Math::DomainError
			NameError
				NoMethodError
			NoMatchingPatternError
				NoMatchingPatternKeyError
			RangeError
				FloatDomainError
			RegexpError
			RuntimeError
				FrozenError
				Gem::Exception
					Gem::CommandLineError
					Gem::DependencyError
						Gem::DependencyResolutionError
						Gem::UnsatisfiableDependencyError
					Gem::DependencyRemovalException
					Gem::DocumentError
					Gem::EndOfYAMLException
					Gem::FilePermissionError
					Gem::FormatException
					Gem::GemNotFoundException
						Gem::SpecificGemNotFoundException
					Gem::GemNotInHomeException
					Gem::ImpossibleDependenciesError
					Gem::InstallError
						Gem::RuntimeRequirementNotMetError
					Gem::InvalidSpecificationException
					Gem::OperationNotSupportedError
					Gem::RemoteError
					Gem::RemoteInstallationCancelled
					Gem::RemoteInstallationSkipped
					Gem::RemoteSourceException
					Gem::RubyVersionMismatch
					Gem::UninstallError
					Gem::UnknownCommandError
					Gem::VerificationError
					Gem::WebauthnVerificationError
				IO::Buffer::AccessError
				IO::Buffer::AllocationError
				IO::Buffer::InvalidatedError
				IO::Buffer::LockedError
				Ractor::Error
					Ractor::IsolationError
					Ractor::MovedError
					Ractor::RemoteError
					Ractor::UnsafeError
			SystemCallError
				Errno::E2BIG
				Errno::EACCES
				Errno::EADDRINUSE
				Errno::EADDRNOTAVAIL
				Errno::EADV
				Errno::EAFNOSUPPORT
				Errno::EAGAIN
					IO::EAGAINWaitReadable
					IO::EAGAINWaitWritable
				Errno::EALREADY
				Errno::EBADE
				Errno::EBADF
				Errno::EBADFD
				Errno::EBADMSG
				Errno::EBADR
				Errno::EBADRQC
				Errno::EBADSLT
				Errno::EBFONT
				Errno::EBUSY
				Errno::ECANCELED
				Errno::ECHILD
				Errno::ECHRNG
				Errno::ECOMM
				Errno::ECONNABORTED
				Errno::ECONNREFUSED
				Errno::ECONNRESET
				Errno::EDEADLK
				Errno::EDESTADDRREQ
				Errno::EDOM
				Errno::EDOTDOT
				Errno::EDQUOT
				Errno::EEXIST
				Errno::EFAULT
				Errno::EFBIG
				Errno::EHOSTDOWN
				Errno::EHOSTUNREACH
				Errno::EHWPOISON
				Errno::EIDRM
				Errno::EILSEQ
				Errno::EINPROGRESS
					IO::EINPROGRESSWaitReadable
					IO::EINPROGRESSWaitWritable
				Errno::EINTR
				Errno::EINVAL
				Errno::EIO
				Errno::EISCONN
				Errno::EISDIR
				Errno::EISNAM
				Errno::EKEYEXPIRED
				Errno::EKEYREJECTED
				Errno::EKEYREVOKED
				Errno::EL2HLT
				Errno::EL2NSYNC
				Errno::EL3HLT
				Errno::EL3RST
				Errno::ELIBACC
				Errno::ELIBBAD
				Errno::ELIBEXEC
				Errno::ELIBMAX
				Errno::ELIBSCN
				Errno::ELNRNG
				Errno::ELOOP
				Errno::EMEDIUMTYPE
				Errno::EMFILE
				Errno::EMLINK
				Errno::EMSGSIZE
				Errno::EMULTIHOP
				Errno::ENAMETOOLONG
				Errno::ENAVAIL
				Errno::ENETDOWN
				Errno::ENETRESET
				Errno::ENETUNREACH
				Errno::ENFILE
				Errno::ENOANO
				Errno::ENOBUFS
				Errno::ENOCSI
				Errno::ENODATA
				Errno::ENODEV
				Errno::ENOENT
				Errno::ENOEXEC
				Errno::ENOKEY
				Errno::ENOLCK
				Errno::ENOLINK
				Errno::ENOMEDIUM
				Errno::ENOMEM
				Errno::ENOMSG
				Errno::ENONET
				Errno::ENOPKG
				Errno::ENOPROTOOPT
				Errno::ENOSPC
				Errno::ENOSR
				Errno::ENOSTR
				Errno::ENOSYS
				Errno::ENOTBLK
				Errno::ENOTCONN
				Errno::ENOTDIR
				Errno::ENOTEMPTY
				Errno::ENOTNAM
				Errno::ENOTRECOVERABLE
				Errno::ENOTSOCK
				Errno::ENOTSUP
				Errno::ENOTTY
				Errno::ENOTUNIQ
				Errno::ENXIO
				Errno::EOVERFLOW
				Errno::EOWNERDEAD
				Errno::EPERM
				Errno::EPFNOSUPPORT
				Errno::EPIPE
				Errno::EPROTO
				Errno::EPROTONOSUPPORT
				Errno::EPROTOTYPE
				Errno::ERANGE
				Errno::EREMCHG
				Errno::EREMOTE
				Errno::EREMOTEIO
				Errno::ERESTART
				Errno::ERFKILL
				Errno::EROFS
				Errno::ESHUTDOWN
				Errno::ESOCKTNOSUPPORT
				Errno::ESPIPE
				Errno::ESRCH
				Errno::ESRMNT
				Errno::ESTALE
				Errno::ESTRPIPE
				Errno::ETIME
				Errno::ETIMEDOUT
				Errno::ETOOMANYREFS
				Errno::ETXTBSY
				Errno::EUCLEAN
				Errno::EUNATCH
				Errno::EUSERS
				Errno::EXDEV
				Errno::EXFULL
				Errno::NOERROR
			ThreadError
			TypeError
			ZeroDivisionError
		SystemExit
			Gem::SystemExitException
		SystemStackError
		fatal
	FalseClass
	Fiber
	File::Stat
	Gem::BasicSpecification
		Gem::Specification
		Gem::StubSpecification
	Gem::Dependency
	Gem::ErrorReason
		Gem::PlatformMismatch
		Gem::SourceFetchProblem
	Gem::List
	Gem::PathSupport
	Gem::Platform
	Gem::Requirement
	Gem::StubSpecification::StubLine
	Gem::UnknownCommandSpellChecker
	Gem::Version
	Hash
	IO
		File
	IO::Buffer
	MatchData
	Method
	Module
		Class
		Refinement
	Monitor
	MonitorMixin::ConditionVariable
	NameError::message
	NilClass
	Numeric
		Complex
		Float
		Integer
		Rational
	ObjectSpace::WeakMap
	Proc
	Process::Status
	Ractor
	Random::Base
		Random
	Range
	Rational::compatible
	Regexp
	RubyVM
	RubyVM::AbstractSyntaxTree::Node
	RubyVM::InstructionSequence
	String
		DidYouMean::ClassNameChecker::ClassName
		Warning::buffer
	Struct
		#<Class:0x0000798f28922be8>
		#<Class:0x0000798f2902c5b0>
		Process::Tms
	Symbol
	Thread
		Process::Waiter
	Thread::Backtrace
	Thread::Backtrace::Location
	Thread::ConditionVariable
	Thread::Mutex
	Thread::Queue
		Thread::SizedQueue
	ThreadGroup
	Time
	Time::tm
	TracePoint
	TrueClass
	UnboundMethod
Ractor::MovedObject

RDoc

RDocとは、Rubyドキュメンテーションの生成ツールであり、Rubyのプログラムに対する文書化を支援するためのツールです。RDocを使用すると、Rubyのプログラムに関する情報をドキュメントとして出力することができます。これにより、ユーザーは作成されたコードを理解するための情報が提供され、開発者は自分たちのコードを文書化することができます。RDocは、Rubyの標準ライブラリに含まれています。

構文

RDocは、Rubyのための文書作成システムで、コード内で注釈コメントを書くことで、自動的にドキュメントを生成します。以下はRDocの構文についての解説です。

RDocの注釈コメントは、通常のRubyコメントとして記述されますが、一つ上に「#」の代わりに「=begin」と「=end」を使ってマークアップを開始し、終了します。

以下は、RDocのマークアップの例です。

=begin
This class does some cool things.

== Example

An example of how to use this class:

  MyClass.new.do_cool_thing

== Copyright

Copyright (c) 2022 John Doe. All rights reserved.
=end

RDocは、以下の形式のマークアップタグをサポートしています。

  • #: コメント
  • =begin/=end: ドキュメントの開始/終了
  • ==: セクションの開始
  • *: 箇条書きの開始
  • +: リストの開始
  • -: リストの終了
  • !>: コードブロックの開始
  • <pre>/</pre>: コードブロックの開始/終了
  • +methodName+(args)+: メソッドの定義
  • (arg), (arg=default): 引数の指定
  • returns (type): 戻り値の指定
  • \: ラインコンティニュー

RDocの実行

RDocの実行には、次のコマンドを使用します。

$ rdoc [options] [names...]

オプションには、ドキュメンテーションを生成するFormattersや、ドキュメント化するファイルの指定などが含まれます。namesには、ドキュメンテーションを生成するファイルまたはディレクトリを指定します。

以上が、RDocの構文解説です。

附録

Rubyの変更の概要

Ruby-3.3.0

Ruby 3.3.0では、多くの重要な変更が導入されました。その中には、言語機能の改善、コアクラスの更新、標準ライブラリの更新、C APIの更新、実装の改善などが含まれます。

言語機能の変更:
  1. 匿名の可変長引数とキーワード可変長引数が引数として渡せるようになりました。
  2. Procオブジェクトのdupとcloneメソッドが#initialize_dupおよび#initialize_cloneフックを呼び出すようになりました。
  3. "Find pattern" が実験的ではなくなりました。
  4. ブロック内での匿名パラメータの転送が制限されました。
# Ruby 3.3.0での変更点の例

# 匿名の可変長引数とキーワード可変長引数が引数として渡せるようになりました。
def example(*args, **kwargs)
  p args
  p kwargs
end

example(1, 2, 3, key1: 'value1', key2: 'value2')

# Procオブジェクトのdupとcloneメソッドが#initialize_dupおよび#initialize_cloneフックを呼び出すようになりました。
proc_object = proc { |x| x * 2 }
cloned_proc = proc_object.clone
p cloned_proc.call(5)  # => 10

# Range#overlap?メソッドが追加されました。
range1 = 1..5
range2 = 4..8
p range1.overlap?(range2)  # => true
Core classesの更新:
  1. Array#packとString#unpackが未知のディレクティブに対してArgumentErrorを発生させるようになりました。
  2. メソッドRange#overlap?が追加され、2つの範囲が重なっているかどうかを確認できるようになりました。
標準ライブラリの更新:
  1. RubyGemsとBundlerが、必要なgemをGemfileやgemspecに追加せずにrequireした場合に警告を発生させるようになりました。
  2. 多くの標準ライブラリのバージョンが更新されました。
C APIの更新:
  1. 新しいAPIが導入されいくつかのAPUが廃止されました。
  2. Postponed job APIが変更され、新しいAPIが追加されました。
実装の改善:
  1. パーサーがBisonからLrama LALRパーサージェネレーターに置き換えられました。
  2. GCとメモリ管理のパフォーマンスが大幅に向上しました。
  3. YJITのパフォーマンスとメモリ使用量が改善されました。
  4. MJITが削除され、代わりにRJITが導入されました。

これらの変更により、Ruby 3.3.0はより高速で効率的なバージョンになりました。

Ruby-3.2.0

Ruby 3.2.0では、いくつかの重要な変更と新機能が導入されています。以下に概要を示します:

  1. 言語の変更:
    • 匿名の可変長引数とキーワード可変長引数が引数として渡せるようになりました。
    • 単一の位置引数とキーワードを受け入れるProcは、自動的にスプラットしなくなりました。
    • 明示的なオブジェクトに対する定数の割り当て評価順序が一貫性を持つようになりました。
    • "Find pattern" が実験的ではなくなりました。
    • restパラメータを持つメソッドが*argsを通じてキーワード引数を委譲する場合、ruby2_keywordsでマークする必要があります。
    # Ruby 3.2.0の言語の変更
    
    # 匿名の可変長引数とキーワード可変長引数が引数として渡せるようになりました。
    # 例:
    def example(*args)
      p args
    end
    
    example(1, 2, 3)  # => [1, 2, 3]
    
    # 単一の位置引数とキーワードを受け入れるProcは、自動的にスプラットしなくなりました。
    # 例:
    proc_example = proc { |a, **kw| [a, kw] }
    p proc_example.call(1, x: 2, y: 3)  # => [1, {:x=>2, :y=>3}]
    
    # 明示的なオブジェクトに対する定数の割り当て評価順序が一貫性を持つようになりました。
    # 例:
    class Foo; end
    BAR = :baz
    Foo::BAR = BAR
    p Foo::BAR  # => :baz
    
    # "Find pattern" が実験的ではなくなりました。
    
    # restパラメータを持つメソッドが*argsを通じてキーワード引数を委譲する場合、ruby2_keywordsでマークする必要があります。
    # 例:
    def target(**kw)
      p kw
    end
    
    ruby2_keywords def delegate(*args)
      target(*args)
    end
    
    delegate(a: 1, b: 2)  # => {:a=>1, :b=>2}
    
  2. コアクラスの更新:
    • 継承可能なファイバーストレージのためのFiber.[]Fiber.[]=が導入されました。
    • Thread、IO、Class、Data、Encoding、Enumerator、Exception、Hash、Integer、Kernel、MatchData、Module、Proc、Process、Regexp、Refinement、RubyVM::AbstractSyntaxTree、Set、String、Struct、Timeなどの新しいメソッドや機能が追加されました。
  3. 標準ライブラリの更新:
    • Bundler、CGI、Coverage、Date、ERB、FileUtils、IRB、Net::Protocol、Pathname、Socket、URIなどの更新と機能強化が行われました。
  4. C APIの更新:
    • 更新されたAPIや非推奨APIの除去、PRNGやスレッドの計装などのための新しいAPIが導入されました。
  5. 実装の改善:
    • Kernel#autoloadにおける競合状態が解消されました。
    • Regexpマッチングのキャッシュベースの最適化が導入されました。
    • 変数幅割り当てがデフォルトで有効化されました。
    • オブジェクトの形状を考慮した新しいインスタンス変数のキャッシングメカニズムが導入され、JITコードの生成が改善されました。
    • 命令シーケンスのマーキングやメジャーコレクションの高速化が実装されました。
  6. JITの改善:
    • YJITが実験的な段階を抜け、複数のプラットフォーム(x86-64やarm64/aarch64など)をサポートするようになりました。
    • YJITはオブジェクトの形状を最適化するように改善され、物理メモリの遅延割り当てが導入されました。
    • MJITコンパイラがRubyで再実装され、フォークされたRubyプロセスで実行されるようになりました。

これらの変更と改善により、Rubyプログラミングのさまざまな側面でパフォーマンス、安定性、機能性が向上しました。ユーザーは、より良いエクスペリエンスと改善されたパフォーマンスのために、Ruby 3.2.0へのアップグレードを検討することが推奨されます。

Ruby-3.1.0

Ruby 3.1.0の変更点は以下の通りです:

言語の変更点:

  1. ブロック引数が、ブロックが別のメソッドに渡される場合は匿名であってもよくなりました。
  2. ピン演算子は、式を受け取れるようになりました。
  3. ピン演算子は、インスタンス、クラス、グローバル変数をサポートするようになりました。
  4. 1行のパターンマッチングが実験的ではなくなりました。
  5. 複数の代入の評価順序が単一の代入の評価順序と一貫性が持たされるようになりました。
  6. ハッシュリテラルとキーワード引数では値を省略できるようになりました。
  7. main-Ractor以外のRactorも、共有可能なオブジェクトを参照する場合はクラス/モジュールのインスタンス変数を取得できるようになりました。
  8. 無限メソッド定義でもコマンド構文が許可されるようになりました。
# ブロック引数が、ブロックが別のメソッドに渡される場合は匿名であってもよくなりました。
def foo(&)
  bar(&)
end

# ピン演算子は、式を受け取れるようになりました。
result = Prime.each_cons(2).lazy.find_all{_1 in [n, ^(n + 2)]}.take(3).to_a
#=> [[3, 5], [5, 7], [11, 13]]

# ピン演算子は、インスタンス、クラス、グローバル変数をサポートするようになりました。
@n = 5
result = Prime.each_cons(2).lazy.find{_1 in [n, ^@n]}
#=> [3, 5]

# 1行のパターンマッチングが実験的ではなくなりました。
# Parenthesesを省略できるようになりました。
[0, 1] => _, x
{y: 2} => y:
x #=> 1
y #=> 2

# 複数の代入の評価順序が単一の代入の評価順序と一貫性が持たされるようになりました。
# Ruby 3.1.0以前
foo[0], bar.baz = a, b
# 評価順序: a, b, foo, []= on foo, bar, []= on bar, baz= on bar

# Ruby 3.1.0以降
# 評価順序: foo, bar, a, b, []= on foo, baz= on bar
foo[0], bar.baz = a, b

# ハッシュリテラルとキーワード引数では値を省略できるようになりました。
# 例: {x: x, y: y} を {x:, y:} と書けます。
コアクラスの更新:
Array、Class、Enumerable、Enumerator::Lazy、File、GC、Integer、Kernel、Marshal、MatchData、Method / UnboundMethod、Module、Process、Struct、String、Thread、Thread::Backtrace、Thread::Queue、Time、TracePointなどのクラスに変更が加えられました。
標準ライブラリの更新:
RubyGems、base64、benchmark、bigdecimal、bundler、cgi、csv、date、did_you_mean、digest、drb、erb、error_highlight、etc、fcntl、fiddle、fileutils、find、io-console、io-wait、ipaddr、irb、json、logger、net-http、net-protocol、nkf、open-uri、openssl、optparse、ostruct、pathname、pp、prettyprint、psych、racc、rdoc、readline、readline-ext、reline、resolv、rinda、ruby2_keywords、securerandom、set、stringio、strscan、tempfile、time、timeout、tmpdir、un、uri、yaml、zlibなどの更新があります。

C APIの更新、実装の改善、JIT(MJIT、YJIT)、静的解析、デバッガ、エラーハイライト、IRBの自動補完、その他の変更が含まれます。

Ruby-3.0.0

Ruby 3.0.0には、多くの言語の変更点が含まれています。主な変更点を以下に示します:

  1. キーワード引数と位置引数の分離: キーワード引数は位置引数と区別されるようになりました。これにより、Ruby 2.7で警告が発生していたコードは、ArgumentErrorが発生するか、異なる動作をするようになります。
  2. Procの動作変更: 単一のrest引数とキーワードを受け入れるProcは、autosplattingの対象外となりました。これにより、単一のrest引数とキーワードを受け入れるProcの動作が統一されました。
  3. Arguments forwardingの改善: Arguments forwarding(...)が前方の引数をサポートするようになりました。
  4. パターンマッチングの改善: パターンマッチング(case/in)が実験的な段階から正式な機能に移行しました。また、一行のパターンマッチングも改良され、=>演算子が導入されました。
  5. Endless method definitionの導入: Endless method definitionが導入され、メソッドをより簡潔に定義することができるようになりました。
  6. 文字列補間リテラルの変更: # frozen-string-literal: trueを使用している場合、文字列補間リテラルが凍結されなくなりました。
  7. 静的解析の導入: 静的解析の基盤が導入され、RBSというRubyプログラムのための型定義言語が導入されました。また、TypeProfというRubyプログラムのための型解析ツールも実験的な形で提供されます。
  8. 非推奨の警告の変更: 非推奨の警告はデフォルトで表示されなくなりました。
  9. $SAFEと$KCODEの変更: $SAFEと$KCODEは通常のグローバル変数として振る舞い、関連するC-APIメソッドが削除されました。
  10. yieldの変更: メソッド内のシングルトンクラス定義でのyieldは、警告ではなくSyntaxErrorとなりました。
  11. クラス変数のアクセスの変更: 同じ定義でクラス変数が上書きされた場合、RuntimeErrorが発生するようになりました。
  12. 番号付きパラメータへの代入の変更: 番号付きパラメータへの代入は、警告ではなくSyntaxErrorとなりました。
# キーワード引数と位置引数の分離
def example(arg, kwarg:)
  puts arg
  puts kwarg
end

# ArgumentErrorが発生します
# example(123, kwarg: 456)

# Procの動作変更
# autosplattingの対象外となりました
proc = ->(*rest, **kwargs) { [rest, kwargs] }

# [rest, kwargs]がそのまま返されます
p proc.call(1, 2, a: 3, b: 4)

# Arguments forwardingの改善
def forwarder(*args, **kwargs)
  receiver(*args, **kwargs)
end

def receiver(a, b:, c: nil)
  puts "a: #{a}, b: #{b}, c: #{c}"
end

forwarder(1, b: 2, c: 3) # => a: 1, b: 2, c: 3

# パターンマッチングの改善
case [1, 2]
in [1, x]
  puts x
end

# => 2

# Endless method definitionの導入
def foo = puts "Hello"

foo # => Hello

# 文字列補間リテラルの変更
# # frozen-string-literal: trueを使用している場合、文字列補間リテラルが凍結されなくなりました
foo = "foo #{Time.now}"

# 静的解析の導入
# 型定義言語RBSが導入され、型解析ツールTypeProfが提供されます

# 非推奨の警告の変更
# デフォルトで表示されなくなりました

# $SAFEと$KCODEの変更
# 通常のグローバル変数として振る舞い、関連するC-APIメソッドが削除されました

# yieldの変更
# メソッド内のシングルトンクラス定義でのyieldは、警告ではなくSyntaxErrorとなりました

# クラス変数のアクセスの変更
class MyClass
  @@var = 1
end

# RuntimeErrorが発生します
# MyClass.class_eval { @@var = 2 }

# 番号付きパラメータへの代入の変更
# SyntaxErrorが発生します
# _1 = 10

これらの変更点は、Ruby 3.0.0で導入された主な機能です。これにより、より安全で効率的なコーディングが可能になり、言語の進化が促進されました。

改廃された技術

Rubyの改廃された技術や利用が推奨されない技術は、言語の進化、パフォーマンスの向上、シンプルさの追求、新しいプログラミングパラダイムへの対応により置き換えられます。以下に、代表的な技術を示します。

taint メソッド

  • サポート開始年: 1997年(Ruby 1.0)
  • サポート終了年: 2020年(Ruby 3.0で削除)
廃止または衰退の理由
信頼性を向上させるため、tainttrustメカニズムは非推奨となり削除されました。セキュリティモデルが変更されたことに伴います。
代替技術
特定のセキュリティ要件に応じて、コードレビューや他の安全な実装方法を採用してください。

= $== グローバル変数

  • サポート開始年: 1997年(Ruby 1.0)
  • サポート終了年: 2007年(Ruby 1.9で削除)
廃止または衰退の理由
可読性の向上と、グローバル変数の乱用を避けるため削除されました。
代替技術
特定の機能に応じて、ThreadまたはFiberローカル変数を使用してください。

= Object#==~ メソッド

  • サポート開始年: 1997年(Ruby 1.0)
  • サポート終了年: 2018年(Ruby 2.5で削除)
廃止または衰退の理由
不要とされ、特定の意味を持たないObject#=~は削除されました。
代替技術
カスタムクラスでマッチングを実装する場合は、#===や明示的なメソッドを実装してください。

FixnumBignum クラス

  • サポート開始年: 1997年(Ruby 1.0)
  • サポート終了年: 2017年(Ruby 2.4で削除)
廃止または衰退の理由
一貫性を持たせるために、整数型がIntegerに統一されました。
代替技術
Integerクラスを使用してください。

Flip-flop 演算子

  • サポート開始年: 1997年(Ruby 1.0)
  • サポート終了年: 2020年(Ruby 3.0で削除)
廃止または衰退の理由
コードの可読性が低く、多くのケースで誤解を招くため非推奨化されました。
代替技術
明示的な条件分岐や状態管理を用いて実装してください。

DL ライブラリ

  • サポート開始年: 2000年(Ruby 1.6)
  • サポート終了年: 2014年(Ruby 2.2で削除)
廃止または衰退の理由
より安全で使いやすいFiddleライブラリに置き換えられました。
代替技術
Fiddleライブラリを使用してください。

sprintf における % フォーマット指定子

  • サポート開始年: 1997年(Ruby 1.0)
  • サポート終了年: 2018年(Ruby 2.5で非推奨、3.0で削除)
廃止または衰退の理由
%の意味があいまいで、誤用される可能性があるため削除されました。
代替技術
明示的なフォーマット指定子(例: %d%s)を使用してください。

String#byteslice の旧引数形式

  • サポート開始年: 2001年(Ruby 1.6)
  • サポート終了年: 2017年(Ruby 2.4で非推奨、2.6で削除)
廃止または衰退の理由
一貫性を保つため引数形式が変更されました。
代替技術
新しい引数形式(String#byteslice(start, length))を使用してください。

旧式のプロック引数

  • サポート開始年: 1997年(Ruby 1.0)
  • サポート終了年: 2011年(Ruby 1.9で削除)
廃止または衰退の理由
一貫した引数形式を推進し、動作を明確化するため削除されました。
代替技術
ブロック引数の新しい形式を使用してください(例: proc { |x| ... })。

YAML における Symbol のデフォルトロード

  • サポート開始年: 2005年(Ruby 1.8)
  • サポート終了年: 2019年(Ruby 2.6で非推奨、3.0で削除)
廃止または衰退の理由
セキュリティリスクを軽減するため、Symbolのデフォルトロードが廃止されました。
代替技術
safe_loadメソッドを使用し、Symbolのロードを手動で有効化してください。

コードギャラリー

エラトステネスの篩

エラトステネスの篩を、若干 Ruby らしく書いてみました。

エラトステネスの篩
def eratosthenes(n)
  is_prime = [true] * (n + 1)
  is_prime[0] = false
  is_prime[1] = false

  (2..Math.sqrt(n)).each do |i|
    if is_prime[i]
      (i*i..n).step(i) do |j|
        is_prime[j] = false
      end
    end
  end

  primes = (2..n).select { |i| is_prime[i] }
  return primes
end

p eratosthenes(100)
実行結果
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]

最大公約数と最小公倍数

最大公約数と最小公倍数を、若干 Ruby らしく書いてみました。

最大公約数と最小公倍数
def gcd2(m, n) = n == 0 ? m : gcd2(n, m % n)
def gcd(*ints) = ints.reduce(&method(:gcd2))
def lcm2(m, n) = m * n / gcd2(m, n)
def lcm(*ints) = ints.reduce(&method(:lcm2))

puts "gcd2(30, 45) => #{gcd2(30, 45)}"
puts "gcd(30, 72, 12) => #{gcd(30, 72, 12)}"
puts "lcm2(30, 72) => #{lcm2(30, 72)}"
puts "lcm(30, 42, 72) => #{lcm(30, 42, 72)}"
実行結果
gcd2(30, 45) => 15
gcd(30, 72, 12) => 6
lcm2(30, 72) => 360
lcm(30, 42, 72) => 2520

二分法

二分法を、若干 Ruby らしく書いてみました。

二分法
def bisection(low, high, &f)
  x = (low + high) / 2.0
  fx = f[x]
  return x if fx.abs < 1.0e-10
  case fx <=> 0
  when -1 then low = x
  when 1  then high = x
  end
  return bisection(low, high, &f)
end

puts bisection(0, 3) { |x| x - 1 }
puts bisection(0, 3) { |x| x * x - 1 }
実行結果
0.9999999999417923
1.0000000000291038
旧課程(-2012年度)高等学校数学B/数値計算とコンピューター#2分法の例を Ruby に移植しました。

関連項目

脚註