Ruby基礎・基本文法
Rubyとは
Rubyは、まつもとゆきひろ(Matz)氏によって開発されたオブジェクト指向スクリプト言語
Rubyの特徴
完全なオブジェクト指向言語で、直感的な文法と強力な機能を持つ柔軟なプログラミング言語
- 数値、文字列、nil(無)まで、すべてのデータがオブジェクト
- 動的型付け言語
- 直感的で読みやすい構文
- 強力なメタプログラミング機能
- Ruby on Railsなど豊富なフレームワーク
基本文法
メソッド呼び出し
オブジェクトに対してメソッドを呼び出すことで処理を実行
メソッド呼び出しの基本的な構文はオブジェクト.メソッド名(引数)
# 基本形
オブジェクト.メソッド(引数1, 引数2, 引数3)
# カッコは省略可能(推奨されない場合もある)
オブジェクト.メソッド 引数1, 引数2, 引数3
# 引数なし
オブジェクト.メソッド
# 実例
"hello".upcase() # "HELLO"
"hello".upcase # "HELLO" (同じ)
[1, 2, 3].push(4) # [1, 2, 3, 4]
[1, 2, 3].push 4 # [1, 2, 3, 4] (同じ)
メソッド名とカッコの間にスペースを入れるとエラー
add(4, 5) # ✓ OK
add (4, 5) # ✗ NG (構文エラー)
文の区切り
基本的に改行が文の区切りになる
セミコロンは通常不要で、長い文は継続記法で複数行に分けることができる
# 改行で文が区切られる
x = 10
y = 20
# 複数の文を1行に書く場合は、セミコロンで区切れるが、通常は改行を使用
x = 10; y = 20
# 文が続くことが明らかな場合は改行できる
result = 1 + 2 + 3 +
4 + 5 + 6
# バックスラッシュで明示的に継続
long_string = "これは非常に長い文字列で" \
"複数行に分けて書きたい"
コメント
コードに注釈を付けるための記法 #(ハッシュマーク)以降その行の終わりまでがコメント
# これは単一行コメントです
puts "Hello"
# 変数の説明
user_name = "太郎" # 行末にもコメントを書ける
# コードを一時的に無効化
# puts "この行は実行されません"
コメントの原則
- WHY(なぜ)を書く、WHAT(何を)は書かない
- コード自体を読みやすくし、コメントは補足に留める
- 正規表現や複雑なアルゴリズムには説明を加える
複数行コメント
=beginから=endまでが複数行コメントになるが、あまり使われない
=begin
これは複数行コメントです。
複数行にわたってコメントを書くことができます。
ただし、実際にはあまり使われません。
=end
識別子
変数、メソッド、クラスなどに付ける名前のことで、種類ごとに異なる命名規則
| 種類 | 規則 | 例 |
|---|---|---|
| ローカル変数 メソッド |
小文字・アンダースコアで始まる スネークケース |
user_name, calculate_total
|
| 定数・クラス モジュール |
大文字で始まる キャメルケース |
UserName, MAX_SIZE
|
| インスタンス変数 |
@で始まる |
@name, @age
|
| クラス変数 |
@@で始まる |
@@count |
| グローバル変数 |
$で始まる |
$global_var |
# ローカル変数(スネークケース)
user_name = "太郎"
total_price = 1000
# 定数(スクリーミングケース)
MAX_SIZE = 100
KEY = "abc123"
# クラス名(キャメルケース)
class UserAccount
end
# メソッド名(スネークケース)
def calculate_total_price
# 処理
end
重要なポイント
- Rubyは大文字と小文字を区別
- 数字で始まる識別子は使えない
- 特殊文字は原則使えない(
?と!はメソッド名の末尾のみ可)
使用可能な文字
- アルファベット(a-z, A-Z)
- 数字(0-9、ただし先頭には使えない)
- アンダースコア(
_) - 日本語などのUnicode文字(非推奨)
予約語
Rubyの文法で特別な意味を持つため、識別子として使用できない単語の一覧
# 制御構文
if, unless, case, when, else, elsif, end
while, until, for, break, next, redo, retry
# 論理演算
and, or, not
# 値
true, false, nil
# クラス・モジュール
class, module, def, undef, alias
# メソッド制御
return, yield, super, self
# 例外処理
begin, rescue, ensure, raise
# その他
do, then, __FILE__, __LINE__, __ENCODING__
in, defined?
リテラル
ソースコードに直接埋め込むことができる値の表現方法
数値、文字列、配列、ハッシュなど様々な種類がある
# 数値リテラル
42 # 整数
3.14 # 浮動小数点数
1_000_000 # 桁区切り(読みやすさのため)
# 文字列リテラル
"Hello" # ダブルクォート
'Hello' # シングルクォート
# 配列リテラル
[1, 2, 3]
# ハッシュリテラル
{ name: "太郎", age: 25 }
# シンボルリテラル
:name
# 正規表現リテラル
/\d+/
# 範囲リテラル
1..10 # 1から10まで(10を含む)
1...10 # 1から10まで(10を含まない)
変数とデータ型
変数の特徴
Rubyでは変数宣言時に必ず値を代入する必要があり、動的型付けにより型宣言は不要
# ✗ 変数だけの宣言はエラー
x
# => NameError: undefined local variable or method `x'
# ✓ 値を代入して初めて変数が生成される
x = 10
# => 10
# 「値がない」ことを表現したい場合はnilを使う
user_name = nil
理由: 動的型付け言語では、変数は「オブジェクトへの参照」に過ぎないため、参照先のオブジェクトがなければ変数は存在できない
変数の命名規則
変数の種類(ローカル、インスタンス、クラス、グローバル、定数)ごとに異なる命名ルール
ローカル変数
- 小文字またはアンダースコアで始まり、スネークケースで命名
- スコープは定義されたブロック、メソッド、クラス/モジュール内でのみ有効
インスタンス変数
- インスタンス変数は、
@で始まり、オブジェクトのインスタンス内で共有 - 初期化されていないインスタンス変数の値は
nil
クラス変数
- クラス変数は、
@@で始まり、クラスとそのすべてのインスタンスで共有 - 継承クラスで共有されるので、予期しない動作を引き起こすことがあり、通常はクラスインスタンス変数を使う方が安全
グローバル変数
- グローバル変数は、
$で始まり、プログラム全体からアクセスできる - グローバル変数の使用は強く非推奨
定数
- 定数は、大文字で始まる識別子
# スネークケース(推奨)
user_name = "太郎"
total_price = 1000
is_valid = true
# ローカル変数: 小文字またはアンダースコアで始まる
temp = 10
_unused = 20
# インスタンス変数: @で始まる
@name = "太郎"
# クラス変数: @@で始まる
@@count = 0
# グローバル変数: $で始まる(非推奨)
$global_var = "グローバル"
# 定数: 大文字で始まる
MAX_SIZE = 100
API_KEY = "abc123"
データ型一覧
Rubyで使用される主要なデータ型とその例を一覧
| データ型 | 説明 | 例 |
|---|---|---|
| Integer | 整数 |
42, -10, 1_000_000
|
| Float | 浮動小数点数 |
3.14, -2.5, 1.0e-3
|
| String | 文字列 |
"hello", 'world'
|
| Symbol | シンボル |
:name, :active
|
| Boolean | 真偽値 |
true, false
|
| NilClass | 無 | nil |
| Array | 配列 | [1, 2, 3] |
| Hash | ハッシュ | { name: "太郎", age: 25 } |
# 整数
age = 25
count = -10
big_number = 1_000_000 # アンダースコアは桁区切り
# 浮動小数点数
pi = 3.14
temperature = -2.5
scientific = 1.5e-3 # 指数表記
# 文字列
name = "太郎"
greeting = '花子'
# シンボル
status = :active
role = :admin
# 真偽値
is_active = true
is_deleted = false
# nil
empty_value = nil
# 配列
numbers = [1, 2, 3, 4, 5]
mixed = [1, "hello", :symbol, true]
# ハッシュ
user = { name: "太郎", age: 25, email: "taro@example.com" }
型の確認と変換
型の確認
オブジェクトの型を確認するための方法として、classメソッドやis_a?メソッドを使用
# classメソッド
42.class # Integer
3.14.class # Float
"hello".class # String
:symbol.class # Symbol
nil.class # NilClass
true.class # TrueClass
false.class # FalseClass
[1, 2, 3].class # Array
{a: 1}.class # Hash
# is_a? メソッド(型チェック)
42.is_a?(Integer) # true
42.is_a?(Numeric) # true(Integerの親クラス)
42.is_a?(String) # false
型変換
異なるデータ型間で値を変換するための主なメソッド一覧
| メソッド | 説明 | 例 |
|---|---|---|
to_i |
整数に変換 |
"42".to_i → 42
|
to_f |
浮動小数点数に変換 |
"3.14".to_f → 3.14
|
to_s |
文字列に変換 |
42.to_s → "42"
|
to_sym |
シンボルに変換 |
"name".to_sym → :name
|
to_a |
配列に変換 |
(1..5).to_a → [1, 2, 3, 4, 5]
|
to_h |
ハッシュに変換 |
[[:a, 1], [:b, 2]].to_h → {a: 1, b: 2}
|
# 文字列 → 整数
"42".to_i # 42
"3.14".to_i # 3(小数点以下切り捨て)
"abc".to_i # 0(変換できない場合は0)
"42abc".to_i # 42(数字部分のみ変換)
# 文字列 → 浮動小数点数
"3.14".to_f # 3.14
"42".to_f # 42.0
# 整数 → 文字列
42.to_s # "42"
-10.to_s # "-10"
# 整数 → 浮動小数点数
42.to_f # 42.0
# 文字列 ⇔ シンボル
"name".to_sym # :name
:name.to_s # "name"
# 範囲 → 配列
(1..5).to_a # [1, 2, 3, 4, 5]
('a'..'e').to_a # ["a", "b", "c", "d", "e"]
文字列
シングルクォート・ダブルクォート
シングルクオート
- 特殊文字をそのまま文字列として扱う・式展開(
#{})が使えない - エスケープシーケンスはほとんど無効・
\'と\\のみエスケープ可能
ダブルクオート
- 特殊文字が機能・式展開(
#{})が使用できます - エスケープシーケンスが有効
# シングルクォート: 文字列をそのまま表現
name = 'Ruby'
puts 'Hello, #{name}' # => Hello, #{name} (式展開されない)
puts 'Line 1\nLine 2' # => Line 1\nLine 2 (エスケープされない)
# ダブルクォート: 式展開とエスケープシーケンスが有効
name = "Ruby"
puts "Hello, #{name}" # => Hello, Ruby (式展開される)
puts "Line 1\nLine 2" # => Line 1
# Line 2 (改行される)
# 式展開の詳細
x = 10
y = 20
puts "#{x} + #{y} = #{x + y}" # => 10 + 20 = 30
# 複雑な式も展開可能
users = ["太郎", "花子", "次郎"]
puts "ユーザー数: #{users.length}" # => ユーザー数: 3
エスケープシーケンス
特殊文字や制御文字を文字列内で表現するためのバックスラッシュ(\)記法
シングルクオートの中にシングルクオートを、ダブルの中にダブルの場合も同様
# よく使うエスケープシーケンス
puts "改行\n次の行"
puts "タブ\tインデント"
puts "引用符を表示: \"Hello\""
puts "シングルクォート: 'Hello'"
puts "バックスラッシュ: \\"
# シングルクォート内でのエスケープ
puts 'He said "don\'t"' # => He said "don't"
文字列の比較
文字列同士を比較する際には、等価演算子や比較演算子を使用します。
# 等価比較
"hello" == "hello" # => true
"hello" == "Hello" # => false (大文字小文字を区別)
# 不等価
"hello" != "world" # => true
# 文字列を構成するバイト値が大小比較
"apple" < "banana" # => true
"abc" < "abd" # => true
文字列の操作
主要な文字列メソッド
文字列を操作するための豊富なメソッドが用意されている
phrase = " Giraffe Academy "
# 大文字・小文字変換
phrase.upcase # => " GIRAFFE ACADEMY "
phrase.downcase # => " giraffe academy "
phrase.capitalize # => " giraffe academy "
phrase.swapcase # => " gIRAFFE aCademy "
# 空白の処理
phrase.strip # => "Giraffe Academy" (前後の空白削除)
phrase.lstrip # => "Giraffe Academy " (左の空白削除)
phrase.rstrip # => " Giraffe Academy" (右の空白削除)
# 文字列情報
phrase.length # => 18 (文字数)
phrase.size # => 18 (lengthと同じ)
phrase.empty? # => false (空文字列かどうか)
"".empty? # => true
# 検索・判定
phrase.include?("Academy") # => true (含まれるか)
phrase.start_with?(" G") # => true (始まるか)
phrase.end_with?("my ") # => true (終わるか)
phrase.index("Academy") # => 10 (位置を取得、0始まり)
# 文字列の一部を取得
phrase[0] # => " " (1文字)
phrase[2] # => "G"
phrase[2, 7] # => "Giraffe" (開始位置、長さ)
phrase[2..8] # => "Giraffe" (範囲指定)
# 置換
"hello world".sub("world", "Ruby") # => "hello Ruby" (最初の1つ)
"hello hello".gsub("hello", "hi") # => "hi hi" (すべて)
# 分割・結合
"apple,banana,orange".split(",") # => ["apple", "banana", "orange"]
["a", "b", "c"].join("-") # => "a-b-c"
# 繰り返し
"abc" * 3 # => "abcabcabc"
# 反転
"hello".reverse # => "olleh"
破壊的メソッド
メソッドには元を変更しない「非破壊的メソッド」と、元を変更する「破壊的メソッド」がある
元の文字列そのものを変更する、メソッド名の末尾に!が付いていることが多い
# 非破壊的(元の文字列は変わらない)
text = "hello"
text.upcase # => "HELLO"
puts text # => "hello" (元のまま)
# 破壊的(元の文字列が変わる)
text = "hello"
text.upcase! # => "HELLO"
puts text # => "HELLO" (変更された)
# 主な破壊的メソッド
# upcase!, downcase!, strip!, reverse!, gsub! など
数値と演算
基本的な演算
四則演算(+, -, *, /)、べき乗(**)、剰余(%)などの数値計算の基本操作
# 四則演算
1 + 1 # => 2 (加算)
10 - 5 # => 5 (減算)
3 * 4 # => 12 (乗算)
10 / 3 # => 3 (除算、整数÷整数=整数)
10.0 / 3 # => 3.333... (浮動小数点数を含むと小数結果)
# べき乗
2 ** 3 # => 8 (2の3乗)
10 ** 2 # => 100
# 剰余(余り)
10 % 3 # => 1
17 % 5 # => 2
# 整数の割り算で商と余りを同時に取得
17.divmod(5) # => [3, 2] (商が3、余りが2)
主要な数値メソッド
num = -20.487
# 絶対値
num.abs # => 20.487
# 四捨五入
num.round # => -20
20.5.round # => 21
# 桁数指定の四捨五入
3.14159.round(2) # => 3.14
3.14159.round(4) # => 3.1416
# 切り上げ
20.3.ceil # => 21
-20.7.ceil # => -20
# 切り捨て
20.9.floor # => 20
-20.3.floor # => -21
# 整数部分
20.9.truncate # => 20
-20.9.truncate # => -20
# 正負の判定
10.positive? # => true
-5.negative? # => true
0.zero? # => true
# 偶数・奇数判定
4.even? # => true
5.odd? # => true
# 次の整数・前の整数
5.next # => 6
5.pred # => 4
代入演算子
変数に対して演算と代入を同時に行う省略記法(+=, -=など)
n = 10
# 加算代入
n += 5 # => 15 (n = n + 5と同じ)
# 減算代入
n -= 3 # => 12 (n = n - 3と同じ)
# 乗算代入
n *= 2 # => 24 (n = n * 2と同じ)
# 除算代入
n /= 4 # => 6 (n = n / 4と同じ)
# べき乗代入
n **= 2 # => 36 (n = n ** 2と同じ)
# 剰余代入
n %= 5 # => 1 (n = n % 5と同じ)
真偽値と演算子
真偽値の基本
Rubyではfalseとnilのみが偽で、それ以外はすべて真として扱われます。
# 偽となるもの
false
nil
# 真となるもの
true
0 # ← 他の言語と異なり、Rubyでは真!
"" # ← 空文字列も真!
[] # ← 空配列も真!
{} # ← 空ハッシュも真!
演算子
値の比較や演算を行う記号・演算子には優先順位がある
2つの値の大小比較には<, <=, >, >=を、等価判定には==や!=を利用
高い ::
[]
+(単項) ! ~
**
-(単項)
* / %
+ -
<< >>
&
| ^
> >= < <=
<=> == === != =~ !~
&&
||
.. ...
?:(条件演算子)
=(+=, -= ... )
not
低い and or
比較演算子
2つの値を比較するための演算子(<, <=, >, >=, == , !=など)
# 等価
5 == 5 # => true
5 == 3 # => false
# 不等価
5 != 3 # => true
# 大小比較
5 > 3 # => true
5 < 3 # => false
5 >= 5 # => true
5 <= 4 # => false
# 宇宙船演算子(3方向比較)
1 <=> 2 # => -1 (左が小さい)
2 <=> 2 # => 0 (等しい)
3 <=> 2 # => 1 (左が大きい)
論理演算子
論理演算(AND, OR, NOT)を行うための演算子と短絡評価
# and条件 条件1かつ条件2が真であれば真、それ以外は偽
条件1 && 条件2
# AND(かつ)
true && true # => true
true && false # => false
false && false # => false
# OR条件 条件1または条件2が真であれば真、両方偽の時偽
条件1 || 条件2
# OR(または)
true || false # => true
false || false # => false
# NOT(否定)
!true # => false
!false # => true
!nil # => true (nilは偽なので否定すると真)
# 優先順位
true && false || true # => true (&&が先に評価)
true && (false || true) # => true (括弧で明示)
論理値変換
任意のオブジェクトを2回否定(!!)して明示的に真偽値(trueまたはfalse)に変換する方法
!!object
# !!で強制的に真偽値に変換
!!nil # => false
!!0 # => true
!!"" # => true
!![] # => true
!!"hello" # => true
条件分岐
if文
条件によって処理を分岐させるための基本的な制御構造
# 基本形
if 条件
# 処理
end
# if-else
if 条件
# 条件が真の場合
else
# 条件が偽の場合
end
# if-elsif-else
age = 18
if age < 13
puts "子供"
elsif age < 20
puts "ティーン"
elsif age < 65
puts "大人"
else
puts "シニア"
end
# if文は式なので値を返す
result = if score >= 60
"合格"
else
"不合格"
end
# 後置if(条件修飾子)
puts "合格" if score >= 60
point += 5 if bonus_day
# 条件がない場合はnilを返す
result = if false
"これは実行されない"
end
# => result = nil
unless文
ifの反対を意味を持つunlessもあり、条件式が偽の時だけ処理を実行
# ifの逆(条件が偽の時に実行)
unless logged_in?
redirect_to login_path
end
# 後置unless
puts "ログインしてください" unless logged_in?
# elseも使えるが、可読性が下がるので非推奨
unless error
puts "成功"
else
puts "失敗" # ← これはif errorと書くべき
end
case式
一つの値を複数の条件と比較して処理を分岐させる構文
# 基本形
day = "mon"
case day
when "mon"
"月曜日"
when "tue"
"火曜日"
when "wed"
"水曜日"
when "thu", "fri" # 複数条件
"平日"
when "sat", "sun"
"週末"
else
"不明"
end
三項演算子(条件演算子)
条件式を1行で記述できる簡潔な条件分岐の記法
# 条件式 ? 真の場合 : 偽の場合
age = 18
status = age >= 20 ? "成人" : "未成年"
メソッド
メソッドの定義
defで始まりendで終わるキーワードを使ってメソッドを定義
パラメータでメソッドに情報を渡し、デフォルト値の設定も可能(パラメータは複数指定可能)
def メソッド名(引数1,引数2...)
# 必要な処理
end
# 実例
def greet(name)
puts "こんにちは、#{name}さん"
end
# デフォルト値を設定
def greet(name = "ゲスト", time_of_day = "こんにちは")
puts "#{time_of_day}、#{name}さん"
end
greet("太郎", "おはよう") # => おはよう、太郎さん
greet("太郎") # => こんにちは、太郎さん
greet() # => こんにちは、ゲストさん
メソッド名の規則
メソッド名はスネークケースで書き(キャメルケースは不可)、数字から始めることはできない
- ?で終わる判定メソッド(true/false)
- !で終わる破壊的メソッド(元のオブジェクトを変更)
- 通常の非破壊的メソッド(新しいオブジェクトを返す)
# スネークケースで書く
def calculate_total_price
# 処理
end
# 述語メソッド: ?で終わる(真偽値を返す)
def empty?
@items.length == 0
end
# 破壊的メソッド: !で終わる(オブジェクトを変更)
def reverse_name!
@name.reverse!
end
return文
- メソッドから値を返す方法として、暗黙の返り値(最後の式が返される)
- 明示的なreturn(
returnキーワード) - 早期リターンではreturn後のコードは実行されない
# 最後の式が自動的に返り値になる(暗黙のreturn)
def add(a, b)
a + b # この値が返される
end
def cube(num)
return num * num * num
puts "Hello" # 実行されない
end
puts cube(3) # 27
# 複数の値を返す
def multiple_return(num)
return num * num * num, 70
end
puts multiple_return(3) # 27, 70
メソッドチェーン
メソッドの返り値に対して連続してメソッドを呼び出す記法
# メソッドの返り値に対してさらにメソッドを呼ぶ
"hello world".upcase.reverse.split(" ")
# => ["DLROW", "OLLEH"]
エイリアスメソッド
同じ機能を持つメソッドに複数の名前がついている場合がある
# lengthとsize
[1, 2, 3].length # => 3
[1, 2, 3].size # => 3 (同じ)
# mapとcollect
[1, 2, 3].map { |n| n * 2 } # => [2, 4, 6]
[1, 2, 3].collect { |n| n * 2 } # => [2, 4, 6] (同じ)
参照の概念
変数はオブジェクトそのものではなく、オブジェクトへの参照が格納されている
変数を他に代入したり、引数として渡したりすると、元の変数と同じオブジェクトを参照
同じオブジェクトを参照しているので、オブジェクトが変更されると各変数も変更される
# 文字列オブジェクトを作成
str1 = "hello"
# str2もstr1と同じオブジェクトを参照
str2 = str1
# str1を変更すると...
str1.upcase!
puts str1 # => "HELLO"
puts str2 # => "HELLO" (同じオブジェクトなので変更される)
# オブジェクトIDで確認
str1.object_id == str2.object_id # => true
freeze(凍結)
# freezeでオブジェクトを変更不可にする
str = "hello".freeze
# str.upcase! # エラー!凍結されているので変更不可
# 凍結状態の確認
str.frozen? # => true
# 定数は自動的に凍結される(警告が出る)
CONSTANT = "constant"
# CONSTANT.upcase! # 警告: warning: can't modify frozen String
ガベージコレクション (GC)
使用されなくなったオブジェクトを自動的に回収してメモリから解放する仕組み
# 使われなくなったオブジェクトは自動的にメモリから解放される
def create_objects
str = "temporary"
arr = [1, 2, 3]
# メソッド終了後、これらのオブジェクトは参照されなくなり
# ガベージコレクションの対象になる
end
create_objects
# メソッド終了後、strとarrは自動的に解放される
# GC統計情報
GC.stat
# => { count: 123, heap_allocated_pages: 456, ... }
配列
配列とは複数のデータをまとめて書くのできるオブジェクト
配列のデータ(要素)は順番に並んで、添え字(インデックス)を指定して取り出せる
配列の作成
配列オブジェクトを生成する様々な方法
# 空の配列
empty_array = []
another_empty = Array.new
# 要素を含む配列
numbers = [1, 2, 3, 4, 5]
fruits = ["apple", "banana", "orange"]
# 異なる型を混在できる(推奨はしない)
mixed = [1, "hello", 3.14, true, nil]
# 配列の配列(多次元配列)
matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]
要素へのアクセス
配列の各要素を取得するには[]と添え字(インデックス)を使って指定・範囲指定も可能
fruits = ["apple", "banana", "orange", "grape", "melon"]
# インデックスでアクセス(0始まり)
fruits[0] # => "apple" (最初)
fruits[1] # => "banana"
fruits[4] # => "melon" (最後)
# 負のインデックス(後ろから)
fruits[-1] # => "melon" (最後)
fruits[-2] # => "grape" (最後から2番目)
# 範囲指定
fruits[1..3] # => ["banana", "orange", "grape"]
fruits[1...3] # => ["banana", "orange"] (終わりを含まない)
fruits[0, 2] # => ["apple", "banana"] (開始位置, 要素数)
# firstとlast
fruits.first # => "apple"
fruits.last # => "melon"
fruits.first(2) # => ["apple", "banana"]
fruits.last(2) # => ["grape", "melon"]
要素の追加・削除
配列に要素を追加したり、削除したりするメソッド一覧
fruits = ["apple", "banana"]
# 末尾に追加
fruits.push("orange") # => ["apple", "banana", "orange"]
fruits << "grape" # => ["apple", "banana", "orange", "grape"]
# 先頭に追加
fruits.unshift("strawberry") # => ["strawberry", "apple", "banana", ...]
# 末尾から削除
last = fruits.pop # => "kiwi" (削除した要素を返す)
# 先頭から削除
first = fruits.shift # => "strawberry"
# 値を指定して削除(すべて)
numbers = [1, 2, 3, 2, 4]
numbers.delete(2) # => [1, 3, 4] (2をすべて削除)
# uniqで重複を削除
[1, 2, 2, 3, 3, 3].uniq # => [1, 2, 3]
配列の操作
配列の長さ取得、要素の存在確認、ソートなどの基本的な操作方法
# 長さ
[1, 2, 3].length # => 3
[1, 2, 3].size # => 3 (lengthと同じ)
# 空判定
[].empty? # => true
[1, 2].empty? # => false
# 要素の存在確認
[1, 2, 3].include?(2) # => true
[1, 2, 3].include?(5) # => false
# ソート
[3, 1, 4, 1, 5].sort # => [1, 1, 3, 4, 5]
["banana", "apple", "cherry"].sort # => ["apple", "banana", "cherry"]
配列の結合・分割
複数の配列を結合したり、配列を文字列に変換する方法
# 結合
[1, 2] + [3, 4] # => [1, 2, 3, 4]
[1, 2].concat([3, 4]) # => [1, 2, 3, 4] (破壊的)
# 文字列として結合
["apple", "banana", "orange"].join(", ")
# => "apple, banana, orange"
# zip(複数配列を組み合わせ)
names = ["太郎", "花子", "次郎"]
ages = [25, 30, 22]
names.zip(ages)
# => [["太郎", 25], ["花子", 30], ["次郎", 22]]
配列の変換
# 文字列から配列
"apple,banana,orange".split(",") # => ["apple", "banana", "orange"]
"hello world".split(" ") # => ["hello", "world"]
# 範囲から配列
(1..5).to_a # => [1, 2, 3, 4, 5]
('a'..'e').to_a # => ["a", "b", "c", "d", "e"]
# 配列から文字列
["a", "b", "c"].join # => "abc"
["a", "b", "c"].join("-") # => "a-b-c"
ハッシュ
ハッシュはキーと値の組み合わせを管理するオブジェクト
ハッシュの作成
キーと値のペアを管理するハッシュオブジェクトの生成方法
# 空のハッシュ
empty_hash = {}
another_empty = Hash.new
# キーと値のペア
user = {
"name" => "太郎",
"age" => 25,
"email" => "taro@example.com"
}
# シンボルをキーにする(推奨)
user = {
name: "太郎",
age: 25,
email: "taro@example.com"
}
# 古い書き方(ハッシュロケット)
user = {
:name => "太郎",
:age => 25
}
# 混在も可能(あまり推奨されない)
mixed = {
"name" => "太郎",
age: 25,
:status => :active
}
値へのアクセス
ハッシュからキーを使って値を取得する方法と、安全なアクセス方法
user = { name: "太郎", age: 25, email: "taro@example.com" }
# キーで値を取得
user[:name] # => "太郎"
user[:age] # => 25
# 存在しないキー
user[:address] # => nil
# fetch(デフォルト値を指定)
user.fetch(:name) # => "太郎"
user.fetch(:address, "不明") # => "不明"
user.fetch(:address) { "デフォルト" } # ブロックで動的に生成
# 複数レベルのアクセス(dig)
data[:user][:profile][:name] # => "太郎"
data.dig(:user, :profile, :name) # => "太郎" (安全)
data.dig(:user, :unknown, :name) # => nil (エラーにならない)
要素の追加・更新・削除
ハッシュに要素を追加、更新、削除する方法の一覧
user = { name: "太郎", age: 25 }
# 追加・更新
user[:email] = "taro@example.com" # 追加
user[:age] = 26 # 更新
# 複数同時に追加
user.merge!(city: "東京", country: "日本")
# 削除
user.delete(:email) # => "taro@example.com" (削除した値を返す)
# 条件で削除
scores = { math: 80, english: 60, science: 70 }
scores.delete_if { |subject, score| score < 70 }
# => { math: 80, science: 70 }
# すべて削除
user.clear # => {}
シンボルを使ったハッシュ
ハッシュのキーにシンボルを使う場合、 =>を利用せずシンボル: 値としてハッシュを作成できる
- シンボルは一度作成されると変更できないため、ハッシュのキーとして安全
- シンボルは同じ名前なら常に同一のオブジェクトを参照するため、メモリ使用量が少ない
{ japan: :yen} == { :japan => :yen}
# シンボルはコロンが前に付く (:name)
user = { :name => "Michael Hartl", :email => "michael@example.com" }
# => {:name=>"Michael Hartl", :email=>"michael@example.com"}
# 新しい構文 (同じ意味)
user = { name: "Michael Hartl", email: "michael@example.com" }
# アクセス
user[:name] # => "Michael Hartl"
user[:password] # => nil (未定義のキー)
ネストしたハッシュ
ハッシュの中にハッシュや配列を含む階層構造と、そのアクセス方法
# 階層構造
company = {
name: "ABC株式会社",
address: {
zip: "100-0001",
city: "東京都",
street: "千代田区1-2-3"
},
employees: [
{ name: "太郎", age: 30 },
{ name: "花子", age: 28 }
]
}
# アクセス
company[:address][:city] # => "東京都"
company[:employees][0][:name] # => "太郎"
# dig で安全にアクセス
company.dig(:address, :city) # => "東京都"
company.dig(:address, :country) # => nil (エラーにならない)
シンボル
シンボルは文字列に似ているが不変で一意なオブジェクト
シンボルと文字列の違いは内部では整数として管理されるので、高速処理・全く同じオブジェクトとして参照できる、破壊的な変更はできない
# シンボルの作成
:シンボルの名前
:name
:email
:status
# 文字列との違い
"hello".object_id # => 70123456789000
"hello".object_id # => 70123456789020 (異なるID)
:hello.object_id # => 1234567
:hello.object_id # => 1234567 (常に同じID)
シンボルの特徴
シンボルの不変性、メモリ効率、比較速度などの特性
# 1. イミュータブル(不変)
str = "hello"
str.upcase! # OK
sym = :hello
# sym.upcase! # エラー!シンボルは変更できない
# 2. 同じシンボルは同じオブジェクト
:name.equal?(:name) # => true
"name".equal?("name") # => false
# 3. メモリ効率が良い
1000.times { "hello" } # 1000個のオブジェクト
1000.times { :hello } # 1つのオブジェクトを再利用
# 4. 比較が高速
"hello" == "hello" # 文字列を1文字ずつ比較
:hello == :hello # オブジェクトIDの比較(高速)
ブロックとイテレータ
ブロックはメソッドの引数として渡せれる処理のかたまり
rubyではほとんどforによる繰り返しを行わず、eachメソッドを利用する
eachメソッドが範囲の各値をnに次々代入してブロックを実行
ブロックの基本
# 1行のブロック(波括弧)
[1, 2, 3].each { |n| puts n * 2 }
# 複数行のブロック(do...end)
[1, 2, 3].each do |n|
doubled = n * 2
puts doubled
end
# どちらも同じ意味
イテレータ
# 基本的なブロック
(1..5).each { |i| puts 2 * i } # |i| = ブロックで使う変数
# ハッシュのイテレーション
user = { name: "太郎", age: 25, email: "taro@example.com" }
# each: キーと値を取得
user.each do |key, value|
puts "#{key}: #{value}"
end
ループ処理
while文
while構文: 条件が真の間繰り返す
# 基本形
i = 1
while i <= 5
puts i
i += 1
end
# => 1, 2, 3, 4, 5
# 条件が偽になるまで繰り返す
count = 0
while count < 3
puts "Count: #{count}"
count += 1
end
for文
配列の各要素に対して処理を実行
# 配列の各要素に対して
fruits = ["apple", "banana", "orange"]
for fruit in fruits
puts fruit
end
# 範囲に対して
for i in 1..5
puts i
end
# 注意: Rubyではfor文よりeachの方が推奨される
fruits.each { |fruit| puts fruit } # こちらが推奨
その他
多重代入
複数の変数に同時に値を代入できる
# 複数の変数に同時に代入
a, b = 1, 2
# => a = 1, b = 2
# 右辺が足りない場合はnil
a, b = 1
# => a = 1, b = nil
# 右辺が多い場合は切り捨て
a, b = 1, 2, 3, 4
# => a = 1, b = 2 (3と4は無視される)
# 配列の展開
values = [10, 20, 30]
x, y, z = values
# => x = 10, y = 20, z = 30
# 残りをまとめて取得(スプラット演算子)
first, *rest = 1, 2, 3, 4, 5
# => first = 1, rest = [2, 3, 4, 5]
# 値の交換
a, b = b, a
# 使わない値はアンダースコア
name, _, age = ["太郎", "男性", 25]
# => name = "太郎", age = 25 (性別は無視)
puts, print, p, pp
標準出力を行う4つのメソッドで、それぞれ挙動が異なります。putsやprintは一般ユーザー向け、p/ppは開発者向けです。
x = [1, 2, 3]
# puts: 改行を加えて出力、戻り値はnil
puts x
# => 1
# 2
# 3
# 戻り値: nil
# print: 改行なしで出力、戻り値はnil
print x
# => [1, 2, 3]
# 戻り値: nil
# p: inspectメソッドを使って出力、戻り値はオブジェクト自身
p x
# => [1, 2, 3]
# 戻り値: [1, 2, 3]
# pp: pの整形版(require 'pp'が必要)
require 'pp'
pp x
# => [1, 2, 3]
# 戻り値: [1, 2, 3]
Safe Navigation Operator(ぼっち演算子)
# Ruby 2.3以降
user = nil
# 通常の書き方(エラーになる)
# user.name # NoMethodError
# ぼっち演算子
user&.name # => nil (エラーにならない)
# 実用例
def get_user_email(user)
user&.profile&.email&.downcase
end
get_user_email(nil) # => nil (安全)
Discussion