はじめに:「これは何ですか?」
先日Qiitaを見てたら、@gam0022さんの「整数 n を なるべく等しい整数になるように m 等分する」という記事が目にとまりました。
Rubyプログラミングのお題としてはシンプルだけどなかなか興味深く、なおかつ@gam0022さんの書いたコードにまだ改善の余地がありそうだったので、個人的な趣味で(おせっかいで?)ちょっとリファクタリングしてみよう、と思いました。
また、単にリファクタリングした結果を見せるだけでなく、どういう意図でこのコードになったのか、という過程も見てもらった方が面白いなと思い、リファクタリングする一部始終を動画(スクリーンキャスト)で撮ってみました。
この記事はその動画の内容と、動画の中で登場したRubyMineの便利機能を紹介していきます。
お題の確認:「整数 n を なるべく等しい整数になるように m 等分する」とは?
「整数 n を なるべく等しい整数になるように m 等分する」というのはたとえば、10を2で割ると5と5に、10を3で割ると4と3と3に分割するという意味です。
元記事のサンプルコードではこんなふうになっています。
p divide_equally(10,2) # => [5, 5]
p divide_equally(10,3) # => [4, 3, 3]
p divide_equally(10,10) # => [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
p divide_equally(0,10) # => [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
コードの確認:「どんなふうにリファクタリングしたの?」
コードのBefore/Afterだけをお見せするとこんな感じです。
まず、元のコードはこのように書かれていました。
# 整数 n を なるべく等しい整数になるように m 等分する
# 返り値は整数の Array とする
def divide_equally(n, m)
result = Array.new(m, n/m)
rest = n%m
if rest == 0
# 端数が出ない場合
return result
else
rest.times do |i|
result[i] += 1
end
return result
end
end
p divide_equally(10,2) # => [5, 5]
p divide_equally(10,3) # => [4, 3, 3]
p divide_equally(10,10) # => [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
p divide_equally(0,10) # => [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
そして僕は上のコードを最終的にこんなふうにリファクタリングしてみました。
require 'minitest/autorun'
def divide_equally(dividend, divisor)
quotient, remainder = dividend.divmod(divisor)
divisor.times.map{|i| i < remainder ? quotient.succ : quotient }
end
class TestDivideEqually < MiniTest::Test
def test_1
assert_equal [5, 5], divide_equally(10, 2)
end
def test_2
assert_equal [4, 3, 3], divide_equally(10, 3)
end
def test_3
assert_equal [1, 1, 1, 1, 1, 1, 1, 1, 1, 1], divide_equally(10, 10)
end
def test_4
assert_equal [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], divide_equally(0, 10)
end
end
このあとに紹介する動画を見てもらうと、僕が何をどう考えてこんなコードにしたのかがわかると思います。
余談:初めてMiniTestでテストを書きました
ちなみに、今回初めてMiniTestを使ってテストを書いてみました。(普段はRSpecを使っています)
元のコードがとてもシンプルなので、わざわざRSpecをインストールしなくてもRuby標準のテスティングツールで十分だろう、と思ったのがその理由です。
動画の確認:「で、その動画はどこで見られるの?」
リファクタリングしている動画はYouTubeで公開しています。
スクリーンキャストで学ぶRubyリファクタリング: divide_equally編 - YouTube
長いので倍速再生推奨です
開発環境をセットアップするところや、リファクタリング中の試行錯誤も含んでいるので、動画の長さは33分39秒になっています。
結構長いので1.5倍~2.0倍ぐらいの速度で再生することをオススメします。
あと、解像度が低いとコードが見にくいかもしれないので、動画の画質をHDに上げて見てもらった方がいいと思います。
実際にリファクタリングを始めるのは17分20秒以降です
リファクタリングを開始するのは 17分20秒 ぐらいからです。
それまでは開発環境のセットアップ(テストを書くところまで)になっています。
おまけ:動画で登場するRubyMineのTips集
今回のリファクタリングではRubyMineを使いました。
動画中にいろいろとRubyMineならではの便利機能やTipsが登場しているので、ここからはそれを紹介していきます。
Cmd-,でPreferenceを開く(1:39)
Cmd-, はPreferenceを開くショートカットです。
さらにPreferenceの検索窓を使って目的の項目を絞り込むことができます。
Alt-F12でTerminalを開く (2:02)
RubyMineはIDEの中でTerminalを開くことができます。
ショートカットはAlt-F12です。
アプリケーションを切り替えなくてもTerminalが使えるのでちょっと便利です。
RubyMineの中からgit initする (2:40)
ファイルメニュー > VCS > Enable Version Control Integration で作業中のディレクトリをgit initできます。
RubyMineの中からgit commitする (3:00)
Cmd-9で変更されたファイルの一覧が表示されます。
まだgit管理下にないファイルはAdd VCS(Alt-Cmd-A)でgitに追加できます。
Cmd-KでCommit用のウインドウが立ち上がり、コメントと一緒にcommitできます。
requireの候補を自動的に表示、補完してくれる (4:50)
require 'minitest/'
のように入力すると、requireの候補が自動的に表示されます。
十字キー等で選択すると、そのまま入力が確定します。
メソッドの候補を自動的に表示、補完してくれる (5:40)
コードの自動補完はrequireのときだけでなく、いろんな場面で機能します。
ここでは assert
まで入力したときに、候補となるメソッド名が一覧表示されました。
Tips: 自動補完されない場合はAlt-/で単語補完
目的のメソッドやクラスが候補として表示されない場合は、Alt-/を押してみてください。
現在開いているファイルの中から、テキストベースで一致する単語を補完してくれます。
ちなみに僕はVimと同じようにCtrl-NとCtrl-Pにキーマップを割り当てています。
Ctrl-Rでテストの再実行 (6:45)
一度実行したテストを再実行するときのショートカットは Ctrl-R です。
ただし、IdeaVimプラグインのキーバインドと競合しているとCtrl-Rが使えません。
なので、代わりに Alt-Ctrl-R でRubyMine内で実行するプログラムを選択しています。
もちろん、画面上の緑の三角マーク(一番左)をクリックしても構いません。
ちなみに、この動画を撮った後でわかったんですが、Ctrl-RをIDE標準の動きにするか、Vimの動きにするのかは「Preference => Other Settings => Vim Emulation」で変更できるみたいです。
変更点をRevert (11:50)
Cmd-9で変更されたファイルの一覧を表示したあと、紫色の矢印マークをクリックすると変更の取消(Revert)ができます。
ショートカット(Alt-Cmd-Z)を使うのもOKです。
また、まだgitに追加されていない新しいファイルもファイル一覧の中から削除(Delete)することができます。
変更内容のdiffを見る (13:40)
Cmd-9で変更されたファイルの一覧を表示した後、Cmd-Dを押すと変更点のdiffが表示されます。
左に元のファイルが、右に変更後のファイルが表示されるので、変更点を確認しやすいです。
Tips: 変更されたファイルが複数ある場合は Cmd-Shift-]またはCmd-Shift-[で移動
変更されたファイルが複数ある場合、Cmd-Shift-] または Cmd-Shift-[ で前後のファイルへ移動できます。
テストの自動再実行 (14:50)
以下のアイコンをクリックすると、テストの自動再実行が有効になり、ファイルが変更されるたびにテストが再実行されます。
停止したい場合はもう一度同じアイコンをクリックしてください。
メソッド引数のリネーム (21:10)
メソッドの引数の上にカーソルを置いた状態で、「Cmd-Shft-A (Find action) => renameを入力、選択」すると、引数をリネームできます。
もちろん、その引数が使われている箇所も一緒に変更されます。
リネームは「右クリック => Refactor => Rename」や「Shft-F6」でも実行できます。
ローカル変数のリネーム (23:35)
メソッド引数だけでなく、ローカル変数も同じようにリネームできます。
メソッドの定義元にジャンプ (33:00)
メソッドの上にカーソルを置いた状態でCmd-Bを押すと、メソッドの定義元にジャンプできます。
候補が複数ある場合は選択肢が表示されます。
自分が定義したメソッドだけでなく、gemやRuby本体のメソッドにもジャンプすることができます。
RubyMineについてもっと知りたい?
RubyMineのことが気になってきたあなたにはこちらをどうぞ!
世界最強のRuby IDEの魅力をもっともっと知ることができますよ~!!
まとめ
というわけで、この記事ではRubyのコードをリファクタリングする様子を動画つきで紹介してみました。
元のロジックもシンプルでしたが、じっくり見てみるといろいろと改善ポイントが見えてくるものです。
今回の記事がみなさんの書くRubyのコードの改善に役立つと嬉しいです。
また、興味深いお題を提供してくれた@gam0022さんにも感謝します!
どうもありがとうございました。
あわせて読みたい
お時間のある方はこちらの記事もどうぞ。
動画(スクリーンキャスト)で学ぶRubyリファクタリング: Keitai Message編
今回の記事と同じように、他の人が書いたRubyプログラムを僕がリファクタリングしています。
もちろんスクリーンキャスト付きです。
PR: 弊社ソニックガーデンでも技術トレーニングを行っています
Rubyらしく、Railsらしく、きれいなコードできちんと動くWebアプリケーションを作りたい、という方は弊社ソニックガーデンの技術トレーニングを受講してみてはいかがでしょうか?
興味がある方は弊社Webサイトからお気軽にお問い合わせください。