Ruby 技術者認定試験 Silver/Gold 対策の個人的なハマり問題集

## 概要

- Ruby技術者認定試験のSilver/Gold対策の勉強で、私自身が何回も間違ってしまう問題を集めてみました
- 掲載した問題と解説は、基本、[RubyExamination](https://rex.libertyfish.co.jp/)を参考にしていますので実際に対策に取り組む場合は、そちらのサイトで取り組んでください！

## 個人的なハマり問題集

### 問題1. Enumerable#injectの使い方

```ruby
# 次のコードを実行するとどうなりますか

p [1, 2, 3].inject{|x, y| x + y ** 2} rescue p $!
#=> 14

p [1, 2, 3].inject(0){|x, y| x + y ** 2} rescue p $!
#=> 14

p [1, 2, 3].inject([]){|x, y| x << y ** 2} rescue p $!
#=> [1, 4, 9]

p [1, 2, 3].inject do|x, y| x + y ** 2 end rescue p $!
#=> #<LocalJumpError: no block given>
```

#### 解説

- [Enumerable#inject](https://docs.ruby-lang.org/ja/2.1.0/method/Enumerable/i/inject.html)はブロックを使用して繰り返し計算を行うメソッド
- injectメソッドには以下のような特徴がある
    - ブロック引数の1番目は前回の戻り値が渡される。初回は、初期値が渡される
    - ブロック引数の2番目は要素が順番に渡される
    - 引数を省略した場合は、要素1が初期値になる
    - 引数を指定した場合は、その値が初期値になる
- 4行目は、[1, 2, 3].injectまでがpメソッドの引数となるため、pメソッドへブロックが不正に渡されるため、エラーとなる

```ruby
p([1, 2, 3].inject) do|x, y|
  x + y ** 2
end rescue p $!
```

`do...end`と`{...}`挙動の違いに関しては、以下の記事でも取り上げている
[ブロックをdo…endで書くか{…}で書くかにより挙動が変わる例](https://qiita.com/riocampos/items/43e4431ddff93e01a18d)

#### 参考

- https://docs.ruby-lang.org/ja/2.1.0/method/Enumerable/i/inject.html
- [Ruby の inject メソッドを使いこなす](https://blog.toshimaru.net/ruby-inject/)
- [ブロックをdo…endで書くか{…}で書くかにより挙動が変わる例](https://qiita.com/riocampos/items/43e4431ddff93e01a18d)

### 問題2. `do ... end`と`{ ... }`の挙動の違い

```ruby
# 次のコードを実行するとどうなりますか

p [1,2,3,4].map do |e| e * e end
#=> #<Enumerator: [1, 2, 3, 4]:map>
```

#### 解説

- `do ... end`の結合度が弱いため、p([1, 2, 3, 4].map)とpが評価されてしまう
- 問題のように式の内容を直接使用する際は、{ ... }を使用
- `do...end`と`{...}`挙動の違いに関しては、以下の記事でも取り上げている
    - [ブロックをdo…endで書くか{…}で書くかにより挙動が変わる例](https://qiita.com/riocampos/items/43e4431ddff93e01a18d)

```ruby
# {…}だと、思った通りの出力になる

p [1,2,3,4].map { |e| e * e }
#=> [1, 4, 9, 16]と出力される
```

#### 参考

- [ブロックをdo…endで書くか{…}で書くかにより挙動が変わる例](https://qiita.com/riocampos/items/43e4431ddff93e01a18d)

### 問題3. Stringクラスの似たメソッド

```ruby
# 次のコードを実行するとどうなりますか

p " Liberty Fish   \r\n".strip
#=> "Liberty Fish"

p " Liberty Fish   \r\n".chomp
#=> " Liberty Fish   " 

p " Liberty Fish   \r\n".chop
#=> " Liberty Fish   "

p " Liberty Fish   ".chop
#=> " Liberty Fish  "
```

#### 解説

| Method　| 説明 |
|:--|:--|
| String#strip(String) | 文字列の先頭と末尾の空白文字(\t\r\n\f\v)を取り除きます。 |
| String#chomp(String) | 末尾から改行コードを取り除きます。 |
| String#chop(String) | 末尾の文字を取り除きます。ただし、文字列の末尾が"\r\n"であれば、2文字とも取り除きます。 |

#### 参考

- https://docs.ruby-lang.org/ja/2.1.0/method/String/i/strip.html
- https://docs.ruby-lang.org/ja/2.1.0/method/String/i/chomp.html
- https://docs.ruby-lang.org/ja/2.1.0/method/String/i/chop.html

### 問題4. Enumerable#each_consとEnumerable#each_slice

```ruby
# 次のコードを実行するとどうなりますか

(1..10).each_cons(3) {|arr| p arr }
#=> [1, 2, 3]
#=> [2, 3, 4]
#=> [3, 4, 5]
#=> [4, 5, 6]
#=> [5, 6, 7]
#=> [6, 7, 8]
#=> [7, 8, 9]
#=> [8, 9, 10]

(1..10).each_slice(3) {|arr| p arr }
#=> [1, 2, 3]
#=> [4, 5, 6]
#=> [7, 8, 9]
#=> [10]
```
#### 解説

| メソッド | 説明 |
|:--|:--|
|Enumerable#each_cons| enum.each_cons(n){ ｜arr｜ block }<br/>引数nで指定した数の要素を繰り返し取り出して、ブロックを実行します。<br />ブロック引数arrには、取り出した要素が配列で入ります。<br />nの数が要素数より大きいときは、ブロックを1度も実行しません。<br />[要素1, 要素2, 要素3, ...]、[要素2, 要素3, 要素4, ...]、[要素3, 要素4, 要素5, ...]、...のように取り出します。 |
|Enumerable#each_slice| enum.each_slice(n){ ｜arr｜ block }<br/>引数nで指定した数の要素を繰り返し取り出して、ブロックを実行します。<br />ブロック引数arrには、取り出した要素が配列で入ります。 <br />[要素1, 要素2, 要素3]、[要素4, 要素5, 要素6]、[要素7, 要素8, 要素9]のように取り出します。 |

#### 参考

- https://docs.ruby-lang.org/ja/2.1.0/method/Enumerable/i/each_cons.html
- https://docs.ruby-lang.org/ja/2.1.0/method/Enumerable/i/each_slice.html

### 問題5. Array#zipとArray#transpose

```ruby
# 次のコードを実行するとどうなりますか

p [1, 2].zip([3, 4])
#=> [[1, 3], [2, 4]]

p [[1, 2], [3, 4]].transpose
#=> [[1, 3], [2, 4]]

p [1, 2, 3].zip([3, 4])
#=> [[1, 3], [2, 4], [3, nil]]

p [[1, 2, 3], [3, 4]].transpose
#=> `transpose': element size differs (2 should be 3) (IndexError)
```

#### 解説

- zipとtransposeの大きな違いは、配列内の各配列の要素数が一致しないときの挙動である点に注意
    - zip: 足りない要素はnilで埋められ、余分な要素は捨てられる
    - transpose: 例外IndexErrorが発生する

| メソッド | 説明 |
|:--|:--|
|Array#zip|array.zip(other_array, ...)<br />配列の要素を引数の配列other_arrayの要素と組み合わせ、配列の配列を作成して返します。transposeメソッドで[array, other_array, ...].transposeとしたときと同じく、行と列を入れ替えます。ただし、transposeメソッドと違って足りない要素はnilで埋められ、余分な要素は捨てられます。|
|Array#transpose|array.transpose<br />配列の配列を行と列からなるデータと見立てて、行と列を入れ替えた配列の配列を作成して返します。配列内の各配列の要素数が一致しないときは例外IndexErrorが発生します。|

#### 参考

- https://docs.ruby-lang.org/ja/2.1.0/method/Enumerable/i/zip.html
- https://docs.ruby-lang.org/ja/2.1.0/method/Array/i/transpose.html

### 問題6. Hashクラスのメソッド

```ruby
# 次のコードを実行するとどうなりますか

hash = { 'apple' => 'grate', 'banana' => 'ole', 'orange' => 'juice' }
p hash.member?('apple')
#=> true

hash = { 'apple' => 'grate', 'banana' => 'ole', 'orange' => 'juice' }
p hash.to_a
#=> [["apple", "grate"], ["banana", "ole"], ["orange", "juice"]]

hash = { 'apple' => 'grate', 'banana' => 'ole', 'orange' => 'juice' }
# updateは、破壊的メソッドである点に注意!
hash.update('grape' => 'juice')
p hash
#=> {"apple"=>"grate", "banana"=>"ole", "orange"=>"juice", "grape"=>"juice"}

hash = { 'apple' => 'grate', 'banana' => 'ole', 'orange' => 'juice' }
# clearは、破壊的メソッドである点に注意!
p hash.clear
#=> {}
```

#### 解説

- updateメソッドは、`破壊的メソッド`である点に注意

| メソッド | 説明 |
|:--|:--|
|Hash#include?, member?|hash.member?(key)<br />ハッシュが引数keyと同じキーを持っていればtrue、なければfalseを返します。has_key?, key?, include?の別名です。|
|Hash#to_a|hash.to_a <br/>ハッシュを配列に変換して返します。[キー, 値]を並べた配列の配列ができます。|
|Hash#update|hash.update(other_hash)<br/>merge!の別名です。レシーバhashの内容に引数other_hashの内容を加えます。|
|Hash#clear|hash.clear<br />キーと値をすべて削除し、ハッシュを空にします。レシーバ自身を変更するメソッドです。戻り値はレシーバ自身です。|

#### 参考

- https://docs.ruby-lang.org/ja/2.1.0/method/Hash/i/update.html
- https://docs.ruby-lang.org/ja/2.1.0/method/Hash/i/to_a.html
- https://docs.ruby-lang.org/ja/2.1.0/method/Hash/i/include=3f.html

### 問題7. メソッド内の定数

```ruby
# 以下のコードを実行するとどうなりますか

def hoge
  x = 10
  Y = x < 10 ? "C" : "D"
  puts Y
end
hoge
#=> Error!! dynamic constant assignment
```

#### 解説

Rubyではメソッド内で定数を定義することができない


#### 参考

- https://docs.ruby-lang.org/ja/2.1.0/doc/spec=2fvariables.html#const

```
アルファベット大文字 ([A-Z]) で始まる識別子は定数です。 
定数の定義 (と初期化) は代入によって行われますが、メソッドの中では定義できません。
一度定義された定数に再び代入を行おうと すると警告メッセージが出ます。
定義されていない定数にアクセスすると例外 NameError が発生します。
```

### 問題8. 文字列の取り出し

```ruby
# 次のコードを実行するとどうなりますか

str = 'abcdefghijk'
p str[2, 4]
#=> "cdef"
p str.slice(2, 4)
#=> "cdef"

p str[2..4]
#=> "cde"
p str.slice(2..4)
#=> "cde"

p str[2...4]
#=> "cde"
p str.slice(2...4)
#=> "cde"

```

#### 解説

- []とsliceメソッドは、同じメソッド

| メソッド | 説明 |
|:--|:--|
|str[idx]|その位置の文字を整数のコードで返します（0が1番目、1が2番目、...）。範囲外の位置を指定すると、nilが返ります。|
|str[idx, len]|idxの位置からlen文字の文字列を返します（位置は0が1番目、1が2番目、...です）。範囲外の位置を指定すると、nilが返ります。|
|str[range]|範囲に対応する部分文字列を返します。範囲外の位置を指定すると、nilが返ります。|

#### 参考

- https://docs.ruby-lang.org/ja/2.1.0/method/String/i/=5b=5d=3d.html

### 問題9. Date.strftimeの引き数

```ruby
# 次のコードを実行するとどうなりますか


require 'date'

p Date.today.strftime('%F')
#=> "2018-09-13"

p Date.today.strftime('%Y-%m-%d')
#=> "2018-09-13"

p Date.today.to_s
#=> "2018-09-13"

p Date.today.strftime('%x')
#=> "09/13/18"

p Date.today.strftime('%m/%d/%y')
#=> "09/13/18"

p Date.today.strftime('%D')
#=> "09/13/18"
```

#### 解説

- `%F`, `%Y-%m-%d`, `to_s`は等価
    - 出力例: "2018-09-13"
- `%x`, `%D`, `%m/%d/%y`は等価
    - 出力例: "09/13/18"

| フォーマット | 説明 |
|:--|:--|
|%F|%Y-%m-%d と同等 (ISO 8601の日付フォーマット)|
|%Y|西暦を表す数|
|%y|西暦の下2桁(00-99)|
|%x|%m/%d/%y と同等|
|%m|月を表す数字(01-12)|
|%d|日(01-31)|
|%D|%m/%d/%y と同等|

#### 参考

- https://docs.ruby-lang.org/ja/2.1.0/method/Time/i/strftime.html

### 問題10. ==, eql?, equal?の違い

```ruby
x = 1
y = 1.0

p x == y
#=> true

p x.eql? y
#=> false

p x.equal? y
#=> false

p x.equal?(1)
#=> true
```

#### 解説


- Rubyでオブジェクトの比較をするメソッドは大きく分けて`==`、`eql?`、`equal?`の３つがある

| メソッド | 説明 |
|:--|:--|
|==|数値として等しいかを判定します。1と1.0は同じ数値として判定され、trueになります。|
|eql?|同じクラスのオブジェクトかつレシーバーにある==メソッドで等しいと判定された場合にtrueになります。|
|equal?|オブジェクトIDが同じであれば、trueになります。|

- Rubyリファレンスによると`eql?`は非推奨らしい。
    - `eql?メソッドを使わずに、「文字列の内容が同じかどうか」を調べるには==メソッドを、「同じオブジェクトかどうか」を調べるにはequal?メソッドを使ってください。`と記載されている
    - https://ref.xaio.jp/ruby/classes/string/eql

#### 参考

- https://ref.xaio.jp/ruby/classes/string/eql
- [Rubyにおける==と===とeql?とequal?の違いを今更](http://o.inchiki.jp/obbr/298)
- [Rubyの == と equal? と === と eql? のまとめ](http://d.hatena.ne.jp/k-sato_at_oiax/20100614/1276519946)

### 問題11. Lazyとchunkの使い方

```ruby:Lazy,chunk
# 次のコードを実行するとどうなりますか?

p (1..100).each.lazy.chunk(&:even?)
#=> #<Enumerator::Lazy: #<Enumerator: #<Enumerator::Generator:0x00007fd3711d8b38>:each>>
```

```ruby:最初の5つの値
# 先頭から5つの値を取り出すにはどのメソッドが必要ですか?
# 答え：first(5), take(5).force

p (1..100).each.lazy.chunk(&:even?).first(5)
#=> [[false, [1]], [true, [2]], [false, [3]], [true, [4]], [false, [5]]]

p (1..100).each.lazy.chunk(&:even?).take(5)
#=> #<Enumerator::Lazy: #<Enumerator::Lazy: #<Enumerator: #<Enumerator::Generator:0x00007f920e1d8a88>:each>>:take(5)>

p (1..100).each.lazy.chunk(&:even?).take(5).force
#=> [[false, [1]], [true, [2]], [false, [3]], [true, [4]], [false, [5]]]
```

#### 解説

- Lazyは、map や select などのメソッドの遅延評価版を提供するためのクラス
- chunkは、要素を前から順にブロックで評価し、その結果によって 要素をチャンクに分けた(グループ化した)要素を持つ Enumerator を返す
- Enumerator::Lazyは、とりあえずEnumeratorの用意はするが、数字を出すのは必要になってからになるので、非常に大きなRangeだとしても、すぐに値が返ってくる
    - [RubyのEnumeratorとEnumerator::怠惰(Lazy)の使い所とベンチマーク](http://tango-ruby.hatenablog.com/entry/2016/09/02/192053)の記事がわかりやすかった

| メソッド | 説明 |
|:--|:--|
|Enumerator#Lazy|map や select などのメソッドの遅延評価版を提供するためのクラス|
|Enumerator#chunk|enum.chunk {｜item｜ block }<br />要素を前から順にブロックで評価し、その結果によって 要素をチャンクに分けた(グループ化した)要素を持つ Enumerator を返す|

```ruby:Lazyが有効な例

# Lazyを使うとすぐに実行結果が返ってくるので、Lazyがとても有効
p (1..Float::INFINITY).each.lazy.select(&:even?).first(5)
#=> [2, 4, 6, 8, 10]

# Lazyを使わないと、Rangeの全ての奇数をselectしてから、first(5)が実行されるので
# 実行結果が返ってこない。
p (1..Float::INFINITY).each.select(&:even?).first(5)
```

- chunkは、`行単位データの並びに意味があるときに、その順位を崩さずに部分まとめ（チャンク）を生成するときはchunkを使うってことらしい
    - [行指向ドキュメント処理で活躍するRubyのだんご化３兄弟といったら... ](http://melborne.github.io/2014/04/08/3-chunk-brothers-in-ruby/)が参考になった

#### 参考

- https://docs.ruby-lang.org/ja/2.1.0/class/Enumerator=3a=3aLazy.html
- https://docs.ruby-lang.org/ja/2.1.0/method/Enumerable/i/chunk.html
- [RubyのEnumeratorとEnumerator::怠惰(Lazy)の使い所とベンチマーク](http://tango-ruby.hatenablog.com/entry/2016/09/02/192053)
- [行指向ドキュメント処理で活躍するRubyのだんご化３兄弟といったら... ](http://melborne.github.io/2014/04/08/3-chunk-brothers-in-ruby/)

### 問題12. lambdaとprocの違い

```ruby:lambda
# 次のコードを実行するとどうなりますか
# 答え：エラー!!

local = 0

p1 = lambda { |arg1, arg2|
  arg1, arg2 = arg1.to_i, arg2.to_i
  local += [arg1, arg2].max
}

p1.call("1", "2")
p1.call("7", "5")
p1.call("9")

p local
#=> wrong number of arguments (given 1, expected 2) (ArgumentError)
```

```ruby:lambda
# 次のコードを実行するとどうなりますか

local = 0

p1 = proc { |arg1, arg2|
  arg1, arg2 = arg1.to_i, arg2.to_i
  local += [arg1, arg2].max
}

p1.call("1", "2")
p1.call("7", "5")
p1.call("9")

p local
#=> 18
```

#### 解説

| メソッド | 説明 |
|:--|:--|
|Kernel#lambda|callする際の引数は省略できない。省略するとArgumentErrorエラーが発生する|
|class#proc|callする際の引数を省略できる。省略するとnilが入力される|

#### 参考

- [Rubyの ブロック、Proc.new、lambdaの違い](https://qiita.com/ryo-ma/items/24c46592b45775e8644d)


### 問題13. instance_evalの使い方

```ruby
# 次のプログラムを実行するとどうなりますか

m = Module.new

CONST = "Constant in Toplevel".freeze

_proc = Proc.new do
  CONST = "Constant in Proc".freeze
end

m.instance_eval(<<-EOS)
  CONST = "Constant in Module instance".freeze

  def const
    CONST
  end
EOS

m.module_eval(&_proc)

p m.const
#=> "Constant in Module instance"
```

#### 解説

- 特異メソッドを定義するのであれば、instance_evalを使う
- インスタンスメソッドを定義するのであれば、module_evalを使う

| メソッド | 説明 |
|:--|:--|
|Object#instance_eval|obj.instance_eval { block }<br />渡されたブロックをレシーバのインスタンスの元で実行します。ブロックの戻り値がメソッドの戻り値になります。|
|Module#module_eval|mod.module_eval { block }<br/>ブロックをクラス定義やモジュール定義の中のコードであるように実行します。ブロックの戻り値がメソッドの戻り値になります。|

```ruby:module_evalを使った書き方

CONST = 'Constant in Toplevel'.freeze

_proc = proc do
  CONST = 'Constant in Proc'.freeze
end

Module.module_eval(<<-EOS)
  CONST = "Constant in Module instance".freeze

  def const
    CONST
  end
EOS

Module.module_eval(&_proc)
m = Module.new
p m.const
#=> "Constant in Module instance"
```

### 問題14. moduleとself、parentが混じった場合のインスタンスメソッドの探索順序

```ruby
module M
  def foo
    super
    puts "M#foo"
  end
end

class C2
  def foo
    puts "C2#foo"
  end
end

class C < C2
  def foo
    super
    puts "C#foo"
  end
  include M
end

p C.ancestors
#=> [C, M, C2, Object, Kernel, BasicObject]

C.new.foo
#=> C2#foo
#=> M#foo
#=> C#foo

```

#### 解説

- ancestorsメソッドを使って優先順位を確認する
    - `[C, M, C2, Object, Kernel, BasicObject]`
- self、module、parentの順番であることがわかる
- superは、親が呼ばれるので、Cのsuperは、M

#### 参考

- https://docs.ruby-lang.org/ja/2.1.0/method/Module/i/ancestors.html

### 問題15. クラス変数のスコープ

```ruby
# 次のプログラムを実行するとどうなりますか

module M
  @@val = 10

  class Parent
    @@val = 1
  end

  class Child < Parent
    @@val += 2
  end

  if Child < Parent
    @@val += 3
  else
    @@val += 4
  end
end

p M::Child.class_variable_get(:@@val)
# 3
```

#### 解説

- クラス変数のスコープはsuperクラスを含む。しかし、Moduleは上位スコープ（外側）なので、スコープ外となる
    - `Mの@@val`は上位スコープなのでスコープ外となる
    - `Parentの@@val`と`Childの@@val`は同じスコープとなる
- クラス変数は、その場所を囲むもっとも内側の(特異クラスでない) class 式 または module 式のボディをスコープとして持つ
    - https://docs.ruby-lang.org/ja/2.1.0/doc/spec=2fvariables.html#class_var_scope
    
```ruby
module M
  @@val = 10 # M

  class Parent
    @@val = 1 # M::Child
  end

  class Child < Parent
    @@val += 2 # M::Child
  end

  if Child < Parent
    @@val += 3  # M
  else
    @@val += 4  # M
  end
end

p M::Child.class_variable_get(:@@val)
#=> 3

p M.class_variable_get(:@@val)
#=> 13
```

#### 参考

- [クラス変数のスコープ](https://docs.ruby-lang.org/ja/2.1.0/doc/spec=2fvariables.html#class_var_scope)

### 問題16. prependの探索順

```ruby:prependの探索順
# 次のコードを実行するとどうなりますか

module M1
end

module M2
end

class C
  prepend M1, M2
end

p C.ancestors
#=> [M1, M2, C, Object, Kernel, BasicObject]
```

#### 解説

- prependはモジュールのメソッドを`特異メソッドとして追加`する
- 特異メソッドとはクラスではなくある特定のオブジェクトに固有のメソッドのこと
    - https://docs.ruby-lang.org/ja/2.1.0/doc/spec=2fdef.html#singleton_method
- objというオブジェクトがあったとして、obj.mというメソッド呼び出しがあった場合、Rubyは以下の優先順位でメソッドの探索を行う。＊[Rubyの各メソッドと優先順位](https://qiita.com/tatane616/items/1e88929227529bf67414)の記事を参照
    - objの特異メソッド
    - objのクラスのmというインスタンスメソッド
    - objのクラスがインクルードしているモジュールのインスタンスメソッド(includeされた順とは逆順でモジュールを検索する)
    - スーパークラスのインスタンスメソッド
    - スーパークラスがインクルードしているモジュールのインスタンスメソッド
    - 見つからない場合、method_missingメソッドが呼び出される
- 従って、prependで拡張したメソッドは、selfより探索順が先になる

#### 参考

- [Rubyの各メソッドと優先順位](https://qiita.com/tatane616/items/1e88929227529bf67414)
- https://docs.ruby-lang.org/ja/2.1.0/doc/spec=2fdef.html#singleton_method


### 問題17. Comparable#betweenの使い方

```ruby
# 次のコードを実行するとどうなりますか

class Company
  include Comparable
  attr_reader :id
  attr_accessor :name
  def initialize(id, name)
    @id = id
    @name = name
  end

  def to_s
    "#{id}:#{name}"
  end
  def <=> other
    self.id <=> other.id
  end
end

c1 = Company.new(3, 'Liberyfish')
c2 = Company.new(2, 'Freefish')
c3 = Company.new(1, 'Freedomfish')

p c1.between?(c2, c3)
#=> false
p c2.between?(c3, c1)
#=> true
```

#### 解説

- `between?`で値を比較するためには、`Comparable`をincludeする必要がある
    - なぜなら、between?は、Comparableのメソッドであるため
    - https://docs.ruby-lang.org/ja/2.1.0/method/Comparable/i/between=3f.html
- 自作のクラスで比較する場合は、`def <=> other`で並び順を指定する必要がある
    - 数値クラス、String、Timeなどの標準クラスであれば、comparableが実装済なので必要なし

| メソッド | 説明 |
|:--|:--|
|Comparable#between?|obj.between?(min, max)<br />レシーバobjの値が引数minとmaxの間に含まれればtrue、そうでなければfalseを返します。objがminまたはmaxと等しいときはtrueを返します。|

#### 参考

- https://docs.ruby-lang.org/ja/2.1.0/method/Comparable/i/between=3f.html
- https://docs.ruby-lang.org/ja/2.1.0/class/Comparable.html
- [[ruby]値が範囲内に存在するかbetweenでチェック（数、文字、日付）](https://qiita.com/namitop/items/967b8dbb078837acfee9)

### 問題18. 定数の探索順序

```ruby
# 次のプログラムを実行するとどうなりますか
# 答え： "011"

module M1
  class C1
    CONST = '001'.freeze
  end

  class C2 < C1
    CONST = '010'.freeze

    module M2
      CONST = '011'.freeze

      class Ca
        CONST = '100'.freeze
      end

      class Cb < Ca
        p CONST
        #=> "011"
        p Module.nesting
        #=> [M1::C2::M2::Cb, M1::C2::M2, M1::C2, M1]
      end
    end
  end
end
```

```ruby
# 次のプログラムを実行するとどうなりますか
# 答え： "Hello, world"

class C
  CONST = 'Good, night'.freeze
end

module M
  CONST = 'Good, evening'.freeze
end

module M
  class C
    CONST = 'Hello, world'.freeze
  end
end

module M
  class C
    p CONST
    #=> "Hello, world"
    p Module.nesting
    #=> [M::C, M]
  end
end
```

#### 解説

- 親クラスとネストのクラスで同名の定数が定義されているとネストの定数の方を先に参照する
    - `M1::C2::M2::Cb`とネストされているので、cb -> M2 -> C2 -> M1と定数が探索される
- M1,C2,M2,Cbに定数が無かったら、superクラスの`Ca`が探索される
- この問題の場合は、`M2`に定数があるので、`'011'`と出力される。

```ruby:ancestors
p M1::C2::M2::Cb.ancestors
#=> [M1::C2::M2::Cb, M1::C2::M2::Ca, Object, Kernel, BasicObject]
```
#### 参考

- [定数参照の優先順位](https://docs.ruby-lang.org/ja/2.1.0/doc/spec=2fvariables.html)


### 問題19. メソッド名と引数の間の空白の扱い

```
# 次のコードを実行するとどうなりますか
# 答え：256

def foo(n)
  n ** n
end

puts foo (2) * 2
#=> 256
```

#### 解説

- `foo (2) * 2`はメソッド名と引数の間に空白があるため、`foo((2) * 2)`が呼ばれたと解釈される。よって、4の4乗の256になる。
- 空白の有無でSyntaxErrorになるなど解釈が異なるので注意が必要
    - [Rubyでは、メソッド引数のカッコはメソッド名の直後に書くこと。空白を入れちゃだめ](http://yucatio.hatenablog.com/entry/2018/06/17/161726)という記事にも、これに書かれています。

#### 参考

- [Rubyでは、メソッド引数のカッコはメソッド名の直後に書くこと。空白を入れちゃだめ](http://yucatio.hatenablog.com/entry/2018/06/17/161726)

### 問題20. 自己代入演算子の変数

```ruby
# 次のコードを実行するとどうなりますか
# 答え：エラー

x = 0
def hoge
  (1...5).each do |_i|
    x += _i
　　　　　　　　#> `block in hoge': undefined method `+' for nil:NilClass (NoMethodError)
  end
  puts x
end
hoge
```

#### 解説

- そもそも、 メソッドの外と内では、変数名が同一でも、別変数として扱われる
    - `1行目のx`と`hogeメソッドのx`では別変数として扱われる
- 自己代入演算子では、左のオペランドを評価するため、変数が初期化されていないとnilでエラーが発生する
    - hogeメソッドの`x += _i`は、ブロック内で、変数定義しているので、nilとなって、NoMethodErrorが発生する

### 問題21. rescueの型を省略した場合に捕捉できるErrorと捕捉順序

```ruby
# 次のコードを実行するとどうなりますか
# 答え：'Error.\nEnsure'

begin
  puts 1 + '2'
  # TypeErrorが発生！
rescue => ex
  puts 'Error.'
rescue TypeError => ex
  puts 'Type Error.'
ensure
  puts 'Ensure'
end
```

#### 解説

- rescueで、型を省略すると、StandardErrorとその子クラスの例外を補足する
    - `puts 1 + '2'`で発生するTypeErrorは、StandardErrorの子クラスなので、型を省略したrescueにて処理される
    - TypeErrorを捕捉する場合は、rescure TypeErrorを先に宣言すると想定通りの動作となる

```ruby:TypeErrorを捕捉する場合
begin:
  puts 1 + '2'
rescue TypeError => ex
  puts 'Type Error.'
rescue => ex
  puts 'Error.'
ensure
  puts 'Ensure'
end
```

- Rubyで自前の例外クラスを作るときExceptionではなくStandardErrorを継承する
    - http://d.hatena.ne.jp/yarb/20121005/p1
    - 理由としては、StandardErrorはアプリケーションErrorで例外処理する。システム障害系は、復旧できない例外として強制終了するという使い分けができると思った
    - そういう訳で、rescueで、型を省略すると、StandardErrorが捕捉できるのがRubyの思想だと理解した

#### 参考

- [Rubyで自前の例外クラスを作るときExceptionではなくStandardErrorを継承する理由](http://d.hatena.ne.jp/yarb/20121005/p1)
- [rubyの例外についてまとめてみた](https://qiita.com/kasei-san/items/75ad2bb384fdb7e05941)

### 問題22. オーバーライドした場合のsuperの実行順序

```ruby
# 次のコードを実行するとどうなりますか
# 答え：'Hoge'

class Hoge
  attr_reader :message
  def initialize
    @message = 'Hoge'
  end
end

class Piyo < Hoge
  def initialize
    @message = 'Piyo'
    super
  end
end

puts Piyo.new.message
#=> Hoge
```

#### 解説

- オーバーライドした場合、superはコードの順序通りに実行される
    - Piyo#initializeで、`@message`に格納した後にsuperが呼ばれているので、`@message`に'hoge'が格納される
- 以下、superを上に持ってくると、'Piyo'が出力される

```ruby:superを上に定義した場合
class Hoge
  attr_reader :message
  def initialize
    @message = 'Hoge'
  end
end

class Piyo < Hoge
  def initialize
    super  # superを上に持ってきた
    @message = 'Piyo'
  end
end

puts Piyo.new.message
#=> Piyo
```

### 問題23. superの暗黙の引数

```ruby
# 次のコードを実行するとどうなりますか
# 答え：
#  Hello
#  Hoge
#  Piyo

class Hoge
  def say(message)
    puts message
  end
end

class Piyo < Hoge
  def say(message)
    super
    puts 'Piyo'
  end
end

p Piyo.new.say('Hello')
#=> Hello
#=> Hoge
#=> Piyo
```

```ruby
# 次のコードを実行するとどうなりますか
# 答え： エラー!!

class Hoge
  def say
    puts'Hoge'
  end
end

class Piyo < Hoge
  def say(message)
    super
    puts 'Piyo'
  end
end

p Piyo.new.say('Hello')
#=> `say': wrong number of arguments (given 1, expected 0) (ArgumentError)
```

#### 解説

- superを引数なしで呼び出すと、子クラスのメソッドの引数が自動的に親クラスのメソッドに渡される
    - [Ruby: `super`キーワードの4つの側面（翻訳）](https://techracho.bpsinc.jp/hachi8833/2018_06_20/57090)
    - しかし、親クラスのメソッドに引数が定義されていない場合、引数が暗黙で親クラスのメソッドに渡されてしまうので、ArgumentErrorが発生してしまう
- Rubyは、ancestorsチェインで、メソッドを探索し、見つかったメソッドに引数を渡すことがポイント
    - ancestorsチェインを調べるには、ancestorsメソッドを使うと調べることが可能
    - https://docs.ruby-lang.org/ja/2.1.0/method/Module/i/ancestors.html

#### 参考

- [Ruby: `super`キーワードの4つの側面（翻訳）](https://techracho.bpsinc.jp/hachi8833/2018_06_20/57090)
- https://docs.ruby-lang.org/ja/2.1.0/method/Module/i/ancestors.html

### 問題24. ローカル変数とブロック変数

```ruby
# 次のコードを実行するとどうなりますか
# 答え： エラー!!

str = '0'
%w[1 2 3].each do |str|
  print str + ' '
end
puts str
#=> 1 2 3 0
```

#### 解説

- ローカル変数とブロック変数では、スコープが異なるので別変数として扱われる

### 問題25. trueとfalseの条件

```ruby
# 次のコードを実行するとどうなりますか
# 答え: 
# is false.
#false is false.
#  is true.
#0 is true.
#1 is true.
#0.0 is true.

[nil, false, ' ', 0, 1, 0.0].each do |val|
  if val
    puts "#{val} is true."
  else
    puts "#{val} is false."
  end
end
```

#### 解説

- false は nil オブジェクトとともに偽を表し、 その他の全てのオブジェクトは真となる
    - https://docs.ruby-lang.org/ja/2.1.0/class/TrueClass.html
- `0`は他言語(例えばC言語)ではFalseと判断される。しかし、Rubyの場合、`0`はTrueと判断される点に注意
    - 詳しい話は「[Rubyのtrueとfalseの話](https://qiita.com/rotelstift/items/70461f35c0d691e7b246)」が参考になりました。

#### 参考

- https://docs.ruby-lang.org/ja/2.1.0/class/TrueClass.html
- [Rubyのtrueとfalseの話](https://qiita.com/rotelstift/items/70461f35c0d691e7b246)


### 問題25. 変数の初期化

```ruby
# 次のコードを実行するとどうなりますか

a = 1, 2, 3
p a
#=> [1, 2, 3]

a, b = 1, 2, 3
p a
#=> 1
p b
#=> 2

a, *b = 1, 2, 3
p a
#=> 1
p b
#=> [2, 3]

*a, b = 1, 2, 3
p a
#=> [1, 2]
p b
#=> 3
```


### 問題26. String#deleteの使い方

```ruby
# 次のコードを実行するとどうなりますか

"hello".delete "l","lo"        
#=> "heo"

"hello".delete "lo"            
#=> "he"

"hello".delete "aeiou", "^e"   
#=> "hell"

"hello".delete "ej-m"          
#=> "ho"
```

#### 解説

- 指定した文字を取り除いた文字列を生成して返すメソッド
- 「-」は文字列の両端にない場合にだけ範囲指定の意味になる。 「^」も文字列の先頭にあるときだけ効果を発揮します。 また、「-」「^」「\」はバックスラッシュ (「\」) によってエスケープできる
- 引数を複数指定した場合は、 すべての引数にマッチする文字だけが削除される

#### 参考

- https://docs.ruby-lang.org/ja/2.1.0/method/String/i/delete.html