Ruby(1.8.7〜1.9.0)の配列の末尾には見えない要素があるらしい。

 少し細かい話を・・・・。
 『プログラミング言語Ruby』の「9.5.2.3 配列要素の書き換え」(p353)にsliceメソッドの解説に以下のようなサンプルコードが掲載されています。


<h1>slice!で要素、部分配列を削除</h1>

a = [1,2,3,4,5,6,7,8]
a.slice!(0)

<h1>=> 1: 添字 0 の要素を削除: a は[2,3,4,5,6,7,8]</h1>

a.slice!(-1,1)

<h1>=> [8]: 末尾の部分配列を削除: aは[2,3,4,5,6,7]</h1>

a.slice!(2..3)

<h1>=> [4,5]: 範囲を受け付ける: aは[2,3,6,7]</h1>

a.slice!(4,2)

<h1>=> []: 末尾の位置にある空配列: a は変更されず</h1>

a.slice!(5,2)

<h1>=> nil: a は [2,3,6,7,nil]に</h1>

1行目から8行目まではよくわかるのですが、9行目と11行目がよく理解できませんでした。同じ配列の範囲外を指定しているのに、なぜ9行目が空の配列を返し、11行目がnilを返してくるのかと。
 当然ですが、クラスも異なります。

a.slice!(4,2).class
#⇒ Array
a.slice!(5,2).class
#⇒ Nilclass

 
 う~ん、よくわからん。配列の末尾には

[2,3,5,6,(nil)]

 
という感じで見えないnilの値でも入っているのだろうかと、眠れない夜が続いていた(嘘)のですが、Rubyに詳しい方にこの件についてお尋ねする機会があったので伺ってみると、配列には見えない形で末尾に配列の末端であることを示すデータが入っているらしいのだそうです。C言語で書かれているのでその実装上の問題とか・・。C言語はよくわかりませんが、そうなんですか・・・。
つまり、

a ⇒[2,3,5,6,(配列の末尾であることを示す見えない要素)]

であるので、

a.slice!(4,2)

<h1>=> []</h1>

 
 と、空の配列を返すのだそうです。なるほど!そして、以下の場合

slice!(5,2)

 
 以下の■の部分のような配列の外を第一引数が指してしまうので、nilが返ってくるということになるらしい。なるほどぉぉお。

a ⇒[2,3,5,6,(配列の末尾であることを示す見えないデータ)] ■←ココ!

 
ちなみに
 『プログラミング言語Ruby』では以下のようにa配列の5番目の要素に”nil”が追加されますが、

a.slice!(5,2)

<h1>=> nil: a は [2,3,6,7,nil]に</h1>

 
 私のMacでは以下のようになります。a配列に5番目の要素に”nil”が追加されたりはしません。ちなみに私はRuby 1.8.7を使用しています。『プログラミング言語Ruby』は1.9.0までフォローしています。

a.slice!(5,2)

<h1>=> nil: a は [2,3,6,7]に</h1>

 
 この辺はRubyの実装環境の違いなんでしょうか・・・。
 ちなみにWebベースのirbであるtry ruby!で試したRuby 1.9.2だとArgumentErrorを返すようです。バグとして修正されたのでしょうか。

a = [2,3,6,7]
=> [2,3,6,7]

<blockquote>
  <blockquote>
    a.slice[4,2]
    ArgumentError: wrong number of arguments (0 for 1..2)
    a.slice[5,2]
    ArgumentError: wrong number of arguments (0 for 1..2)
    

 

RubyのClassクラスのインスタンスの定数はどこで定義されているのか

  RubyのクラスはClassクラスのインスタンスらしい。なるほど。
つまり、以下のようにABCクラスを作るとClassクラスのインスタンスが生成されるらしい。

Class ABC
end
ABC.class
=> Class

 そして、クラス名は定数なのだそうだ。上の場合だと”ABC”は定数でもあるらしい(だから、クラス名の最初は大文字)。Classクラスのインスタンスを定数ABCに代入しているということらしい。
上でクラスの作成は以下のように書くこともできる。

ABC = Class.new{ }

 より定数に代入しているっぽいですね。
 ならば、このクラス名の定数”ABC”はどこのクラスで定義されているのだろうかと。
 ちょっと調べたら簡単にわかりました。以下のようにObjectクラスの定数としてクラス名は定義されているようです。

p Object.constants.grep /ABC/
⇒ ["ABC"]

Rubyのその他の組み込みクラスのクラス名もObjectクラスに定数として定義されています。さきほど私が定義したクラス名ABCも含めて。

p Object.constants
=> ["ThreadError", "RUBY_PLATFORM", "Regexp", "Deprecate", "Numeric", "String",
"SLex", "Bignum", "IndexError", "SecurityError", "UnboundMethod", "Exception",
"Readline", "RubyLex", "NoMethodError", "FALSE", "Object", "RELEASE_DATE", "IRB",
"Range", "IO", "Integer", "Config", "TypeError", "RubyToken", "Dir", "RUBY_VERSION",
 "ZeroDivisionError", "Gem", "SystemExit", "NotImplementedError", "Hash", "RegexpError",
"Kernel", "TOPLEVEL_BINDING", "RUBY_COPYRIGHT", "GC", "TRUE", "STDOUT", "LocalJumpError",
"Binding", "Interrupt", "RUBY_FRAMEWORK", "ObjectSpace", "SyntaxError", "Marshal",
"Enumerable", "Struct", "Class", "Continuation", "IOError", "MAXHISTSIZE", "RangeError",
"Kconv", "Data", "Thread", "RUBY_PATCHLEVEL", "SystemStackError", "Array", "NoMemoryError",
 "FileTest", "MatchData", "ARGF", "ETC_IRBRC_LOADED", "NIL", "Comparable", "PLATFORM",
 "ArgumentError", "Float", "FloatDomainError", "RUBY_RELEASE_DATE", "RuntimeError",
"Precision", "ThreadGroup", "Signal", "Fixnum", "STDERR", "FalseClass", "VERSION",
 "Exception2MessageMapper", "RbConfig", "Errno", "StandardError", "Math", "EOFError",
 "LoadError", "NKF", "RUBY_FRAMEWORK_VERSION", "APPLE_GEM_HOME", "STDIN", "TrueClass",
 "NameError", "NilClass", "Process", "Proc", "RUBY_DESCRIPTION", "ARGV", "ABC",
"SystemCallError", "SignalException", "MatchingData", "File", "Time", "ENV",
 "ScriptError", "Symbol", "Module", "Method", "StopIteration",
"DTracer", "CROSS_COMPILING", "HISTFILE"]

 なるほど。なるほど。なるほど。