差をつけるRuby

## Rubyを楽しむために

Rubyは、奥が深い言語です。**覚えゲー**ともいいます。
Rubyは、いろんな機能を覚えるとキレイに書けて気持ちがいいです。
キレイに書けるとRubyが楽しくなります。

Rubyを触り始めて一通りの処理が書けるようになった人が、次に書けるようになるとプロっぽいRubyの機能や文法を紹介します。

注意：本記事ではRubyのバージョンは2.6以上を仮定します

## `map`の小技 ①

```ruby
[1, -1, 2, -3].map { |e| e.abs } # => [1,1,2,3]
[1, -1, 2, -3].map(&:abs) # => [1,1,2,3]
```

`map`メソッドにはブロックを渡して処理をするのが普通ですが、`map(&:method_name)`と書くこともできます。
ブロックを渡した場合と意味は同じですが、より短く簡潔に書けます。

## `map`の小技 ②

```ruby
h = { a: 1, b: 2, c: 3 }
[:a, :c, :b, :x].map { |k| h[k] } # => [1, 3, 2, nil]
[:a, :c, :b, :x].map(&h) # => [1, 3, 2, nil]
```

Arrayの中身をHashを使って変換したいことがあると思います。そういうときは`map(&:hash)`と書けます(`&`によってHashをブロックに変換しています)。
ブロックを使った場合と意味は同じですが、より短く簡潔に書けます。

## `map`の小技 ③

(Thanks @pink_bangbi san)

```ruby
[:a, :c, :b, :x].map { |e| p e }
[:a, :c, :b, :x].map(&method(:p))
```

上の2つの行は同じ意味になります。

2行目では、 `method(:p)` によって `p` メソッドをProcオブジェクトに変換し、`&`
によってProcオブジェクトを「展開」して、`map`にブロックを渡しています。

## Safe navigation operator

```ruby
arr = []
arr.first.hoge # => NoMethodError
arr.first&.hoge # => nil
```

上の例では空配列の`first`を呼び出していて、この値は`nil`になります。`nil`に`hoge`メソッドは生えていないので、2行目はNoMethodErrorを吐きます。
一方で3行目は`&.`を使ってメソッドを呼び出しているので、エラーを吐かずに単に`nil`を返します。

## `||=`

`||=`を使うと「左辺が偽(`nil`か`false`)の場合だけ代入する」ということができます。

```ruby
a = 100
a ||= '代入されない！！！'
a # => 100

b = nil
b ||= '代入される！！！'
b # => '代入される！！！'
```

これを使うと、「最初に呼び出されたときに初期化する」みたいなことができます。

```ruby
class Hoge
  def add_history(value)
    @c ||= [] # add_historyが最初に呼ばれたときだけ実行される
    @c.push(value)
  end
end
```

## Hashのデフォルト値

`Hash.new`にブロックを渡して初期化すると、登録されていないキーが参照された場合にそのブロックの返り値が返却されます。
つまり、ブロックの返り値がHashのデフォルト値になります。

```ruby
h = Hash.new { 100 }
h[:key] = h[:key] + 1
h[:key] # => 101
```

`Hash.new`に渡すブロックを工夫すると、さらにキレイに書けます（ブロックの
第一引数がハッシュ自身、第二引数がハッシュのキーになることと、代入文の返り値は右辺の値であることを利用している）。

```ruby
h = Hash.new { |hash, key| hash[key] = 100 }
h[:key] += 1
h[:key] # => 101
```

これを使うと、計算結果のキャッシュができます。

```ruby
memo = Hash.new { |hash, arg| hash[arg] = some_func(arg) }
memo['somearg'] # => some_func('somearg')が走る
memo['somearg'] # => キャッシュが使われる
```

[メモ化](https://ja.wikipedia.org/wiki/%E3%83%A1%E3%83%A2%E5%8C%96)もキレイに書けます。
フィボナッチ数列をメモ化を使って実装する例：

```ruby
fib = Hash.new do |hash, n|
  if n == 0
    hash[n] = 0
  elsif n == 1
    hash[n] = 1
  else
    hash[n] = hash[n-1] + hash[n-2]
  end
end

fib[100] # => 354224848179261915075
```

## `tap`

`tap`はすべてのオブジェクトに生えているメソッドです。ブロックを渡すことができ、その返り値は呼び出し元のオブジェクトになります。
メソッドチェインの途中の状態を見たいときとかに使えます。

説明するのが難しいので例を見てくれ：

```ruby
[-10, 10, -20]
  .map(&:abs).map(&:to_s)
  .tap { |inner_arr| p inner_arr } # => ["10", "10", "20"]
  .map(&:chars).map(&:first)
```

`method(:p)`を使って以下のようにも書けます(Thanks @pink_bangbi san)：

```ruby
[-10, 10, -20]
  .map(&:abs).map(&:to_s)
  .tap(&method(:p)) # => ["10", "10", "20"]
  .map(&:chars).map(&:first)
```

## Struct

いわゆる構造体です。クラスを作るほどではないが、Hashよりは "ちゃんとしたい" ときに使います。

```ruby
Point = Struct.new(:x, :y)
point = Point.new(100, 200)
point.x # => 100
point.y # => 200
point.x = 500
point.x # => 500
```

構造体にはメソッドが生やせます (Thanks @horyu san)。

```ruby
Point = Struct.new(:x, :y) do
  def fuga
    "somestring"
  end
end

Point.new(100,200).fuga # => "somestring"
```

構造体はクラスなので、継承できます。

```ruby
class Point < Struct.new(:x, :y)
  def add_x(dx)
    self.x += dx
  end
end

point = Point.new(100,200)
point.add_x(5)
point.x # => 105
```

## モンキーパッチ

Rubyでは、既存のクラスに後からメソッドを生やせます。もちろん組み込みクラスにも生やせます。これを[モンキーパッチ](https://ja.wikipedia.org/wiki/%E3%83%A2%E3%83%B3%E3%82%AD%E3%83%BC%E3%83%91%E3%83%83%E3%83%81)といいます。

例えば`String`クラスに新しく`hoge`メソッドを生やしてみます：

```ruby
some_string = 'aaaaa'

class String
  def hoge
    'This is hoge!!!'
  end
end

some_string.hoge # => 'This is hoge!!!'
```

このようにモンキーパッチは、存在するすべてのインスタンスにメソッドを生やします。

モンキーパッチを乱用すると最悪なことになるので、慎重に使いましょう(cf. [refine](https://docs.ruby-lang.org/ja/2.6.0/class/Module.html#I_REFINE))。

## `Proc`の合成

`Proc`は`>>`によって合成できます。

```ruby
f1 = proc { |e| e * 10 }
f2 = proc { |e| e + 1 }
composed = f1 >> f2

composed.call(2) # => 21
[1,2,3].map(&composed) # => [11, 21, 31]
```