ワンライナーで書く前に、ある正数以上の数で最小のNの倍数を求めるRubyスクリプトを作る。

例として、ある正数を引数で渡して変数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だから、rに関わらず常にqのためと書ける。
p q * N
# rが使用されていないのでdivmodを使用せず次のように書ける。
p given_nr / N * N