解答例 ─ Ruby初級者向けレッスン 44回

第56回 Ruby/Rails勉強会@関西 での Ruby初級者向けレッスン 44回 の解答例 *1

演習問題 1

0 から 9 までの数値をもつ配列 a がある。
a = (0..9).to_a
a # => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
  1. 各要素を順番に表示しよう。
  2. 各要素を 2倍した値を持つ配列を作ろう。
  3. 全要素の合計値を計算しよう。
  • スライドのおさらい。
  • 改めてやってみると、意外とできないかも知れないよ。
# -*- coding: utf-8; -*-

# 0 から 9 までの数値をもつ配列 a がある。
a = (0..9).to_a         # => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]


# 各要素を順番に表示しよう
a.each{|i| puts i}

# >> 0
# >> 1
# >> 2
# >> 3
# >> 4
# >> 5
# >> 6
# >> 7
# >> 8
# >> 9


# 各要素を 2倍した値を持つ配列を作ろう
a.map{|i| i * 2}  # => [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]


# 全要素の合計値を計算しよう
a.inject{|s, i| s + i}      # => 45

## マニアが書くと
a.inject(:+)                # => 45

演習問題 2

0 から 9 までの数値をもつ配列 a がある。
  • 奇数の要素だけを持つ配列を作ろう。
  • ただし odd? メソッドは使用禁止。
  • パズルだと思って、いろいろやってみよう。
# -*- coding: utf-8; -*-

# 0 から 9 までの数値をもつ配列 a がある。
a = (0..9).to_a       # => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]


# 奇数の要素だけを持つ配列を作ろう
a.select{|i| i.odd?}            # => [1, 3, 5, 7, 9]


# ただし odd? メソッドは使用禁止


# 2 で割った余りが 1 なら奇数
a.select{|i| i % 2 == 1}        # => [1, 3, 5, 7, 9]


# 偶数でなければ、それは奇数
a.select{|i| !i.even?}          # => [1, 3, 5, 7, 9]


# rubyist は true/false の否定を嫌う
# reject は式が真の要素を捨て去る
a.reject{|i| i.even?}           # => [1, 3, 5, 7, 9]

## マニアが書くと
a.reject(&:even?)               # => [1, 3, 5, 7, 9]

マニアのコードに出てきた & は、スライドに登場した、ブロックの受け渡しに使う & とは少し違う。*2

演習問題 3

Enumerable#map を自作してみよう。
module Enumerable
  def my_map
    ……
  end
end
ただしEnumerable#map とEnumerable#map! は使用禁止。

会場には「mapが使えないなら collectを使えばいいじゃない」と言う人がいた。
こういうことですね。わかります。*3

module Enumerable
  def my_map &block
    collect(&block)
  end
end

a = [*0..4]             # => [0, 1, 2, 3, 4]

a.map{|i| i * 2}        # => [0, 2, 4, 6, 8]
a.my_map{|i| i * 2}     # => [0, 2, 4, 6, 8]
a.map                   # => #<Enumerator: [0, 1, 2, 3, 4]:map>
a.my_map                # => #<Enumerator: [0, 1, 2, 3, 4]:collect>

惜しい。
多くのブロック付きメソッドはブロックを渡さないと Enumerator を返す。
my_map の方は # になっちゃってる。

# -*- coding: utf-8; -*-

# Enumerable#map を自作してみよう
# ただし Enumerable#map と Enumerable#map! は使用禁止

module Enumerable
  def my_map
    unless block_given?
      # ブロックがもらえなかったら Enumerator を返す
      to_enum __callee__
    else
      # 空の Array を用意し、
      inject([]) do |result, item|
        # ブロックの評価結果を追加する
        result << yield(item)
      end
    end
  end
end

# 試してみる
a = [*0..3]           # => [0, 1, 2, 3]

# map と my_map の比較
a.map{|i| i * 2}      # => [0, 2, 4, 6]
a.my_map{|i| i * 2}   # => [0, 2, 4, 6]

# Enumerator も比較
i = a.map             # => #<Enumerator: [0, 1, 2, 3]:map>
j = a.my_map          # => #<Enumerator: [0, 1, 2, 3]:my_map>

i.next                # => 0
i.next                # => 1
i.next                # => 2
i.next                # => 3

j.next                # => 0
j.next                # => 1
j.next                # => 2
j.next                # => 3
j.next                # => 

# ~> -:40:in `next': iteration reached an end (StopIteration)
# ~> 	from -:40:in `<main>'

詳しくは、時間がとれたら書きたい。*4

*1:There's More Than One Way To Do It. もっといいやり方があったら教えて。

*2:どう違うかはうまく説明できない。><

*3:これも反則です。

*4:コメントいただければ可能な限りお答えしたい。