Railsチュートリアル 第4章 - Rails風味のRubyを学ぶ …ブロック、配列・範囲・メソッド

# ブロック

```irb:
>> (1..5).each { |i| puts 2 * i } 
2
4
6
8
10
=> 1..5
```

上記コードにおけるブロックの基本的な説明は以下のとおりです。

* 波括弧`{}`で囲むことにより、その部分がブロックであることを示す
* `|i|`のように縦棒`|`に囲まれた変数名は、ブロックを操作するための変数であることを示す
  * 任意の名前を使うことができる
* 範囲に含まれるそれぞれの値を変数`i`に次々代入していく

以下のコードも、上記コードと同じ動作をします。

```irb:
>> (1..5).each do |i|
?>   puts 2 * i
>> end
2
4
6
8
10
=> 1..5
```

上記は波括弧`{}`ではなく、`do`キーワードと`end`キーワードで囲むことによってブロックを示す記法を用いた例です。一般に、ブロックが1行で構成されるならば`{}`で、複数行にわたるならば`do..end`でブロックを示すことが多いです。

以下はブロック内で変数を使わない例です。

```irb:
>> 3.times{ puts "Betelgeuse!" }  # ブロック内で変数を使わない例
Betelgeuse!
Betelgeuse!
Betelgeuse!
=> 3
```

Rubyには、「引数として与えたブロックの内容を、配列や範囲の要素の数だけ繰り返し実行し、実行結果を配列として返す」というメソッドがあります。そのメソッドの名前は、`map`あるいは`collect`といいます（名前が違うだけで動作は同じ）。以下、`map`メソッドを用いた例です。

```irb:
>> (1..5).map{ |i| i**2 }  # 「**」記法は冪乗
=> [1, 4, 9, 16, 25]

>> %w[a b c]  # %w で文字列の配列を作成する
=> ["a", "b", "c"]
>> %w[a b c].map{ |char| char.upcase }
=> ["A", "B", "C"]
>> %w[A B C].map{ |char| char.downcase }
=> ["a", "b", "c"]
```

## `%w`という記法

`%w`という記法を使えば、ブラケット記号`[]`や引用符`''``""`なしで文字列を要素とした配列を生成することができます。この例では、文字列先頭と文字列終端を表す記号としてブラケット記号を使っていますが、対応が取れるならば別の記号でも構いません。例えば、以下のような記述でもOKです。

```irb:
>> %w(a b c)
=> ["a", "b", "c"]

>> %w?a b c?
=> ["a", "b", "c"]
```

なお、`%w`という記法の場合、文字列中の式は展開されません（シングルクォート`''`と同等の動作）。文字列中の式を展開させたい場合は、`%W`という記法を使います。

```irb:
>> langauge = 'Ruby'
=> "Ruby"

>> %w(#{langauge} Java Python)
=> ["\#{langauge}", "Java", "Python"]

>> %W(#{langauge} Java Python)
=> ["Ruby", "Java", "Python"]
```

## 単体テストにおけるブロック

```Ruby:
test "should get home" do
  get static_pages_home_url
  assert_response :success
  assert_select "title", "Ruby on Rails Tutorial Sample App"
end
```

`do`と`end`というキーワードが含まれているので、この単体テストコードの基本的な構造もブロックです。

* `test`という名前のメソッドを定義している
* `test`メソッドは、文字列（説明文）とブロックを引数として取る
* テストが実行される際、ブロック内部のコードが実行される

今のところは、上記の点を理解しておきましょう。

## `('a'..'z').to_a.shuffle[0..7].join`というコードの意味

第1章において、ランダムなサブドメインを生成するために用いたコードです。メソッド、メソッドチェーン、配列、範囲まで理解していれば、このコードの意味も理解できるかと思います。

```irb:
>> ('a'..'z')  # 英小文字から成る範囲を定義する
=> "a".."z"
>> ('a'..'z').to_a  # 範囲の中身を列挙した配列に変換する
=> ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]
>> ('a'..'z').to_a.shuffle  # 配列の要素をランダムに並べ替える
=> ["x", "f", "y", "b", "i", "a", "q", "t", "c", "v", "u", "k", "o", "p", "n", "l", "e", "r", "z", "h", "w", "m", "s", "g", "d", "j"]
>> ('a'..'z').to_a.shuffle[0..7]  # 配列の先頭7つの要素を取り出す
=> ["c", "d", "b", "p", "h", "o", "m", "j"]
>> ('a'..'z').to_a.shuffle[0..7].join  # 取り出した要素を結合し、1つの文字列に変換する
=> "pkahitcm"
```

