割と頻繁にunlessやuntilがわかりづらいという意見を目にする。
rubocopなどでも禁止されているという話も聞いた。
これは根本的な考え方が間違っている。
基本的な考え方として false になったら、という考えを捨てる。
大前提として true になったらどうなるかで覚えると混乱しない。
unless
unless は条件を満たしたら実行しない式と考える
result = 1 if a && b # a && b を満たしたら、左辺を実行する
result = 2 unless a && b # a && b を満たしたら、左辺を実行しない
returnと組み合わせる場合(ガード節)は、処理を進める条件と考える
return if a && b # a && b を満たしたら、処理を抜ける
return unless a && b # a && b を満たしたら、処理を進める
ガード節として使う場合は以下のように覚える。両者は明確に役割が異なる。
| キーワード | 役割 | 動作 |
|---|---|---|
return if 式 |
ブラックリスト | 式の条件を満たしたら処理を抜ける |
return unless 式 |
ホワイトリスト | 式の条件を満たしたら処理を進める |
使い分けとして、両者が混ざるような判定式は分割するべきである。
以下に例を示す。
def gagazet_gate(yevonite) # case 1
return if yevonite == Kimahri
# キマリだけを通さない。これだと一般人も通れてしまう。
end
def gagazet_gate(yevonite) # case 2
return unless yevonite == Summoner || yevonite == Guardians
# 召喚士とガードは通す。これだとキマリも通れてしまう。
end
def gagazet_gate(yevonite) # Bad case
return if yevonite == Kimahri || yevonite != Summoner && yevonite != Guardians
# キマリと召喚士じゃない奴とガードじゃない奴は通さない。
# こんなビランは嫌だ。
end
def gagazet_gate(yevonite) # Best case
return unless yevonite == Summoner || yevonite == Guardians
return if yevonite == Kimahri
# 召喚士は通す。ガードも通す。キマリは通さない。
# この二つのガード節は目的が真逆なため、別個に使い分けるべきである。
end
until
until はtrueになったらループを抜ける条件式と考える
i = 0
p i while (i += 1) < 10 # iが10未満の間ループする
p i until (i -= 1) <= 0 # iが0以下になったらループを抜ける
例えば以下のような例がわかりやすい。式としてもスマート。
a = (1..100).to_a
a.shift until a.empty? # aが空になったらループを抜ける
まとめ
unless は if not への置き換えを推奨される事も多いが、その意見も前提が間違っている。
unless、until は if、while の条件式を反転して評価する構文なのではない。条件式に対する「動作を反転」する構文なのである。
また until はゴールを明確にする分、むしろ while よりもループに向く。
ループ文の真のエースは until である。決して while の亜種ではないのだ。