Ruby で配列の値を swap してみよう

に公開

Ruby の配列に対して swap してみよう!
ってことでやってみました。
swap とは『値を入れ替える』という意味で今回は『任意の配列に対して2つの位置の要素を入れ替える』という形でやってみました。
NOTE: Array#swap を生やしているんですが単に複数回呼び出す場合に都合がいいだけなのでそこに関しては特に深い意味ないです。

一旦値を保存しておいて後から代入する

ary = %w(homu mami mado saya an)

のような配列がある場合に単に

ary[1] = ary[3]
ary[3] = ary[1]

のように代入してもうまく動きません。
なぜなら ary[1] = ary[3] の時点で ary[1] の値が書き換わっているので2つ目の ary[3] = ary[1] は意図する挙動にならないからですね。
なので以下のように変更前の値を保存しておいて、後から参照する必要があります。

class Array
  def swap(a, b)
    result = dup

    # result[a] の値を保存しておいて後から使用する
    tmp = result[a]
    result[a] = result[b]
    result[b] = tmp

    result
  end
end

ary = %w(homu mami mado saya an)

# 1番目と3番目の要素を入れ替える
pp ary.swap(3, 1)
# => ["homu", "saya", "mado", "mami", "an"]

# 0番目と4番目の要素を入れ替える
pp ary.swap(0, 4)
# => ["an", "mami", "mado", "saya", "homu"]

多重代入を利用する

Ruby では多重代入を利用すると1度の代入式で2つの変数に代入することができます。

a, b = 1, "homu"

pp a   # => b
pp b   # => "homu"

これを利用すると tmp のような一時変数を利用することなく swap を実現することができます。

class Array
  def swap(a, b)
    result = dup

    # 右辺で result[a] と result[b] の値を取得した後に逆に位置に対して値が代入される
    result[b], result[a] = result[a], result[b]
    result
  end
end

ary = %w(homu mami mado saya an)

# 1番目と3番目の要素を入れ替える
pp ary.swap(3, 1)
# => ["homu", "saya", "mado", "mami", "an"]

# 0番目と4番目の要素を入れ替える
pp ary.swap(0, 4)
# => ["an", "mami", "mado", "saya", "homu"]

代入式を使わないで swap する

上であげた例はいずれも代入式を使ったコードだったんですが代入式を使わないでできないかなーと考えたのが下のコードになります。

class Array
  def swap(a, b)
    a, b = b, a if a > b
    # 自身が [1, 2, 3, 4, 5, 6, 7] のときに a = 2, b = 5 だった場合は
    # [*[1, 2], 6, *[4, 5], 1, *[7]] と展開する
    [*self[...a], self[b], *self[a+1...b], self[a], *self[b+1..]]
  end
end

ary = %w(homu mami mado saya an)

pp ary.swap(3, 1)
# => ["homu", "saya", "mado", "mami", "an"]

pp ary.swap(0, 4)
# => ["an", "mami", "mado", "saya", "homu"]

要は入れ替える位置で配列を分割して、間に入れ替える値を挿入しつつ配列に置き換えている感じですね。

# 自身が [1, 2, 3, 4, 5, 6, 7] のときに a = 2, b = 5 だった場合は
ary = [1, 2, 3, 4, 5, 6, 7]
a = 2
b = 5

first = ary[...a]       # =< [1, 2]
middle = ary[a+1...b]   # => [4, 5]
last = ary[b+1..]       # => [7]

pp [*first, ary[b], *middle, ary[a], *last]
# => [1, 2, 6, 4, 5, 3, 7]

他にいい感じにの swap するコードとかかけるかなー。

GitHubで編集を提案

Discussion