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
- なので組み込みの
#clamp
もNaN
を返すのが期待する挙動では?という旨らしい
[Bug #18188] -1 ** 0 is 1 not -1
-1 ** 0
は1
を期待するが実際には-1
が返ってくる、というバグ報告- が、Ruby は
-1 ** 0
を-(1 ** 0)
としてパースするのでこれは期待する挙動になっている(-1) ** 0
は1
が返ってくる
# これは -(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"]
- 具体的なユースケースがパッと思い浮かばなかった
- チケットには巡回セールスマン問題を特例が載っている
- https://bugs.ruby-lang.org/issues/18181#Example
- コメントで『最小(最大)の要素が複数ある場合に複数の要素を返す必要がある』などと指摘されてる
- その上で以下のように複数の要素を返すようにするといいのでは?とコメントされてる
%w(abcde fg hi jkl mn).min_by_with_elements(&:size) # => [2, ["fg", "hi", "mn"]]
- ただし
#min_by
は複数の要素があっても単一の要素を返すのでそっちに合わせるなら1つがよさそう?- 複数の要素を返す場合に大量にメモリを消費し、遅くなってしまうとコメントもされている
- https://bugs.ruby-lang.org/issues/18181#note-2
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
みたいに呼び出せる機能がほしい
[Bug #18170] Exception#inspect should not include newlines
- 次のようにエラー内容に改行が含まれている場合、標準出力でも改行されてしまう
p StandardError.new("foo\nbar") #=> # #<StandardError: foo # bar>
- これを
#<StandardError: "foo\nbar">
を返すようにするのはどうか、という提案 did_you_mean
やerror_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
はレシーバを引数のオブジェクトの内容で置き換えるメソッド- インスタンス変数などで保持していない内部情報をコピーするためのメソッド
- https://docs.ruby-lang.org/ja/latest/method/Object/i/initialize_copy.html
- 最新版では
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