前田です。
mrkn (Kenta Murata) wrote:
以下のように String#[] に対して、範囲の開始インデックスが文字列の長さ以下の値である逆順の Range (beg > end) を渡した場合に空文字列が返ります。
"1"[1..0] #=> ""
"1"[1..-1] #=> ""
"123"[2..1] #=> ""
"123"[2..-2] #=> ""
一方、範囲の開始インデックスが文字列の長さより大きい場合は nil が返ります。
"1"[2..0] #=> nil
"1"[2..-1] #=> nil
"123"[4..1] #=> nil
"123"[4..-2] #=> nil
この挙動の違いは、rb_range_beg_len の実装に起因しています。
文字列のインデックスが昇順のみである仕様を考慮すると、上記前者の場合も nil が返るべきじゃないかと思います。
私も最初そう思ったのですが、PythonやJavaScriptではどちらの場合も空文字列を返すようです。
以下の例はどちらもRubyのs[x...y]相当(s[x..y]ではない)です。¶
defiant:ruby$ python
Python 2.7.2+ (default, Oct 4 2011, 20:03:08)
[GCC 4.6.1] on linux2
Type "help", "copyright", "credits" or "license" for more information.
"abc"[1:2]
'b'
"abc"[1:1]
''
"abc"[1:0]
''
"abc"[4:0]
''
defiant:ruby$ js
"abc".slice(1,2)
'b'
"abc".slice(1,1)
''
"abc".slice(1,0)
''
"abc".slice(4,0)
''
これはこれで合理的だと思いますがどうでしょうか。
また、Net::FTP#parse227が今の挙動に依存しているようです。
def parse227(resp) # :nodoc:
if resp[0, 3] != "227"
raise FTPReplyError, resp
end
left = resp.index("(")
right = resp.index(")")
if left == nil or right == nil
raise FTPProtoError, resp
end
numbers = resp[left + 1 .. right - 1].split(",")
if numbers.length != 6
raise FTPProtoError, resp
end
host = numbers[0, 4].join(".")
port = (numbers[4].to_i << 8) + numbers[5].to_i
return host, port
end
respが"227 )("のようなケースでも今のString#[]ではresp[left + 1 .. right - 1]
が""になるのでsplitできますが、nilを返すようになるとnil.split(",")でNoMethodError
になってしまいます。
このコードがひどいと言われればそれまでですが、若気の至りということで。
たぶん当時のftplib.pyのパクリなんだろうなあ。¶