今週の気になった bugs.ruby
内容は適当です。
今週と言っても今週みかけたチケットなだけでチケット自体は昔からあるやつもあります。
[Feature #16923] Switch reserved for numbered parameter warning to SyntaxError
_1
という名前は Numbered parameter で使用されており、同名の変数やメソッドを定義するとエラーになるようになる- 2.7 現在では警告がでている
- 次期 Ruby では以下のような挙動になる
# 2.7 : warning: `_1' is reserved for numbered parameter; consider another name # dev : error: _1 is reserved for numbered parameter _1 = 42 # 2.7 : warning: `_1' is reserved for numbered parameter; consider another name # dev : error: _1 is reserved for numbered parameter def _1 end
- また
define_method
で動的に_1
という名前のメソッドを定義することは可能
# 2.7 : no warning # dev : no warning, no error define_method(:_1) { 42 } p _1 # => 42
[Feature #17056] Array#index: Allow specifying start index to search like String#index does
String#index
では第二引数で検索開始位置を指定できる
str = "foo hoge foo hoge" # "hoge" の開始位置を返す p str.index("hoge") # => 4 # 6 文字目以降で検索する p str.index("hoge", 6) # => 13
Array#index
も定義されているがこちらは第二引数を受け取らないArray#index
でも開始位置を指定できるようにする提案
[Feature #17016] Enumerable#scan_left
- まだ議論中
- 以下のように書くことで Ruby 2.7 でも
#scan_left
と同等の処理を遅延評価できる
p (1..).lazy.enum_for(:inject, 0).map {|a, b| a + b }.take(10).force # => [1, 3, 6, 10, 15, 21, 28, 36, 45, 55] # もしくは # p (1..).lazy.enum_for(:inject, 0).map {|a, b| a + b }.first(10)
# 銀行の入出金履歴 gains = [+3000, -2000, +2000, -1000] # 残高の履歴を計算 sums = [0] (1..gains.length).each do |i| sums[i] = sums[i - 1] + gains[i - 1] end pp sums # => [0, 3000, 1000, 3000, 2000] # scan_left を使うとシュッとできる sums = gains.scan_left(0, &:+) pp sums # => [0, 3000, 1000, 3000, 2000]
#scan_left
という名前はあんまりよろしくないと言うことで別の名前の提案が出てる#reflect
や#project
や#interject
など
#reflect
の実装 : https://github.com/ruby/ruby/pull/3358
# reflect (0...0).reflect(:+) # => [] (5..10).reflect(:+) # => [5, 11, 18, 26, 35, 45] (5..10).reflect { |sum, n| sum + n } # => [5, 11, 18, 26, 35, 45] (5..10).reflect(1, :*) # => [1, 5, 30, 210, 1680, 15120, 151200] (5..10).reflect(1) { |product, n| product * n } # => [1, 5, 30, 210, 1680, 15120, 151200]o
# lazy.reflect (0..Float::INFINITY).lazy.reflect(:+).first(4) # => [0, 1, 3, 6] (0..Float::INFINITY).lazy.reflect(:+).first(4) # => [1, 1, 2, 4] enum = (5..10).lazy.reflect(:+) # => #<Enumerator::Lazy: #<Enumerator::Lazy: 5..10>:reflect(:+)> 6.times.map { enum.next } # => [5, 11, 18, 26, 35, 45] enum = (5..10).lazy.reflect(1, :*) # => #<Enumerator::Lazy: #<Enumerator::Lazy: 5..10>:reflect(1, :*)> 7.times.map { enum.next } # => [1, 5, 30, 210, 1680, 15120, 151200]
- 議論の裏で
Enumerable::Lazy#inject
が実装されたりしてた
(1..).lazy.inject(0, :+).first(10) #=> [0, 1, 3, 6, 10, 15, 21, 28, 36, 45]
[Bug #17017] Range#max & Range#minmax incorrectly use Float end as max その後
Bug #17017
の対応によりRange#max
Range#minmax
の挙動が少し変わった
p (1..3.1).max # 2.7 : => 3.1 # dev : => 3 p (1..3.1).minmax # 2.7 : => [1, 3.1] # dev : => [1, 3]
- この影響で dev 版では以下のようなコードがエラーになると書いた
p (42..Float::INFINITY).max # 2.7 : => Infinity # dev : error: `floor': Infinity (FloatDomainError)
- この挙動は既存のアプリケーションを壊していたので現行の挙動になるように修正された
- 最新の dev 版では以下のような動作になる
p (42..Float::INFINITY).max # 2.7 : => Infinity # dev : => Infinity
[Feature #17055] Allow suppressing uninitialized instance variable and method redefined verbose mode warnings
# 警告を出すようにする $VERBOSE = true class X def initialize @init = 42 end def hoge; end # 同名のメソッドを再定義すると警告が出る # warning: method redefined; discarding old hoge # warning: previous definition of hoge was here def hoge; end def foo # no warning @init # 未定義のインスタンス変数を参照すると警告ができる # warning: instance variable @no_init not initialized @no_init end end p X.new.foo
- この警告を実行時に Ruby のコードで制御する機能を追加する提案のチケット
.expected_redefined_method?
: メソッド再定義の警告を制御#expected_uninitialized_instance_variable?
: 未定義のインスタンス変数を参照したときの警告を制御
- これは以下のように使用できる
# 警告を出すようにする $VERBOSE = true class X # メソッドが再定義されたときに呼ばれるコールバックメソッド # 引数には再定義されたメソッドの名前を Symbol で受け取る # true を返した場合は警告が出なくなる def self.expected_redefined_method?(name) name == :hoge end # 未定義のインスタンス変数が呼ばれたときのコールバックメソッド # 引数にはインスタンス変数の名前を Symbol で受け取る # true を返した場合は警告が出なくなる def expected_uninitialized_instance_variable?(name) name == :@no_init end def initialize @init = 42 end def hoge; end # .expected_redefined_method? が true を返すので警告が出ない # no warning def hoge; end def bar; end # .expected_redefined_method? が false を返すので警告が出ない # warning: warning: method redefined; discarding old bar # warning: instance variable @warning_init not initialized def bar; end def foo # no warning @init # #expected_uninitialized_instance_variable? が true を返すので警告が出ない # no warning @no_init # #expected_uninitialized_instance_variable? が false を返すので警告が出る # warning: instance variable @no_init not initialized @warning_init end end p X.new.foo
.expected_redefined_method?
はクラスメソッド、#expected_uninitialized_instance_variable?
はインスタンスメソッドなので注意- 普段は警告を出さないようにしているけど、こういう細かい制御ができると便利なんですかね?
- ちなみに ActiveSupport には
Module#redefine_method
というヘルパメソッドが定義されており、これを使用するとメソッド再定義時の警告が出ないようになる - またインスタンス変数は
nil
で初期化しない方がパフォーマンスが向上するらしい