ワンライナーの前にRubyである正数に最も近いNの倍数を求めるスクリプトを作る。
例として、ある正数を引数で渡して変数given_nrに格納し、Nを8とする。スクリプトファイル名をmultiple.rbとする。
まず考えられる解法は、given_nrをNで割り、余りがでなければgiven_nrを、余りが出れば商に1を足した数にNをかけたものを出力する方法だ。
N=8 given_nr=ARGV[0].to_i # 方法0 p given_nr % N == 0 ? given_nr : (given_nr / N + 1) * N
実行すると以下のように出力される。
$ ruby multiple.rb 15 16 $ ruby multiple.rb 16 16 $ ruby multiple.rb 17 24
Rubyでは商と余りの両方を一度に求めることができるので、以下のようにも書ける。
# 方法1 q, r = given_nr.divmod(N) p r == 0 ? given_nr : (q + 1) * N
また余りが0のときgiven_nrを返すのと商 * Nを返すのは同じ意味になるため、次の二通りの方法で書き換えることができる。
# 方法2 p (r == 0 ? q : q + 1) * N # 方法3 p ((r == 0 ? 0 : 1) + q) * N
方法3のロジックを少し変わった書き方で書いてみる。
# 方法4 p ((r <=> 0) + q) * N
<=>
で余りと0を比較している。余りがあれば0より大きいということなので1を返し、余りがなければ0と等しいということなので0を返す。
他にも次のように書ける。
# 方法5 p ([r, 1].min + q) * N
余りがあれば1が最小値として返り、余りがなければrが0なので0が最小値として返る。
方法0はgiven_nrが何度も出てきている。今回はgiven_nrを最初にセットしてから使用しているので問題ないが、ワンライナーで書こうとした場合、何度もgiven_nrに相当する式を書くのは都合が悪い。
同様に方法1から方法5も商と余りを変数q, rに代入している点でワンライナーとは厳密には言えなくなる。
ワンライナーで書きたい場合、次のようにすると簡潔に書ける。
# 方法_ワンライナー p (given_nr + N - 1) / N * N
given_nrは一度しか出てこないし、商と余りの両方を計算する必要もない。複数回登場するのは定数であるNだけ。
ロジックは、given_nrにNを超えない数を足した上で、Nで割った商に再度Nをかけるというもの。given_nrがNの倍数の時はN - 1
の値を足しても変わらず商が倍数のままでいてくれるし、given_nrがNの倍数でない時はN - 1
を足すことでgiven_nr以上の数で最小のNの倍数+αの数になり、商を求めてから再度Nをかけることでgiven_nr以上の数で最小のNの倍数が取得できる。
ワンライナーで実際に書いてみる。
$ ruby -e 'p (ARGV[0].to_i + 8 - 1) / 8 * 8' 15 16 $ ruby -e 'p (ARGV[0].to_i + 8 - 1) / 8 * 8' 16 16
ちなみに、「ある正数以上の数で最小のNの倍数を求める」方法を書いてきたが、「ある正数以下の数で最小のNの倍数を求める」方法は最後のワンライナーの方法のみになる。
# 方法_ワンライナー(以下version) p given_nr / N * N
商と余りの考え方で書いてもいいのだが、実際には上記と全く同じ式に収斂していく。
q, r = given_nr.divmod(N) # 方法1と同じ考え方 p r == 0 ? given_nr : q * N # r == 0のとき、given_nr == q * Nだから次のように書ける p (r == 0 ? q : q) * N # rに関わらず常にqのためと書ける。 p q * N # rが使用されていないのでdivmodを使用せず次のように書ける。 p given_nr / N * N
Copyright © 2017 システム開発メモ All Rights Reserved.