|
1 | | -# 第4章: ブロックとイテレータ - Rubyの最重要機能 |
| 1 | +# 第4章: すべてがオブジェクト |
2 | 2 |
|
3 | | -Rubyの学習において、**ブロック (Block)** は最も重要で強力な機能の一つです。他言語の経験者にとって、これはラムダ式や無名関数、クロージャに似た概念ですが、Rubyではこれが言語構文の核に深く組み込まれています。 |
| 3 | +Rubyの設計思想における最も重要かつ強力なコンセプトの一つは、「すべてがオブジェクトである」という点です。他の言語、例えばJavaやC++では、数値(int, double)や真偽値(boolean)は「プリミティブ型」として扱われ、オブジェクトとは区別されます。 |
4 | 4 |
|
5 | | -この章では、ブロックの使い方と、ブロックを活用する「イテレータ」と呼ばれるメソッドを学びます。 |
| 5 | +しかしRubyでは、`5` のような数値も、`"hello"` のような文字列も、そして `nil` さえも、すべてがメソッド(振る舞い)を持つオブジェクトです。 |
6 | 6 |
|
7 | | -## ブロック構文: do...end と {} |
| 7 | +## 🎯 Rubyの核心: 5.times の衝撃 |
8 | 8 |
|
9 | | -ブロックとは、メソッド呼び出しに渡すことができる**コードの塊**です。メソッド側は、受け取ったそのコードの塊を好きなタイミングで実行できます。 |
10 | | - |
11 | | -ブロックには2種類の書き方があります。 |
12 | | - |
13 | | -1. **`{ ... }` (波括弧)**: 通常、1行で完結する場合に使われます。 |
14 | | -2. **`do ... end`**: 複数行にわたる処理を書く場合に使われます。 |
15 | | - |
16 | | -どちらも機能的にはほぼ同じです。最も簡単な例は、指定した回数だけブロックを実行する `times` メソッドです。 |
| 9 | +他の言語の経験者がRubyに触れて最初に驚くことの一つが、以下のようなコードが動作することです。 |
17 | 10 |
|
18 | 11 | ```ruby-repl:1 |
19 | | -irb(main):001:0> 3.times { puts "Hello!" } |
20 | | -Hello! |
21 | | -Hello! |
22 | | -Hello! |
23 | | -=> 3 |
24 | | -
|
25 | | -irb(main):002:0> 3.times do |
26 | | -irb(main):003:1* puts "Ruby is fun!" |
27 | | -irb(main):004:1> end |
28 | | -Ruby is fun! |
29 | | -Ruby is fun! |
30 | | -Ruby is fun! |
31 | | -=> 3 |
| 12 | +irb(main):001:0> 5.times do |
| 13 | +irb(main):002:1* print "Ruby! " |
| 14 | +irb(main):003:1> end |
| 15 | +Ruby! Ruby! Ruby! Ruby! Ruby! => 5 |
32 | 16 | ``` |
33 | 17 |
|
34 | | -`3.times` というメソッド呼び出しの後ろに `{ ... }` や `do ... end` で囲まれたコードブロックを渡しています。`times` メソッドは、そのブロックを3回実行します。 |
35 | | - |
36 | | -## 代表的なイテレータ |
37 | | - |
38 | | -Rubyでは、コレクション(配列やハッシュなど)の各要素に対して処理を行うメソッドを**イテレータ (Iterator)** と呼びます。イテレータは通常、ブロックを受け取って動作します。 |
| 18 | +`5` という数値リテラルが `.times` というメソッドを呼び出しています。これは、`5` が単なる値ではなく、`Integer` クラスのインスタンス(オブジェクト)だからです。 |
39 | 19 |
|
40 | | -代表的なイテレータを見ていきましょう。 |
41 | | - |
42 | | -### each |
43 | | - |
44 | | -`each` は、コレクションの各要素を順番に取り出してブロックを実行します。他言語の `foreach` ループに最も近いものです。 |
45 | | - |
46 | | -`|n|` の部分は**ブロック引数**と呼ばれ、イテレータが取り出した要素(この場合は配列の各要素)を受け取ります。 |
| 20 | +同様に、文字列もオブジェクトです。 |
47 | 21 |
|
48 | 22 | ```ruby-repl:2 |
49 | | -irb(main):001:0> numbers = [1, 2, 3] |
50 | | -=> [1, 2, 3] |
51 | | -
|
52 | | -irb(main):002:0> numbers.each do |n| |
53 | | -irb(main):003:1* puts "Current number is #{n}" |
54 | | -irb(main):004:1> end |
55 | | -Current number is 1 |
56 | | -Current number is 2 |
57 | | -Current number is 3 |
58 | | -=> [1, 2, 3] |
| 23 | +irb(main):001:0> "hello, world".upcase |
| 24 | +=> "HELLO, WORLD" |
| 25 | +irb(main):002:0> "hello, world".length |
| 26 | +=> 12 |
59 | 27 | ``` |
60 | 28 |
|
61 | | -> **Note:** `each` メソッドの戻り値は、元の配列 (`[1, 2, 3]`) 自身です。`each` はあくまで「繰り返すこと」が目的であり、ブロックの実行結果は利用しません。 |
62 | | -
|
63 | | -### map (collect) |
| 29 | +`"hello, world"` という `String` オブジェクトが、`upcase` や `length` というメソッド(メッセージ)に応答しています。 |
64 | 30 |
|
65 | | -`map` は、各要素に対してブロックを実行し、その**ブロックの戻り値**を集めた**新しい配列**を返します。 |
| 31 | +`.class` メソッドを使うと、そのオブジェクトがどのクラスに属しているかを確認できます。 |
66 | 32 |
|
67 | 33 | ```ruby-repl:3 |
68 | | -irb(main):005:0> numbers = [1, 2, 3] |
69 | | -=> [1, 2, 3] |
70 | | -
|
71 | | -irb(main):006:0> doubled = numbers.map { |n| n * 2 } |
72 | | -=> [2, 4, 6] |
73 | | -
|
74 | | -irb(main):007:0> puts doubled.inspect |
75 | | -[2, 4, 6] |
76 | | -=> nil |
77 | | -
|
78 | | -irb(main):008:0> puts numbers.inspect # 元の配列は変更されない |
79 | | -[1, 2, 3] |
80 | | -=> nil |
| 34 | +irb(main):001:0> 5.class |
| 35 | +=> Integer |
| 36 | +irb(main):002:0> "hello".class |
| 37 | +=> String |
| 38 | +irb(main):003:0> 3.14.class |
| 39 | +=> Float |
81 | 40 | ``` |
82 | 41 |
|
83 | | -`map` は、元の配列を変換した新しい配列が欲しい場合に非常に便利です。 |
| 42 | +## 👻 nil オブジェクト: 無ですらオブジェクト |
84 | 43 |
|
85 | | -### select (filter) |
| 44 | +Rubyには「何もない」「無効」な状態を示す `nil` という特別な値があります。これは他の言語における `null` や `None` に相当します。 |
86 | 45 |
|
87 | | -`select` は、各要素に対してブロックを実行し、ブロックの戻り値が**真 (true)** になった要素だけを集めた**新しい配列**を返します。 |
| 46 | +しかし、Rubyの哲学を徹底している点は、この `nil` ですらオブジェクトであるということです。 |
88 | 47 |
|
89 | 48 | ```ruby-repl:4 |
90 | | -irb(main):009:0> numbers = [1, 2, 3, 4, 5, 6] |
91 | | -=> [1, 2, 3, 4, 5, 6] |
92 | | -
|
93 | | -irb(main):010:0> evens = numbers.select { |n| n.even? } # n.even? は n % 2 == 0 と同じ |
94 | | -=> [2, 4, 6] |
| 49 | +irb(main):001:0> nil.class |
| 50 | +=> NilClass |
95 | 51 | ``` |
96 | 52 |
|
97 | | -### find (detect) |
98 | | - |
99 | | -`find` は、ブロックの戻り値が**真 (true)** になった**最初の要素**を返します。見つからなければ `nil` を返します。 |
| 53 | +`nil` は `NilClass` という専用クラスの唯一のインスタンスです。オブジェクトであるため、`nil` もメソッドを持ちます。 |
100 | 54 |
|
101 | 55 | ```ruby-repl:5 |
102 | | -irb(main):011:0> numbers = [1, 2, 3, 4, 5, 6] |
103 | | -=> [1, 2, 3, 4, 5, 6] |
104 | | -
|
105 | | -irb(main):012:0> first_even = numbers.find { |n| n.even? } |
106 | | -=> 2 |
107 | | -
|
108 | | -irb(main):013:0> over_10 = numbers.find { |n| n > 10 } |
109 | | -=> nil |
110 | | -``` |
111 | | - |
112 | | -## for ループとの比較 |
113 | | - |
114 | | -他言語経験者の方は、`for` ループを使いたくなるかもしれません。 |
115 | | - |
116 | | -```c |
117 | | -// C や Java の for ループ |
118 | | -for (int i = 0; i < 3; i++) { |
119 | | - printf("Hello\n"); |
120 | | -} |
| 56 | +irb(main):001:0> nil.nil? |
| 57 | +=> true |
| 58 | +irb(main):002:0> "hello".nil? |
| 59 | +=> false |
| 60 | +irb(main):003:0> nil.to_s |
| 61 | +=> "" |
| 62 | +irb(main):004:0> nil.to_i |
| 63 | +=> 0 |
121 | 64 | ``` |
122 | 65 |
|
123 | | -Rubyにも `for` 構文は存在します。 |
| 66 | +`nil` がメソッドを持つことで、`null` チェックに起因するエラー(例えば `null.someMethod()` のような呼び出しによるエラー)を避けやすくなり、より安全で流暢なコードが書ける場合があります。 |
124 | 67 |
|
125 | | -```ruby-repl:6 |
126 | | -irb(main):014:0> numbers = [1, 2, 3] |
127 | | -=> [1, 2, 3] |
128 | | -
|
129 | | -irb(main):015:0> for num in numbers |
130 | | -irb(main):016:1* puts num |
131 | | -irb(main):017:1> end |
132 | | -1 |
133 | | -2 |
134 | | -3 |
135 | | -=> [1, 2, 3] |
136 | | -``` |
| 68 | +## 📨 メソッド呼び出しの仕組み: メッセージパッシング |
137 | 69 |
|
138 | | -しかし、Rubyの世界では `for` ループは**ほとんど使われません**。なぜなら、`for` は内部的に `each` メソッドを呼び出しているに過ぎないからです。 |
| 70 | +Rubyのメソッド呼び出し `オブジェクト.メソッド名(引数)` は、厳密には「**メッセージパッシング**」という概念に基づいています。 |
139 | 71 |
|
140 | | -Rubyプログラマは、`for` よりも `each` などのイテレータをブロックと共に使うことを圧倒的に好みます。イテレータの方が、何をしているか(単なる繰り返し、変換、選択など)がメソッド名 (`each`, `map`, `select`) から明確であり、コードが読みやすくなるためです。 |
| 72 | +`5.times` というコードは、以下のように解釈されます。 |
141 | 73 |
|
142 | | -## ブロック引数とブロックの戻り値 |
| 74 | +1. レシーバ(受信者): `5` という `Integer` オブジェクト |
| 75 | +2. メッセージ: `:times` というシンボル(メソッド名) |
| 76 | +3. `5` オブジェクトに `:times` というメッセージを送る。 |
| 77 | +4. `5` オブジェクト(の所属する `Integer` クラス)は、そのメッセージを解釈し、関連付けられた処理(ブロックを5回実行する)を実行する。 |
143 | 78 |
|
144 | | -すでに出てきたように、ブロックは `| ... |` を使って引数を受け取ることができます。 |
| 79 | +この考え方は、オブジェクト指向の「カプセル化(オブジェクトが自身の振る舞いを決定する)」を強力にサポートします。`+` などの演算子でさえ、実際にはメソッド呼び出しのシンタックスシュガー(糖衣構文)です。 |
145 | 80 |
|
146 | | -```ruby-repl:7 |
147 | | -irb(main):018:0> ["Alice", "Bob"].each do |name| |
148 | | -irb(main):019:1* puts "Hello, #{name}!" |
149 | | -irb(main):020:1> end |
150 | | -Hello, Alice! |
151 | | -Hello, Bob! |
152 | | -=> ["Alice", "Bob"] |
| 81 | +```ruby-repl:6 |
| 82 | +irb(main):001:0> 10 + 3 |
| 83 | +=> 13 |
| 84 | +irb(main):002:0> 10.+(3) # 内部的にはこれと同じ |
| 85 | +=> 13 |
153 | 86 | ``` |
154 | 87 |
|
155 | | -また、ブロックも(Rubyのすべての式と同様に)戻り値を持ちます。ブロックの戻り値とは、**ブロック内で最後に評価された式の値**です。 |
156 | | - |
157 | | - * `each` はブロックの戻り値を**無視**します。 |
158 | | - * `map` はブロックの戻り値を**集めて新しい配列**にします。 |
159 | | - * `select` はブロックの戻り値が**真か偽か**を判定に使います。 |
160 | | - |
161 | | -```ruby-repl:8 |
162 | | -irb(main):021:0> result = [1, 2].map do |n| |
163 | | -irb(main):022:1* m = n * 10 # mは 10, 20 |
164 | | -irb(main):023:1* m + 5 # ブロックの戻り値 (15, 25) |
165 | | -irb(main):024:1> end |
166 | | -=> [15, 25] |
167 | | -``` |
| 88 | +## 🛠️ よく使う組み込みクラスのメソッド |
168 | 89 |
|
169 | | -## yield:ブロックを受け取るメソッド |
| 90 | +すべてがオブジェクトであるため、Rubyは基本的なデータ型に対して非常に多くの便利なメソッドを標準で提供しています。 |
170 | 91 |
|
171 | | -では、どうすればブロックを受け取るメソッドを自分で作れるのでしょうか? |
172 | | -それには `yield` というキーワードを使います。 |
| 92 | +### String (文字列) |
173 | 93 |
|
174 | | -メソッド内で `yield` が呼び出されると、そのメソッドに渡されたブロックが実行されます。 |
| 94 | +`String` クラスには、テキスト操作のための豊富なメソッドが用意されています。 |
175 | 95 |
|
176 | | -```ruby:yield_basic.rb |
177 | | -def simple_call |
178 | | - puts "メソッド開始" |
179 | | - yield # ここでブロックが実行される |
180 | | - puts "メソッド終了" |
181 | | -end |
| 96 | +```ruby:string_methods.rb |
| 97 | +text = " ruby is convenient " |
182 | 98 |
|
183 | | -simple_call do |
184 | | - puts "ブロック内の処理です" |
185 | | -end |
186 | | -``` |
| 99 | +# 先頭と末尾の空白を除去 |
| 100 | +cleaned_text = text.strip |
| 101 | +puts "Strip: '#{cleaned_text}'" |
187 | 102 |
|
188 | | -```ruby-exec:yield_basic.rb |
189 | | -メソッド開始 |
190 | | -ブロック内の処理です |
191 | | -メソッド終了 |
192 | | -``` |
| 103 | +# 先頭の文字を大文字に |
| 104 | +puts "Capitalize: #{cleaned_text.capitalize}" |
193 | 105 |
|
194 | | -`yield` はブロックに引数を渡すこともできます。 |
| 106 | +# "convenient" を "powerful" に置換 |
| 107 | +puts "Gsub: #{cleaned_text.gsub("convenient", "powerful")}" |
195 | 108 |
|
196 | | -```ruby:yield_with_args.rb |
197 | | -def call_with_name(name) |
198 | | - puts "メソッド開始" |
199 | | - yield(name) # ブロックに "Alice" を渡す |
200 | | - yield("Bob") # ブロックに "Bob" を渡す |
201 | | - puts "メソッド終了" |
202 | | -end |
| 109 | +# "ruby" という文字列で始まっているか? |
| 110 | +puts "Start with 'ruby'?: #{cleaned_text.start_with?("ruby")}" |
203 | 111 |
|
204 | | -call_with_name("Alice") do |n| |
205 | | - puts "ブロックが #{n} を受け取りました" |
206 | | -end |
| 112 | +# 単語に分割 (配列が返る) |
| 113 | +words = cleaned_text.split(" ") |
| 114 | +p words # p はデバッグ用の表示メソッド |
207 | 115 | ``` |
208 | 116 |
|
209 | | -```ruby-exec:yield_with_args.rb |
210 | | -メソッド開始 |
211 | | -ブロックが Alice を受け取りました |
212 | | -ブロックが Bob を受け取りました |
213 | | -メソッド終了 |
| 117 | +```ruby-exec:string_methods.rb |
| 118 | +Strip: 'ruby is convenient' |
| 119 | +Capitalize: Ruby is convenient |
| 120 | +Gsub: ruby is powerful |
| 121 | +Start with 'ruby'?: true |
| 122 | +["ruby", "is", "convenient"] |
214 | 123 | ``` |
215 | 124 |
|
216 | | -`each` や `map` のようなイテレータは、内部でこの `yield` を使って、コレクションの各要素をブロックに渡しながら実行しているのです。 |
217 | | - |
218 | | -## この章のまとめ |
| 125 | +### Integer / Float (数値) |
219 | 126 |
|
220 | | - * **ブロック**は、メソッドに渡せるコードの塊で、`{}`(1行)または `do...end`(複数行)で記述します。 |
221 | | - * **イテレータ**は、ブロックを受け取り、要素の繰り返し処理を行うメソッドです(`each`, `map`, `select` など)。 |
222 | | - * Rubyでは `for` ループよりもイテレータが好まれます。 |
223 | | - * ブロックは `|arg|` で引数を受け取ることができ、ブロックの最後の式の値が戻り値となります。 |
224 | | - * 自作メソッド内で `yield` を使うと、渡されたブロックを実行できます。 |
| 127 | +数値クラス (総称して `Numeric`) も便利なメソッドを持っています。 |
225 | 128 |
|
226 | | -### 練習問題1 |
227 | | - |
228 | | -数値の配列 `[1, 2, 3, 4, 5]` があります。`map` イテレータとブロックを使って、各要素を文字列に変換し(例: `1` → `"1"`)、 `"1"`, `"2"`, `"3"`, `"4"`, `"5"` という文字列の配列を作成してください。 |
| 129 | +```ruby-repl:7 |
| 130 | +irb(main):001:0> # Integer |
| 131 | +irb(main):002:0> 10.even? |
| 132 | +=> true |
| 133 | +irb(main):003:0> 10.odd? |
| 134 | +=> false |
| 135 | +irb(main):004:0> 5.to_s |
| 136 | +=> "5" |
| 137 | +irb(main):005:0> 5.to_f |
| 138 | +=> 5.0 |
| 139 | +
|
| 140 | +irb(main):006:0> # Float |
| 141 | +irb(main):007:0> 10.5.round |
| 142 | +=> 11 |
| 143 | +irb(main):008:0> 10.5.floor # 切り捨て |
| 144 | +=> 10 |
| 145 | +irb(main):009:0> 10.5.ceil # 切り上げ |
| 146 | +=> 11 |
| 147 | +irb(main):010:0> (10.5).to_i |
| 148 | +=> 10 |
| 149 | +``` |
| 150 | + |
| 151 | +## 📜 この章のまとめ |
| 152 | + |
| 153 | + * Rubyでは、数値、文字列、`nil` を含むすべてが **オブジェクト** です。 |
| 154 | + * すべてのオブジェクトは **クラス** に属しています(例: `5` は `Integer` クラス)。 |
| 155 | + * オブジェクトであるため、すべての値は **メソッド** を持つことができます(例: `5.times`, `"hello".upcase`)。 |
| 156 | + * メソッド呼び出しは、オブジェクトへの **メッセージパッシング** として理解されます。 |
| 157 | + * `nil` も `NilClass` のオブジェクトであり、メソッドを持ちます。 |
| 158 | + |
| 159 | +### 練習問題1: 文字列の操作 |
| 160 | +変数 `sentence = " Welcome to the Ruby World! "` があります。`String` のメソッドを組み合わせて、最終的に `"WELCOME, RUBY"` という文字列をコンソールに出力してください。 |
| 161 | + |
| 162 | + * ヒント: `strip`, `upcase`, `gsub` (または `sub`), `slice` (またはインデックスアクセス `[]`) などが使えます。 |
229 | 163 |
|
230 | 164 | ```ruby:practice4_1.rb |
231 | | -array = [1, 2, 3, 4, 5] |
232 | | - |
| 165 | +sentence = " Welcome to the Ruby World! " |
233 | 166 | ``` |
234 | 167 |
|
235 | 168 | ```ruby-exec:practice4_1.rb |
236 | 169 | ``` |
237 | 170 |
|
238 | | -### 練習問題2 |
239 | | -文字列の配列 `["apple", "banana", "cherry", "date"]` があります。`select` イテレータとブロックを使って、文字数が5文字以上の果物だけを抽出した新しい配列(`["apple", "banana", "cherry"]`)を作成してください。 |
240 | 171 |
|
241 | | -```ruby:practice4_2.rb |
242 | | -array = ["apple", "banana", "cherry", "date"] |
| 172 | +### 練習問題2: 数値と判定 |
243 | 173 |
|
| 174 | +`Float` の値 `123.456` があります。この値を四捨五入して整数(`Integer`)にした後、その整数が偶数(even)か奇数(odd)かを判定して、`"Result is even"` または `"Result is odd"` と出力するコードを書いてください。 |
| 175 | + |
| 176 | +```ruby:practice4_2.rb |
| 177 | +value = 123.456 |
244 | 178 | ``` |
245 | 179 |
|
246 | 180 | ```ruby-exec:practice4_2.rb |
|
0 commit comments