きっかけ
Effective Python メモ 項目12 forとwhileループの後のelseブロックは使うのを避ける を読んで、逆に Ruby だとどう書くのかが気になった。
やること
a = 4
b = 9
for i in range(2, min(a, b) + 1):
print('Testing', i)
if a % i == 0 and b % i == 0:
print('Not coprime')
break
else:
print('Coprime')
else
節が実行されるのは、ループが中断されることなく終了した場合です。確かに、直感的な動作ではありません。
これをを Ruby で頑張ります。
検討
else
節を無視すると、Ruby の場合、
a = 4
b = 9
(2..[a, b].min).each do |i|
puts "Testing: #{i}"
if (a % i).zero? && (b % i).zero?
puts "Not coprime"
break
end
end
となります。each
は self
を返すので、break
することなく繰り返しが終了すると、2..[a, b]
すなわち、真の値が返り、break
すると、nil
すなわち、偽の値が返ります。
そこで、and
でつなげば上手くいきそうです。
結論
a = 4
b = 9
(2..[a, b].min).each do |i|
puts "Testing: #{i}"
if (a % i).zero? && (b % i).zero?
puts "Not coprime"
break
end
end and puts "Coprime"
一応、上手くいきました。でも、美しくありませんねぇ。
なお、&&
を使うには puts("Coprime")
とするか (puts "Coprime")
として、puts
と "Coprime"
の結び付きを強くする必要があります。
おまけ
Ruby ぽく puts
を外に出す方法も考えてみました。
その1
a = 4
b = 9
puts (2..[a, b].min).each_with_object("Coprime") { |i|
puts "Testing: #{i}"
if (a % i).zero? && (b % i).zero?
break "Not coprime"
end
}
その2
a = 4
b = 9
puts "Coprime".tap {
(2..[a, b].min).each do |i|
puts "Testing: #{i}"
if (a % i).zero? && (b % i).zero?
break "Not coprime"
end
end
}
どれも分かりづらいです。
『Effective Python』にもあるように、やはり for-else
的なことはやらずに、メソッドに切り出すのが一番のようです。
def coprime?(a, b)
(2..[a, b].min).each do |i|
return false if (a % i).zero? && (b % i).zero?
end
true
end
puts coprime?(4, 9) ? "Coprime" : "Not coprime"