[ruby-dev:39604] [Bug #2323] "Z".."Z".succが空

View: New views
5 Messages — Rating Filter:   Alert me  

[ruby-dev:39604] [Bug #2323] "Z".."Z".succが空

by Bryan McLellan-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Bug #2323: "Z".."Z".succが空
http://redmine.ruby-lang.org/issues/show/2323

起票者: Hiro Asari
ステータス: Open, 優先度: Normal
カテゴリ: core
ruby -v: ruby 1.9.2dev (2009-11-02 trunk 25625) [x86_64-darwin10.0.0]

surfboard:~$ ruby1.9 -v; ruby1.9 -e 'p ("Z".."Z".succ); p ("Z".."Z".succ).to_a'
ruby 1.9.2dev (2009-11-02 trunk 25625) [x86_64-darwin10.0.0]
"Z".."AA"
[]

"Z".succではなくてもうちょっと”離れた”ヤツをRangeの終わりとして指定すると"AA"はしっかりと入っています。

surfboard:~$ ruby1.9 -e 'p ("Z".."ZA").include? "Z".succ'
true

でも半端な”離れ”方ではいけません。

surfboard:~$ ruby1.9 -e 'p ("Z".."CA").include? "Z".succ'
false

to_aを介しても同様です。

surfboard:~$ ruby1.9 -e 'p ("Z".."ZA").to_a.include? "Z".succ'
true
surfboard:~$ ruby1.9 -e 'p ("Z".."CA").to_a.include? "Z".succ'
false


----------------------------------------
http://redmine.ruby-lang.org


[ruby-dev:39612] Re: [Bug #2323] "Z".."Z".succが空

by Nobuyoshi Nakada-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

なかだです。

At Mon, 2 Nov 2009 16:19:40 +0900,
Hiro Asari wrote in [ruby-dev:39604]:
> surfboard:~$ ruby1.9 -v; ruby1.9 -e 'p ("Z".."Z".succ); p ("Z".."Z".succ).to_a'
> ruby 1.9.2dev (2009-11-02 trunk 25625) [x86_64-darwin10.0.0]
> "Z".."AA"
> []

こんなところでしょうか。


Index: string.c
===================================================================
--- string.c (revision 25629)
+++ string.c (working copy)
@@ -2844,4 +2844,44 @@ rb_str_succ_bang(VALUE str)
 }
 
+static int
+str_succ_cmp(VALUE str1, VALUE str2, rb_encoding *enc)
+{
+    const char *p1 = RSTRING_PTR(str1), *e1 = RSTRING_END(str1);
+    const char *p2 = RSTRING_PTR(str2), *e2 = RSTRING_END(str2);
+    size_t l1, l2;
+    do {
+ if (ISUPPER(*p1) && ISUPPER(*p2)) {
+    for (l1 = 1; p1+l1 < e1 && ISUPPER(p1[l1]); l1++);
+    for (l2 = 1; p2+l2 < e2 && ISUPPER(p2[l2]); l2++);
+ }
+ else if (ISLOWER(*p1) && ISLOWER(*p2)) {
+    for (l1 = 1; p1+l1 < e1 && ISLOWER(p1[l1]); l1++);
+    for (l2 = 1; p2+l2 < e2 && ISLOWER(p2[l2]); l2++);
+ }
+ else if (ISDIGIT(*p1) && ISDIGIT(*p2)) {
+    for (l1 = 1; p1+l1 < e1 && ISDIGIT(p1[l1]); l1++);
+    for (l2 = 1; p2+l2 < e2 && ISDIGIT(p2[l2]); l2++);
+ }
+ else {
+    int n, c;
+    for (l1 = 0; p1+l1 < e1; l1 += n) {
+ c = rb_enc_ascget(p1+l1, e1, &n, enc);
+ if (ISALNUM(c)) break;
+    }
+    for (l2 = 0; p2+l2 < e2; l2 += n) {
+ c = rb_enc_ascget(p2+l2, e2, &n, enc);
+ if (ISALNUM(c)) break;
+    }
+    if (l1 == 0 || l2 == 0) return 1;
+ }
+ if (l1 > l2) return 1;
+ if (l1 == l2) {
+    if (memcmp(p1, p2, l1) > 0) return 0;
+ }
+ p1 += l1;
+ p2 += l2;
+    } while (p1 < e1 || p2 < e2);
+    return -(p1 == e1 && p2 == e2);
+}
 
 /*
@@ -2881,4 +2921,5 @@ rb_str_upto(int argc, VALUE *argv, VALUE
     ID succ;
     int n, excl, ascii;
+    char c, e;
     rb_encoding *enc;
 
@@ -2891,8 +2932,9 @@ rb_str_upto(int argc, VALUE *argv, VALUE
     ascii = (is_ascii_string(beg) && is_ascii_string(end));
     /* single character */
-    if (RSTRING_LEN(beg) == 1 && RSTRING_LEN(end) == 1 && ascii) {
- char c = RSTRING_PTR(beg)[0];
- char e = RSTRING_PTR(end)[0];
-
+    if (ascii &&
+ RSTRING_LEN(beg) == 1 && RSTRING_LEN(end) == 1 &&
+ (c = RSTRING_PTR(beg)[0], ISALPHA(c)) &&
+ (e = RSTRING_PTR(end)[0], ISALPHA(e)) &&
+ (ISUPPER(c) == ISUPPER(e))) {
  if (c > e || (excl && c == e)) return beg;
  for (;;) {
@@ -2906,5 +2948,5 @@ rb_str_upto(int argc, VALUE *argv, VALUE
     /* both edges are all digits */
     if (ascii && ISDIGIT(RSTRING_PTR(beg)[0]) && ISDIGIT(RSTRING_PTR(end)[0])) {
- char *s, *send;
+ const char *s, *send;
  VALUE b, e;
  int width;
@@ -2949,5 +2991,8 @@ rb_str_upto(int argc, VALUE *argv, VALUE
     /* normal case */
   no_digits:
-    n = rb_str_cmp(beg, end);
+    if (ascii && rb_enc_str_coderange(beg) == rb_enc_str_coderange(end))
+ n = str_succ_cmp(beg, end, enc);
+    else
+ n = rb_str_cmp(beg, end);
     if (n > 0 || (excl && n == 0)) return beg;
 
Index: test/ruby/test_range.rb
===================================================================
--- test/ruby/test_range.rb (revision 25629)
+++ test/ruby/test_range.rb (working copy)
@@ -256,4 +256,7 @@ class TestRange < Test::Unit::TestCase
     assert(!(("a"..."z").include?("z")))
     assert(!(("a".."z").include?("cc")))
+    assert(("Z".."ZA").include?("AA"))
+    assert(!("Z".."za").include?("AA"))
+    assert(("Z".."CA").include?("AA"))
     assert((0...10).include?(5))
   end
Index: test/ruby/test_string.rb
===================================================================
--- test/ruby/test_string.rb (revision 25629)
+++ test/ruby/test_string.rb (working copy)
@@ -1588,4 +1588,8 @@ class TestString < Test::Unit::TestCase
                    })
     assert_equal(676, count)
+
+    a = []
+    S("Z").upto(S("AA")) {|s| a << s}
+    assert_equal([S("Z"), S("AA")], a)
   end
 


--
--- 僕の前にBugはない。
--- 僕の後ろにBugはできる。
    中田 伸悦


[ruby-dev:39622] Re: [Bug #2323] "Z".."Z".succが空

by Hirotsugu Asari :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

浅里です。

2009/11/2 Nobuyoshi Nakada <nobu@...>:

> なかだです。
>
> At Mon, 2 Nov 2009 16:19:40 +0900,
> Hiro Asari wrote in [ruby-dev:39604]:
>> surfboard:~$ ruby1.9 -v; ruby1.9 -e 'p ("Z".."Z".succ); p ("Z".."Z".succ).to_a'
>> ruby 1.9.2dev (2009-11-02 trunk 25625) [x86_64-darwin10.0.0]
>> "Z".."AA"
>> []
>
> こんなところでしょうか。

このパッチで件の問題は解決されていますが、ちょっと別なところで問題になるかもしれません。

このパッチを当ててみたところ、"Z".."a"は空です。1.8では["Z"]が返ってきます。

更に、
http://redmine.ruby-lang.org/issues/show/1891にあるように、
パッチ無しでは["Z", "[", "\\", "]", "^", "_", "`", "a"]です。
これに対応したリビジョン(r24573)では加えて:Z..:aが認められていて、
それにはアスキー配列に則ったシンボルが含まれています。

個人的には"Z".succが"AA"であるならば、二つ以上の要素を持つ"Z"から始まるRangeは"AA"を含むべきだと考えます。
Rangeを半順序集合に定義するのも可能とは思いますが、特別な動きをする事があるのならば、
それは明確に書かれていないと混乱を招くと思います。

参考までに指摘しておきます。


--
Hirotsugu Asari


[ruby-dev:39630] Re: [Bug #2323] "Z".."Z".succが空

by Yukihiro Matsumoto :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

まつもと ゆきひろです

In message "Re: [ruby-dev:39622] Re: [Bug #2323] "Z".."Z".succが空"
    on Tue, 3 Nov 2009 22:22:58 +0900, Hiro Asari <asari.ruby@...> writes:


|個人的には"Z".succが"AA"であるならば、二つ以上の要素を持つ"Z"から始まるRangeは"AA"を含むべきだと考えます。
|Rangeを半順序集合に定義するのも可能とは思いますが、特別な動きをする事があるのならば、
|それは明確に書かれていないと混乱を招くと思います。

文字列のRangeについては、

  * 文字列の順序の定義が複数ある(辞書順とsuccによるもの)
  * Rangeはsuccを使うが、これは半順序集合でいろいろ面倒

という事情があります。で、現在、ちょっと中途半端な状態になっ
てます。最終的な仕様を検討する時間(とやる気)が取れなくて。

現状

  * 両端ともすべてASCII数字である場合には、数的な順序
  * 両端ともASCII1文字である場合には、ASCII文字コード的な順序

になってます。で、このいずれにもあてはまらないケースの仕様は
正直確定してません。苦労してもあんまり使われなさそうだし。
ただ、
   
  * 両端が共通の先頭部分を持ち、非共通部分がすべてASCII数字で
    ある場合には数的な順序

というのは採用しようと思ってます。あとは、ある文字列から別の
文字列にsuccの連鎖で到達できるかどうか簡易に判定できるのであ
れば、それに従って順序を処理すると良いと思うのですけど、でき
るんだっけか。

                                まつもと ゆきひろ /:|)


[ruby-dev:39631] Re: [Bug #2323] "Z".."Z".succが空

by NARUSE, Yui-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

成瀬です。

Yukihiro Matsumoto wrote:

> まつもと ゆきひろです
>
> In message "Re: [ruby-dev:39622] Re: [Bug #2323] "Z".."Z".succが空"
>     on Tue, 3 Nov 2009 22:22:58 +0900, Hiro Asari <asari.ruby@...> writes:
>
>
> |個人的には"Z".succが"AA"であるならば、二つ以上の要素を持つ"Z"から始まるRangeは"AA"を含むべきだと考えます。
> |Rangeを半順序集合に定義するのも可能とは思いますが、特別な動きをする事があるのならば、
> |それは明確に書かれていないと混乱を招くと思います。
>
> 文字列のRangeについては、
>
>   * 文字列の順序の定義が複数ある(辞書順とsuccによるもの)
>   * Rangeはsuccを使うが、これは半順序集合でいろいろ面倒
>
> という事情があります。で、現在、ちょっと中途半端な状態になっ
> てます。最終的な仕様を検討する時間(とやる気)が取れなくて。
>
> 現状
>
>   * 両端ともすべてASCII数字である場合には、数的な順序
>   * 両端ともASCII1文字である場合には、ASCII文字コード的な順序
>
> になってます。で、このいずれにもあてはまらないケースの仕様は
> 正直確定してません。苦労してもあんまり使われなさそうだし。

念のため補足しておきますと、
Range#each のマニュアルには succ を用いると書いてありますが、
String や Symbol の場合は実際には upto を呼んでおり、
String#upto は常には String#succ を呼んでいません。
上記のまつもとさんの説明は String#upto の説明になります。

言い換えると、Range#to_a から Range#eachが呼ばれ、
range_each 経由で String#upto に行っているので、
この問題は以下のように書き換えることが出来ます。

 % ruby19 -ve'"Z".upto("AA"){|x|p x}'
 ruby 1.9.2dev (2009-11-02 trunk 25632) [x86_64-freebsd8.0]

> あとは、ある文字列から別の
> 文字列にsuccの連鎖で到達できるかどうか簡易に判定できるのであ
> れば、それに従って順序を処理すると良いと思うのですけど、でき
> るんだっけか。

ちょっと簡易とは言いづらい気がします。
先の中田さんのパッチだと "0 0" から到達できない "0 00" に対して、
upto が微妙に動いたりしています。
 "0 0".upto("0 00"){|x|p x}

英数の間に非英数がはさまったり、非英数が繰り上がって英数に突入するケースは
なかなか難しいように思います。
一度きっちり考えて文書化しさえすれば、あとは実装するだけではありますが。


代替案として、常に succ を使うようにして、なんとなく動くようにしつつ、
とりあえず確実に停止するようにするという方法もありますかね。
中田さんのパッチはそのような趣旨であるようにみえます。

--
NARUSE, Yui  <naruse@...>