#背景
Ruby技術者認定試験に向け、Ruby技術者認定試験公式ガイドを使ってお勉強をせっせとしていたところ、ある問題で「むむっ」と詰まってしまい、リファレンスをめちゃくちゃ調べた(当社比)のでメモ的な感じで書いていく。
プログラムの実行環境はRuby2.4.0/Ubuntu 16.04 LTSです。
#ハマった問題
自分がハマったのはこんな問題でした。著作権の都合上、問題文を変えてます。ザックリと言うと、
- こんな出力になるのはどんな時か。当てはまるコードを入れてね。
hs = {:Tom => 2000, :John => 3000}
#ここにコードを書く
p hs
#出力
# {}
選択肢は省きますが、この場合、正解になりうるメソッドは
- delete_if(ブロックを評価して真の場合に要素を削除)
- clear(ハッシュの全内容を削除)
です。ここまでは説明を読んで「ほー」となったのですが、問題はここからです。
- reject(ブロックを評価した値が真の場合、値の要素を削除)は、非破壊的メソッドなのでhs自体に影響を及ぼさないため不正解 ←??
- reject!(rejectの破壊的メソッド)にすれば正解。
どういうことなんだろう。rejectは上辺だけ変えて本質は変えてない?(哲学)なんのこっちゃとなったのでRubyリファレンスマニュアルのお力を借りてみた。リファレンスマニュアルによるとrejectの仕様は次のような記述になっています。
self を複製して、ブロックを評価した値が真になる要素を削除したハッシュを返します。
#ということは
つまり、
hs = {:Tom => 2000, :John => 3000}
p hs.reject { |value, key| true} #rejectしたのを見る
p hs #もっかい見る
とすると、出力は
{}
{:Tom => 2000, :John => 3000}
となります。破壊的メソッドと非破壊的メソッドの本質なのですが、
非破壊的メソッドでは、実行した段階で参照したオブジェクトとは違う新しいオブジェクトが生成され、その中に変更後の値が入ります。つまり、main.rbの2行目で表示しているのは、hsに変更を施したものを入れた別のハッシュです。リファレンスに基づくと「self(自分自身)を複製したハッシュを、値を変更した後のハッシュに置き換える」ということです。当然、3行目で参照しているのはhsそのものなので、変更されてないありのままのハッシュが表示されるわけです。対して、破壊的メソッドでは
hs = {:Tom => 2000, :John => 3000}
p hs.reject! { |value, key| true} #reject!
p hs #もっかい見る
とすると、出力は
{}
{}
になります。破壊的メソッドはオブジェクトそのものを変えてしまうので名前の通り
「破壊的」ですね。めっちゃ基本の復習でした。
こんな感じでdelete_if
やclear
のほかにreject!
がハッシュの要素を削除するメソッドとして使えることがわかりました。コードに何気なく書いてありますがdelete_if
やreject!
で全要素を削除するには評価条件を無理やり全部trueにしておきます。
何か間違いや誤字脱字などがありましたら指摘をよろしくお願いします。初めての投稿(カミングアウト)なので至らない点が無数あると思います。
ここまで読んでいただきありがとうございました。
#参考・引用