パーフェクトRuby勉強会 10/17 3章

# まえがき
+ 今日のために書いたコードは[こちら](https://github.com/kamatama41/perfect-ruby-study)
+ 4章は[こちら](http://qiita.com/kamatama_41/private/cfb5f4fed73bfa114a5c)

- - - 

# 3章 制御構造／メソッド／組み込み関数
## 3-1 演算子
### 3-1-2 メソッドとして定義されている演算子
<pre>
| ^ & <=> == === =~ > >= < <= << >> +@ -@
+ - * / % ** ~ [] []= ` ! != !~
</pre>

#### 演算子メソッドの独自定義


```ruby:「+」演算子のメソッドを引き算として定義してみる
class MyNumber
  def initialize(val)
    @val = val
  end

  def +(other)
    @val - other
  end
end

val = MyNumber.new(3)
puts val + 3  # 0
```

## 3-2 基本的な制御構造
### 3-2-1 条件分岐
case文はJavaより柔軟性が高い

```ruby:case文で正規表現を使ったパターン
stone = 'sardonyx'
case stone
  when /ruby/
    puts '7月'
  when /peridot|sardonyx/
    puts '8月'
  else
    puts 'よくわかりません'
end
```

```ruby:case文は最後に評価した値を返す
detected =
    case stone
      when /ruby/
        '7月'
      when /peridot|sardonyx/
        '8月'
      else
        'よくわかりません'
    end
puts detected
```

```ruby:case文はwhenの値をレシーバーとして「===」メソッドで比較している
class MyString
  def initialize(val)
    @val = val
  end

  def ===(other)
    @val.upcase == other.upcase
  end
end

a = MyString.new('A')
b = MyString.new('B')
c = MyString.new('C')
case 'a'
  when a
    puts 'Aでした'
  when b
    puts 'Bでした'
  when c
    puts 'Cでした'
  else
    puts 'どれでもありませんでした'
end
```

### 3-2-3 ジャンプ構文

#### next (Javaでいうcontinue)

```ruby:ループの中でnextを呼ぶと次の繰り返しに移る
languages = %w(Prel Python Ruby Smalltalk JavaScript)

languages.each do |language|
  puts language
  next unless language == 'Ruby'
  puts 'I found Ruby!!!'
end
```

#### redo

```ruby:ループの中でredoを呼ぶともう一度その処理をやり直す
languages = %w(Prel Python Ruby Smalltalk JavaScript)

languages.each do |language|
  puts language
  if language == 'Ruby'
    puts 'I found Ruby!!!'
    redo if rand(2) == 1  # 無限ループになるのをさける
  end
end
```

## 3-3 例外処理
### 3-3-2 例外を制御する
#### raiseで投げる、rescueで受け取る
```ruby:raiseで投げる、rescueで受け取る
begin
  raise 'error!'
rescue => e
  puts e.class # RuntimeError
  puts e.message
  puts e.backtrace
end
```

#### 投げる例外を指定する
```ruby:投げる例外を指定する
begin
  raise StandardError, 'error!'
rescue => e
  puts e.class
  puts e.message
  puts e.backtrace
end
```

#### 受け取る例外を指定する
```ruby:受け取る例外を指定する
5.times do |n|
  begin
      if n%3 == 0
        raise LoadError
      elsif n%3 == 1
        raise NameError
      else
        raise ArgumentError
      end
  rescue LoadError => e
    puts 'rescue LoadError'
  rescue ArgumentError, NameError => e
    puts "rescue #{e.class}"
  end
end
```
#### 後置rescue
```ruby:後置rescue
result = (1 / 0 rescue false)
puts result # false
```

#### ensure (Javaでいうfinally)
```ruby:例外発生に関わらず必ず実行したい処理はensure節に書く
begin
  file = File.open('test.txt')
  file.each_line do |l|
    puts l
  end
rescue => e
    puts e.class
    puts e.message
    puts e.backtrace
ensure
  file.close if file
end
```

#### else
```ruby:例外発生しなかった時だけ実行する処理はelse節に書く
begin
  file = File.open('test.txt')
  file.each_line do |l|
    puts l
  end
rescue => e
    puts e.class
    puts e.message
    puts e.backtrace
else
  puts 'ファイルが開けました!!'
ensure
  file.close if file
end
```

#### retry
```ruby:rescue句の中でretryを呼び出すとbegin節の処理をやり直す
begin
  failed ||= 0
  puts 'trying...'

  raise
rescue
  failed += 1
  retry if failed < 5
end
```

## 3-5 メソッド定義と呼び出し
### 3-5-1 メソッド呼び出しと括弧
#### メソッド呼び出しの括弧は省略可能
```ruby:メソッド呼び出しの括弧は省略可能
puts 'hello'
puts('hello')
```

#### 括弧を付けるかどうかの判断基準(の一例)
+ 戻り値を得るための式として記述する場合は括弧を付ける
+ 手続きを実行する場合は括弧を省略する

### 3-5-2 メソッド呼び出しとローカル変数
#### レシーバと括弧、引数のないメソッドと変数は変数が優先される
```ruby:レシーバと括弧、引数のないメソッドと変数は変数が優先される
sweet = 'honey'
def sweet
  'salt'
end

puts sweet  # honey
puts sweet()  # salt
```

### 3-5-3 メソッドと定数
#### 大文字で始まるメソッドをレシーバ・引数なしで呼び出そうとするとエラーになる
```ruby:大文字で始まるメソッドをレシーバ・引数なしで呼び出そうとするとエラーになる
def Hello
  puts 'Hello, My method!!'
end

begin
  Hello
rescue => e
  puts "#{e.class}, #{e.message}" # NameError, uninitialized constant Hello
end
```

### 3-5-5 省略可能な仮引数
#### メソッドの引数には任意の式を与えることができる
```ruby:メソッドの引数には任意の式を与えることができる
def greet(name, message = 'Hi')
  puts "#{message}, #{name}"
end

greet 'kamatama'
greet 'kamatama', 'Hello!!!'
```

### 3-5-6 可変長引数
#### 引数の先頭に*を付けることで、任意の数の引数を配列として受け取ることができる
```ruby:引数の先頭に*を付けることで、任意の数の引数を配列として受け取ることができる
def greet_messages(name, *messages)
  messages.each do |message|
    puts "#{message}, #{name}"
  end
end

greet_messages 'kamatama', 'Hi!!', 'Hello!!'
```

### 3-5-7 配列の展開
#### 実引数の頭に*を付けると配列を複数の引数として渡すことができる
```ruby:実引数の頭に*を付けると配列を複数の引数として渡すことができる
def greet_twice(name, first_message, second_message)
  puts "#{first_message}, #{name}"
  puts "#{second_message}, #{name}"
end

greetings = %w(Hello Hola)
greet_twice 'kamatama', *greetings

# 引数に指定しない数を渡すとArgumentErrorになる
begin
  greetings = %w(Hello Hola Hey)
  greet_twice 'kamatama', *greetings
rescue => e
  puts "#{e.class}, #{e.message}"
end
```

### 3-5-8 ブロック
### メソッドはブロック(一連の処理を含んだオブジェクト)を受け取ることができる。
```ruby:yieldをメソッドの中で呼び出すと、受け取ったブロックを実行する
def block_sample
  puts 'stand up'
  yield
  puts 'sit down'
end

block_sample do
  puts 'walk'
end
```

#### ブロックは最後に評価された式を返り値として返すことができる
```ruby:ブロックは最後に評価された式を返り値として返すことができる
def display_value
  puts yield
end

display_value do
  4423
end
```

#### 仮引数の先頭に&を付けることでメソッドにブロックを渡すことができる
yieldとの違いは仮引数はオブジェクト(Procクラス)なので、他のメソッドにさらに引数として渡すことができる

```ruby:仮引数の先頭に&を付けることでメソッドにブロックを渡すことができる
def block_sample_with_block_arg(&block)
  puts 'stand up'

  block.call if block

  puts 'sit down'
end

block_sample_with_block_arg do
  puts 'walk'
end
```

#### ブロックの仕様用途
##### 繰り返し処理
```ruby:配列の要素を全部大文字にした新しい配列を作る
people = %w(Alice Bob Carol)

puts people.map{|person| person.upcase}.inspect # ["ALICE", "BOB", "CAROL"]
```

##### 前後の処理を共通化できる場合
+ ファイルのオープン／クローズ
+ DBへの接続／切断
+ トランザクションの開始／終了
+ ロックと解放
+ ...など

- - - 
# 課題
## 課題1
### 1-1. 以下の仕様を満たすModクラスを作成し、Modクラスとwhen句を使ってFizzBuzzを完成させる
+ 整数のインスタンス変数(valとする)を保持する
+ 引数をvalで割った余りが0の場合のみtrueを返す「===」メソッドを持つ

## 課題2
### 2-1. 以下の仕様を満たすtimeメソッドを作り、実行してみる
+ ブロックを引数に取り、ブロックの処理時間(秒)を出力する
+ 例外が発生した場合も処理時間は計測できる