rubyの「class内のself」と「method内のself」

gold対策(模擬試験15)。  
「class内のself」と「method内のself」を問う問題です。
通常class内はclassが、selfはインスタンスが返りますが ちょっと引っかけっぽいです。

class A
  $a = self
  def hoge
    $b = self
  end
end
$a # => A
$b # => nil # !> global variable `$b' not initialized

これ、$bはnilなんですね。 まだ評価されていないので。

A.new.hoge
$b # => #<A:0x007fd5238400d0>

とするとインスタンスが返ります。

rubyのクラス変数

gold対策(模擬試験13)。  
インスタンス間で共有され、かつ自分自身のクラスとサブクラスで管理される。

class A
  @@x = 0
  class << self
    def x
      @@x
    end
  end
  def x
    @@x = 2
  end
end

class B < A
  @@x = 3
end
p A.x
# >> 3

これだけでも気持ち悪いのですが、 もっと気持ち悪い例がメタプログラミングRuby(4.1.4)で紹介されています。 ちょっと変更して掲載すると

@@v = 1 # !> class variable access from toplevel
class MyClass
  @@v = 2
  def v
    @@v
  end
end
@@v # => 2 # !> class variable access from toplevel
MyClass.new.v # => 2

@@v = 3 # !> class variable access from toplevel
MyClass.new.v # => 3

トップレベルで定義するとwarningが出ます。
こんな使い方はしないはずですが global変数のようです。
 

一応、global変数とは違う例を載せておきます。下記だとエラーとなります。

class MyClass
  @@v = 2
end
@@v # !> class variable access from toplevel
# ~> -:4:in `<main>': uninitialized class variable @@v in Object (NameError)

こんな動きになるのは、

クラス変数がクラスに属していないからだ。
クラスではなく、クラス階層に属しているのだ。
@@vはmainのコンテキストで定義されているので、mainのクラスであるObjectに属していることになる。
それから、Objectのすべての子孫にも属しているかだ。
結局はみんなが同じクラス変数を共有しているというわけだ。

なるほど。試してみます。

class MyClass
  @@v = 2
end
MyClass.class_variables
=> [:v]
>> Object.class_variables
=> []
@@v = 1
class MyClass; end
>> MyClass.class_variables
=> [:v]
>> Object.class_variables
=> [:v]

これは、恐ろしい。

多くのRubyistはクラス変数を使わずにクラスインスタンス変数を使っている。

使わないに越したことはないですね。

rubyのException

gold対策。
 
rubyのクラスを階層構造で表示 - rochefort's blog

簡単に解説すると、Treeってクラスに
Module.constantsで取得したクラス毎に
そのクラスと親のクラスを保有するTreeNodeDataクラスのインスタンスとして突っ込んで行って
to_sで階層化して出力するスクリプトです。
(あんまりきれいではない)
 
このスクリプトの実行部分を下記のようにすると
Exception以下のクラス一覧が取り出せます。

  tree = Tree.new
  Module.constants.each do |elm|
    const = eval(elm.to_s)
    if const.respond_to?(:superclass) && const.ancestors.include?(Exception)
      if const == Exception
        tree << [const, nil]
      else
        tree << [const, const.superclass]
      end
    end
  end

  puts tree

結果

Exception
  NoMemoryError
  ScriptError
    LoadError
    NotImplementedError
    SyntaxError
  SecurityError
  SignalException
    Interrupt
  StandardError
    ArgumentError
    EncodingError
    FiberError
    IOError
      EOFError
    IndexError
      KeyError
      StopIteration
    LocalJumpError
    NameError
      NoMethodError
    RangeError
      FloatDomainError
    RegexpError
    RuntimeError
    StringScanner::Error
    SystemCallError
    ThreadError
    TypeError
    ZeroDivisionError
  SystemExit
  SystemStackError

たくさんあるのですが、重要なところ(出題されそうなところ)としては
下記ぐらいのようです。

Exception
  ScriptError
    SyntaxError
  SignalException
  StandardError
    ArgumentError
    NameError
      NoMethodError
    RuntimeError
    ZeroDivisionError
  SystemExit

個人的に理解していなかったのは、
NoMethodErrorがNameErrorのサブクラスというところ。