2021/09/23 今回の気になった bugs.ruby のチケット

今週は min / max / minmax で比較する値とその結果の要素を返すメソッドの提案などがありました。

[Bug #18187] Float#clamp() returns ArgumentError (comparison of Float with 1 failed)

  • Float::NAN.clamp(0, 100) すると ArgumentError が発生するがこれは期待する挙動ではない、というチケット
  • 例えば以下のように Ruby#clamp を実装した場合は Float::NAN を返す
Float.define_method(:clamp2) { |min, max| self < min ? min : self > max ? max : self }

p 8.0.clamp2(10, 100)
# => 10
p 80.0.clamp2(10, 100)
# => 80.0
p 800.0.clamp2(10, 100)
# => 100
p Float::NAN.clamp2(10, 100)
# => NaN

[Bug #18188] -1 ** 0 is 1 not -1

  • -1 ** 01 を期待するが実際には -1 が返ってくる、というバグ報告
  • が、Ruby-1 ** 0-(1 ** 0) としてパースするのでこれは期待する挙動になっている
    • (-1) ** 01 が返ってくる
# これは -(1 ** 0)
p(-1 ** 0)
# => -1

p(-(1 ** 0))
# => -1

p((-1) ** 0)
# => 1

[Feature #18181] Introduce Enumerable#min_with_value, max_with_value, and minmax_with_value

  • Enumerable#min_with_value, max_with_value, and minmax_with_value を追加する提案
  • これは比較するための値とその要素を一緒に返すためのメソッドになる
# 要素と比較する size の値の両方を結果として返す
%w(abcde fg hijk).min_with_value { |e| e.size } # => ['fg', 2]
%w(abcde fg hijk).max_with_value { |e| e.size } # => ['abcde', 5]
%w(abcde fg hijk).minmax_with_value { |e| e.size } # => [['fg', 2], ['abcde', 5]]
  • 引数を渡すとその個数分の結果も返ってくる
%w(abcde fg hijk).min_with_value(2) { |e| e.size } # => [['fg', 2], ['hijk', 4]]
%w(abcde fg hijk).max_with_value(2) { |e| e.size } # => [['abcde', 5], ['hijk', 4]]
  • 現状だと以下のように書く必要がある
    • のでもっと簡素に書きたいというのが要求みたい?
p %w(abcde fg hijk).map { |e| [e.size, e] }.min_by(&:first)
# => [2, "fg"]
%w(abcde fg hi jkl mn).min_by_with_elements(&:size) # => [2, ["fg", "hi", "mn"]]
  • ただし #min_by は複数の要素があっても単一の要素を返すのでそっちに合わせるなら1つがよさそう?
p %w(abcde fg hi jkl mn).min_by(&:size)
# => "fg"

[Feature #18179] Add Math methods to Numeric

  • Math.sqrt などのクラスメソッドの x.sqrt のように Numericインスタンスメソッドにするチケット
  • オブジェクト指向的にはインスタンスメソッドの方が自然な気がする
  • このチケットとは関係ないんですがこういう Hoge.foo(x) みたいなメソッドをシュッと x.foo みたいに呼び出せる機能がほしい
    • パイプライン演算子みたいなのがほしいって言うわけではなくても Hoge モジュールをインスタンスメソッドとして組み込むような機能

[Bug #18170] Exception#inspect should not include newlines

  • 次のようにエラー内容に改行が含まれている場合、標準出力でも改行されてしまう
p StandardError.new("foo\nbar")
#=>
# #<StandardError: foo
# bar>
  • これを #<StandardError: "foo\nbar"> を返すようにするのはどうか、という提案
  • did_you_meanerror_highlight で複数行のエラーを表示する際に出力が奇妙になるらしい
class Foo
  def initialize
    @exception = begin; exampl; rescue Exception; $!; end
  end

  def example
  end
end

p Foo.new
#=>
# #<Foo:0x00007f15aeb4ba48 @exception=#<NameError: undefined local variable or method `exampl' for #<Foo:0x00007f15aeb4ba48 ...>
#
#     @exception = begin; exampl; rescue Exception; $!; end
#                         ^^^^^^
# Did you mean?  example>>

[Bug #17048] Calling initialize_copy on live modules leads to crashes

  • 以下のコードで Ruby がクラッシュするというバグ報告
loop do
  m = Module.new do
    prepend Module.new
    def hello
    end
  end

  klass = Class.new { include m }
  m.send(:initialize_copy, Module.new)
  GC.start

  klass.new.hello rescue nil
end
  • Module#initialize_copy を呼び出すとクラッシュする可能性があるらしい
  • initialize_copy はレシーバを引数のオブジェクトの内容で置き換えるメソッド
  • 最新版では Module#initialize_copy を呼び出すと TypeError が発生するように修正されている
module A
end

# error: `initialize_copy': already initialized module (TypeError)
A.send(:initialize_copy, Module.new) # fine, no one inherits from A