[Ruby入門] 08. メソッドを定義する

&gt;&gt; 連載の目次は [こちら！](http://qiita.com/prgseek/items/237d02be844534063f1d)

メソッドの話はクラスの回に含めようと思ったが、けっこう大きなトピックになったので章立てすることにした。

### ■ 概要

#### クラスの外（トップレベル）について
+ Rubyは完全にオブジェクト指向だが、クラスの外（トップレベル）にメソッドを定義して、普通の関数っぽく使える。
+ トップレベルに定義したメソッドは、main という名の Objectクラスインスタンス の private メソッドとして定義される。
+ トップレベルでの self は main を指している

```ruby
p self           # main
puts self.class  # Object
```

#### メソッドに関する共通事項
+ 引数の型が動的なので、オーバーロードはできない
+ メソッド呼び出し側の引数の()は省略できるが、通常は記述する。attr_accessor のように特殊な意味を持つメソッドでは省略する慣習がある。
+ メソッド名や変数名はスネークケースが一般的

~~・言うまでもないが、全てはオブジェクトなので引数は参照渡しになっている~~
<font color="Red">　　↑ 間違いです！！</font>
tadsan 様よりご指摘頂きました。（ ありがとうございます m(_ _)m ）
勘違いしやすいので取り消し線で残しておきます。
詳しくは後で整理しますが、ピンと来ない方は↓こちらを参照してください。
[値渡しと参照渡しの違いを理解する](http://magazine.rubyist.net/?0032-CallByValueAndCallByReference)


### ■ メソッドの定義の基本

#### ・いちばん単純なメソッド
```ruby
def hanami
  puts "満開！"
end
hanami
```

#### ・引数あり

デフォルト値を指定可能

```ruby
def hanami_with_cat_and_food(cat, food = "鰹節")
  puts "%sが%sを食べる。桜に興味なし！" % [cat, food];
end
hanami_with_cat_and_food("chee")
hanami_with_cat_and_food("miro", "お刺身")
```

#### ・戻り値あり

returnは省略可能で、その場合はメソッド内の処理で最後に得られた値が戻り値になる。

```ruby
def wants_list(genre)
  list = {music:%w[player, speaker], fashion:%w[T-shirt, shoes], etc:%w[watch, wallet]}
  return list[genre] || {}
end
p wants_list(:fashion)
```

#### ・複数の戻り値を返す
```ruby
def lunch_and_drink
  return %w[カレー パスタ うなぎ].sample, %w[コーヒー 紅茶 黒烏龍茶].sample
end
f, d = lunch_and_drink  # 個別に代入
arr  = lunch_and_drink  # 配列で受け取る
```


### ■ キーワード付き引数（引数にラベルを付ける）
#### ・基本的な使い方
```ruby
def sanpo(animal: "犬", food: "お弁当", to:)
  puts "%s持って%sまで%sとお散歩♪" % [food, to, animal]
end
sanpo(animal: "猫", food: "サンドウィッチ", to:"上野")
sanpo(to:"代々木", food: "ホットドッグ")
sanpo(to:"井の頭公園")
```
+ どの引数がなにを意味してるのかわかりやすい
+ 定義側は、ラベルと同名の変数として引数にアクセスする
+ 定義側は、デフォルト値をコロンの後ろに指定できる
+ 呼び出し側は、ラベル記述必須
+ 呼び出し側は、引数の順番を気にしなくて良い


#### ・ハッシュをキーワード引数として渡す
```ruby
def size(width:, height:)
  puts "#{width} x #{height}"
end
hash = {width:55, height:45}
size(hash)
```

#### ・想定外のキーワード引数をまとめてハッシュとして受け取る
```ruby
def go_shops(where, cafe:, **shops)
  p shops   # {:fashion=>"ユニクロ", :dinner=>"大戸屋"}
  puts "%sで%s行って%s行って%s行く！" % [where, shops[:fashion], cafe, shops[:dinner]]
end
go_shops("地元", cafe:"ドトール", fashion:"ユニクロ", dinner:"大戸屋")
```


### ■ メソッドに処理（ブロック）を渡す

#### ・ブロックだけ渡す
```ruby
def okini_mesumama
    puts "はいはい、仰せの通り #{yield} しますよ〜だ"
end
okini_mesumama {"お掃除"}

# ちなみに、↑上記は、↓以下と同じ。（& + call は yield と同じ）
def okini_mesumama(&block)
    puts "はいはい、仰せの通り #{block.call} しますよ〜だ"
end
okini_mesumama {"お掃除"}
```

#### ・引数とブロックを渡す
```ruby
def okini_mesumama_nandodemo(cnt)
  (1..cnt).each do |i|
    puts "はいはい、#{yield} #{i}回目〜"
  end
end
okini_mesumama_nandodemo(3) {"お掃除"}
```

#### ・ブロックパラメータを持ったブロックを渡す
```ruby
def cleaning(cnt)
  item = "掃除機"
  (1..cnt).each do |i|
    puts "はいはい、#{yield(i, item)} お掃除します〜"
  end
end
cleaning(3) do |i, item|
  if (i == 3) then "#{item}でしっかり" else "さらっと" end
end
```

#### ・ブロックが渡されているかどうか判定する
```ruby
def work
  if block_given?
    puts "はいはい、仰せの通り #{yield} しますよ〜だ"
  else
    puts "楽しい仕事を優先するよ〜"
  end
end
work
work {"せっせと資料作成"}
```


### ■ メソッドをオブジェクトとして扱う

メソッド名をそのまま記述すると実行されてしまうので、Methodクラスのオブジェクトに変換すれば、他のメソッドの引数として渡したりといったやりとりが可能になる。

```ruby
def power(n); n * n end

# methodメソッドで、メソッドをオブジェクト化して...
pw = method(:power)

def exec_method_obj(arg, fun)
  puts fun.call(arg)
end

# そのオブジェクトをメソッドに渡す
exec_method_obj(3, pw)  # 9
```

※ちなみに、上記の「call」メソッドは、省略記法があるので注意。

```ruby
def power(n); n * n end
pw = method(:power)
puts pw[4]   # 16
puts pw.(5)  # 25
```

### ■ その他の引数の受け渡し方法いろいろ

#### ・キーワード引数風に指定してハッシュを渡す
```ruby
def test(args)
  p args
end
test(x:99, y:88)  # {:x=>99, :y=>88}
```

上記のメソッド定義はキーワード引数になってない。呼び出し側でキーワード引数風に指定して渡すと、メソッド側はハッシュとして受け取れる


#### ・呼び出し側で配列を展開して個別の引数として渡す
```ruby
def hikisu_ooiyo(num1, num2, num3, num4)
  puts "%d.%d.%d.%d" % [num1, num2, num3, num4]
end
arr = [192, 168, 1, 12]
hikisu_ooiyo(*arr)
```


#### ・可変数な引数をまとめて配列として受け取る
```ruby
def go_cafes(where, *shops)
  p shops   # ["スタバ", "タリーズ", "ミスド"]
  puts "%sの%sをはしごする！" % [where, shops.join('と')]
end
go_cafes("駅前", "スタバ", "タリーズ", "ミスド")
```


#### ・可変数な引数をまとめて配列とハッシュとして受け取る

ここまでやると複雑になっちゃうから多分やらないだろうな〜
でもこんなこともできるってことでいちおう整理しておく。

```ruby
def hukuzatsu_sugiru(arg, *arr, **hash)
  p arg, arr, hash
end

hukuzatsu_sugiru(1, 2, 3, 4, 5)
  # 1
  # [2, 3, 4, 5]
  # {}
hukuzatsu_sugiru(1, 2, 3, name:"miro", age:9)
  # 1
  # [2, 3]
  # {:name=>"miro", :age=>9}
hukuzatsu_sugiru(1, name:"miro", age:9)
  # 1
  # []
  # {:name=>"miro", :age=>9}
```