2
0

More than 3 years have passed since last update.

Ruby で Python の for-else っぽいことをやってみる

Posted at

きっかけ

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

となります。eachself を返すので、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"
2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0