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