## 余談…`Symbol#to_proc`記法

```irb:
>> %w[A B C].map{ |char| char.downcase }
=> ["a", "b", "c"]

>> %w[A B C].map(&:downcase.to_proc)
=> ["a", "b", "c"]

>> %w[A B C].map(&:downcase) 
=> ["a", "b", "c"]
```

以上3つの記法は、いずれも同じ動作をします。このあたりの挙動はいかなるものなのか、何がどうなっているのかは、現時点では私もよくわかっていません。しかし、このような記法の裏側で動いている仕組みこそが、Rubyというプログラミング言語の醍醐味であることは確かなようです。実際、Web上にもこの仕組みの解説記事は少なからず存在します。

* [instance method Symbol#to_proc - Ruby 2.6.0 リファレンスマニュアル](https://docs.ruby-lang.org/ja/latest/method/Symbol/i/to_proc.html)
* [RubyのSymbol#to_procを考えた人になってみる - hp12c](https://melborne.github.io/2008/09/17/Ruby-Symbol-to_proc/)
* [Rubyの[1, 2, 3].map(&:to_s)的なイディオムとSymbol#to_procについて - ぺけみさお](https://www.xmisao.com/2014/02/09/ruby-array-map-idiom-symbol-to-proc.html)

## 余談…`map`と`collect`

同じ動作をするメソッドに対し、`map`と`collect`という異なった名前が存在する理由としては、多分に宗教的なものがあるのだそうです。具体的には、「関数型 vs. 手続き型」というやつです。[Rubyist Magazineに詳細な解説がある](https://magazine.rubyist.net/articles/0038/0038-MapAndCollect.html)ので、興味がある方はご一読ください。

# 演習 - ブロック、配列・範囲・メソッド

## 1. 範囲オブジェクト0..16を使って、各要素の２乗を出力してください。

`{}`形式の記法では、以下のようになります。

```irb:
>> (1..16).each {|i| puts i**2}
1
4
9
16
25
36
49
64
81
100
121
144
169
196
225
256
=> 1..16
```

ブロック形式の記法では、以下のようになります。

```irb:
>> (1..16).each do |i|           
?>   puts i**2
>> end
1
4
9
16
25
36
49
64
81
100
121
144
169
196
225
256
=> 1..16
```

## 2. yeller (大声で叫ぶ) というメソッドを定義してください。

> このメソッドは、文字列の要素で構成された配列を受け取り、各要素を連結した後、大文字にして結果を返します。例えばyeller([’o’, ’l’, ’d’])と実行したとき、"OLD"という結果が返ってくれば成功です。ヒント: mapとupcaseとjoinメソッドを使ってみましょう。

以下のように定義できますね。

```irb:
>> def yeller(arr)
>>   arr.map(&:upcase).join
>> end
=>:yeller

>> yeller(%w[o l d])
=> "OLD"
```

あるいは以下。

```irb:
>> def yeller(arr)
>>   arr.map{ |char| char.upcase }.join
>> end
=>:yeller

>> yeller(%w[o l d])
=> "OLD"
```

あるいは以下。

```irb:
>> def yeller2(arr)
>>   arr.map do |char|
?>     char.upcase
>>   end
>> end
=> :yeller

>> yeller(%w[o l d])
=> ["O", "L", "D"]
```

## 3. random_subdomainというメソッドを定義してください。

> このメソッドはランダムな8文字を生成し、文字列として返します。ヒント: サブドメインを作るときに使ったRubyコードをメソッド化したものです。

以下のような感じです。

```irb:
>> def random_subdomain()
>>   ('a'..'z').to_a.shuffle[0..7].join
>> end
=> :random_subdomain

>> random_subdomain
=> "dzgcvjiu"
>> random_subdomain
=> "bcztnsav"
>> random_subdomain
=> "sifawold"
```

## 4. [リスト 4.12](https://railstutorial.jp/chapters/rails_flavored_ruby?version=5.1#code-string_shuffle)の「?」の部分を、それぞれ適切なメソッドに置き換えてみてください。

> ヒント:split、shuffle、joinメソッドを組み合わせると、メソッドに渡された文字列 (引数) をシャッフルさせることができます。

以下のような感じです。

```irb:
>> def string_shuffle(s)
>>   s.split('').shuffle.join
>> end

=> :string_shuffle
>> string_shuffle("foobar")
=> "frboao"
```