ham-capのブログ

プログラミング学習の記録

【Ruby】おとなしそうな顔して破壊的メソッドな奴ら(Array#shift・Array#unshift)

初めて使ったメソッドが思いがけず破壊的メソッドだったよって話。

目次

破壊的メソッドでも末尾に必ず「!」がつくとは限らない

Rubyの各クラスで使用できるインスタンスメソッドには破壊的メソッドと非破壊的メソッドというのがあります。

それぞれ大雑把にいうと、

  • 破壊的メソッド:レシーバとなるオブジェクトの値そのものに対して処理を実行する
  • 非破壊的メソッド:レシーバとなるオブジェクトに何らかの処理を行なった結果を新しいオブジェクトとして返す(レシーバのオブジェクトの値は変わらない)

という感じです。

で、破壊的メソッドの場合は基本的にメソッド名の後ろに!がつきます。 例えばgsubは非破壊的メソッドで、gsub!は破壊的メソッドです。

ただし、これはあくまでも原則であって全ての破壊的メソッドに!がついているわけではありません。 というか、!のついていないものも普通にあります。

その中で今回はArrayクラスのshiftunshiftについて、自分が初めて使った時に非破壊的メソッドだと思って使って騙されたので備忘録的な記事です。

shift

まずはshift。以下はるりまの定義です。

配列の先頭の要素を取り除いてそれを返します。引数を指定した場合はその個数だけ取り除き、それを配列で返します。

空配列の場合、n が指定されていない場合は nil を、指定されている場合は空配列を返します。また、n が自身の要素数より少ない場合はその要素数の配列を返します。どちらの場合も自身は空配列となります。

返す値と副作用の両方を利用して、個数を指定して配列を 2 分する簡単な方法として使えます。

class Array (Ruby 3.0 リファレンスマニュアル)

挙動としてはこんな感じ。

a = [0, 1, 2, 3, 4]
first_element = a.shift

p a
=> [1, 2, 3, 4]   

p first_element
=> 0

shiftのレシーバとなったオブジェクトaの値自体が変更されているため、first_element = a.shiftより後で呼び出すと先頭の0がなくなっています。

unshift

次がunshift

指定された obj を引数の最後から順番に配列の先頭に挿入します。引数を指定しなければ何もしません。

class Array (Ruby 3.0 リファレンスマニュアル)

こちらはshiftの逆で先頭に要素を追加します。

a = [0, 1, 2, 3, 4]
a.unshift(5)

p a
=> [5, 0, 1, 2, 3, 4]

こちらもshiftと同様、aの値自体が変更されているのがわかります。

まとめ

まぁなんてことはない話なんですが、なんとなく無意識で!がついていなければ非破壊的なメソッドと思い込んでしまっていることがあるので、意図しない挙動になってしまい、思わぬところでエラーを出さないとも限りません。

また、今回紹介したshiftunshift!がつかない破壊的メソッドのうちのほんの一部です。

他にも各クラスに結構ありますので、初めて使うメソッドはちゃんと動きを確認してから使うよう心がけたいですね。