2022/03/03 今回の気になった bugs.ruby のチケット
今週はラテン文字を String#downcase したときのバグ報告がありました。
[Feature #18603] Allow syntax like obj.method(arg)=value
- 次のようのような構文を許容する提案
obj.method(arg) = value
- これは以下と同じ意味になる
obj.__send__(:method=, arg, value)
- ユースケースとしては
digしつつ代入したい場合に有効らしい
obj.dig(0, :key, 1) = 20
- あるとなにかに利用できそうだけどメソッド呼び出しは
()が省略できるのでなかなかややこしそうobj.method arg = valueみたいなコードは現状でもかけるので
- あと
def obj.method(arg) = valueと構文が似てるので混乱する、とコメントされていますね
[Bug #18590] String#downcase and CAPITAL LETTER I WITH DOT ABOVE
- ラテン文字
İのString#downcaseの結果はiを期待する
'İ'.downcase # => "i̇"
- しかし
String#downcaseの結果に結合文字"̇"が含まれてしまっているというバグ報告
'İ'.downcase.chars # => ["i", "̇"]
- ただ、Unicode の仕様として
İ(U+0130) の小文字がi(U+0069 U+0307) となるのは意図する挙動ぽいです - このあたりの仕様は参照するドキュメントによって違うみたいですね?
- https://www.unicode.org/Public/UCD/latest/ucd/UnicodeData.txt だと以下のように記載されており
0130;LATIN CAPITAL LETTER I WITH DOT ABOVE;Lu;0;L;0049 0307;;;;N;LATIN CAPITAL LETTER I DOT;;;0069;
- http://www.unicode.org/Public/UCD/latest/ucd/CaseFolding.txt だと以下のように記載されている
Fがフルケースホールディングを表しTがチュルク語族を表しているFの場合はU+0069 U+0307になりTの場合はU+0130になる
0130; F; 0069 0307; # LATIN CAPITAL LETTER I WITH DOT ABOVE 0130; T; 0069; # LATIN CAPITAL LETTER I WITH DOT ABOVE
String#downcaseでは引数に:turkicを渡すとチュルク語族に適した形で小文字に変換される
'İ'.downcase.chars # => ["i", "̇"] 'İ'.downcase(:turkic).chars # => ["i"]
String#downcaseのデフォルトの挙動としてはフルケースホールディングになるのが意図しているので今回の報告は期待する挙動だったみたい- ただし、rdoc で参照しているドキュメントが正しくなかったのでそれに関しては別途修正されている
- https://github.com/ruby/ruby/pull/5607
- 元のドキュメントだと
İ(U+0130) の小文字がi(U+0069)と記載されていたのでそれに対する対応
[Feature #18551] Make Range#reverse_each to raise an exception if endless
- 終端無限
Rangeに対してRange#reverse_eachを呼び出した時に例外を発生させる提案 - 現状は無限ループになる
- これは内部で
#to_aを呼び出して配列に変換しようとしているから
- これは内部で
# 無限ループになる (1..).reverse_each { }
- 先端無限に対して
#eachを呼び出す場合は例外になるのでそれに合わせたいって感じみたい
# error: `each': can't iterate from NilClass (TypeError) (..1).each { }
- 懸念点として
Range#reverse_eachを新たに実装する必要があるがその場合のパフォーマンスや仕様の複雑さがコメントされている - 定期的に無限
Rangeに対するこの手の話題が上がってる気がする - 個人的には全体的に一貫性の挙動にはなってほしい
[Bug #18577] Range#include? returns wrong result for beginless range with exclusive string end
(...'z')の場合は終端の値を含めるべきではないが#include?#member?#===はtrueになり終端を含めた結果を返す- ただし
#cover?はfalseを返す - これは先端が無限の場合にのみ発生してる
- ただし
# 数値は終端を含まない (...10).include?(10) # => false (...10).member?(10) # => false (...10) ===(10) # => false (...10).cover?(10) # => false # 文字列は終端を含めている (...'z').include?('z') # => true (...'z').member?('z') # => true (...'z') ===('z') # => true (...'z').cover?('z') # => false # 先端が無限でない場合は発生しない ('a'...'z').include?('z') # => false ('a'...'z').member?('z') # => false ('a'...'z') ===('z') # => false ('a'...'z').cover?('z') # => false
- これは Ruby 3.2 で修正される
# Ruby 3.2 だと全部 false を返す (...'z').include?('z') # => false (...'z').member?('z') # => false (...'z') ===('z') # => false (...'z').cover?('z') # => false