3

何らかの理由で、Unicode 文字の範囲比較で予期しない結果が得られます。

要約すると、最小化されたテスト コードで("\u1000".."\u1200") === "\u1100"false、は であると予想されますが、trueに対する同じテスト"\u1001"true予想どおりです。これはまったく理解できないと思います。演算子の結果<も興味深いもので、矛盾してい===ます。

次のコードは、適切な最小限の例です。

# encoding: utf-8

require 'pp'

a = "\u1000"
b = "\u1200"

r = (a..b)

x = "\u1001"
y = "\u1100"

pp a, b, r, x, y

puts "a < x = #{a < x}"
puts "b > x = #{b > x}"

puts "a < y = #{a < y}"
puts "b > y = #{b > y}"

puts "r === x = #{r === x}"
puts "r === y = #{r === y}"

===ここでは、両方の操作が「true」を生成することを単純に期待します。ただし、このプログラムを実行した実際の出力は次のとおりです。

ruby 1.9.3p125 (2012-02-16 revision 34643) [x86_64-darwin11.3.0]
"\u1000"
"\u1200"
"\u1000".."\u1200"
"\u1001"
"\u1100"
a < x = true
b > x = true
a < y = true
b > y = true
r === x = true
r === y = false

誰かが私を啓発できますか?

(私は Mac OS X で 1.9.3 を使用しており、エンコーディングを明示的に utf-8 に設定していることに注意してください。)

4

2 に答える 2

3

アクション: この動作をバグ #6258 として ruby​​-lang に提出しました。

その範囲の文字の照合順序に奇妙な点があります

irb(main):081:0> r.to_a.last.ord.to_s(16)
=> "1036"
irb(main):082:0> r.to_a.last.succ.ord.to_s(16)
=> "1000"
irb(main):083:0> r.min.ord.to_s(16)
=> "1000"
irb(main):084:0> r.max.ord.to_s(16)
=> "1200"

範囲の最小値と最大値は入力から期待される値ですが、範囲を配列に変換すると、最後の要素は "\u1036" になり、後続の要素は "\u1000" になります。内部では、Range#=== は最小値と最大値の単純な境界チェックではなく、 String#succシーケンスを列挙している必要があります。

Range#===のソース (トグルをクリック)を見ると、 Range#include?にディスパッチされていることがわかります。. Range#include? source は文字列の特別な処理を示しています -- 回答が文字列の長さだけで決定できる場合、または関連するすべての文字列が ASCII である場合、単純な境界チェックを行います。それ以外の場合は、super にディスパッチします。Enumerable#include?によって回答が得られますか? これはRange#eachを使用して列挙します。これは再び文字列に対して特別な処理を行い、 String#uptoにディスパッチします。これはString#succで列挙します。

String#succ には、文字列に is_alpha または is_digit の数字が含まれている場合 ( U+1036には当てはまらない)、特別な処理がたくさんありますenc_succ_char。この時点で証跡は失われますが、おそらくこれは、文字列に関連付けられたエンコーディングと照合情報を使用して後続を計算します。

ところで、回避策として、整数の序数の範囲を使用して、単一の文字のみを気にする場合は序数に対してテストできます。例えば:

r = (a.ord..b.ord)
r === x.ord
r === y.ord
于 2012-04-04T23:41:43.800 に答える
2

Range は、私たちが考えている意味とは違うようです。

私が考えているのは、文字、数字、および句読点を含めようとしている Range を作成していることです。Rubyはこれを行うことができず、本質的にコードポイントの配列が必要であることを「理解」していません。

これにより、Range#to_a メソッドがバラバラになります。

("\u1000".."\u1099").to_a.size  #=> 55
("\u1100".."\u1199").to_a.size  #=> 154
("\u1200".."\u1299").to_a.size  #=> 73

ジンガーは、3つすべてを一緒にすると次のようになります。

("\u1000".."\u1299").to_a.size  #=> 55

Ruby 1.8.7 は期待どおりに動作します。Matt がコメントで指摘しているように、Unicode がないため、「\u1000」は単なる「u1000」です。

string#succ C ソース コードは、次の codepoint を返すだけではありません。

Returns the successor to <i>str</i>. The successor is calculated by                                                                                                                                                                                                          
incrementing characters starting from the rightmost alphanumeric (or                                                                                                                                                                                                         
the rightmost character if there are no alphanumerics) in the                                                                                                                                                                                                                
string. Incrementing a digit always results in another digit, and                                                                                                                                                                                                            
incrementing a letter results in another letter of the same case.                                                                                                                                                                                                            
Incrementing nonalphanumerics uses the underlying character set's                                                                                                                                                                                                            
collating sequence.     

Range は、単に next, next, next とは異なることを行っています。

これらの文字を含む範囲は、ACSII シーケンスを実行します。

('8'..'A').to_a
=> ["8", "9", ":", ";", "<", "=", ">", "?", "@", "A"]

しかし、#succ の使用はまったく異なります。

'8'.succ
=> '9'

'9'.succ
=> '10'  # if we were in a Range.to_a, this would be ":"
于 2012-04-04T23:17:10.480 に答える