# 7. 組み込みライブラリ

- 7.1 `instance_methods(true)`は何を返しますか
- 7.2 `rand`がいつも同じ乱数列を出しますが
- 7.3 0から51の中から重複のない5つをランダムに選ぶにはどうしますか
- 7.4 `Fixnum`、`Symbol`、`true`、`nil`、`false`が即値だということですが、参照との違いは何ですか
- 7.5 `nil`と`false`はどう違いますか
- 7.6 ファイルを読み込んで書き換えても変化しません
- 7.7 同じ名前のファイルに書き戻したいのですが
- 7.8 ファイルに書き込んでそのファイルをコピーしましたが、全部コピーされません
- 7.9 パイプで`less`に文字列を渡したのですが、表示されません
- 7.10 参照されない`File`オブジェクトはどうなりますか
- 7.11 ファイルを`close`しないのは気持ちが悪いのですが
- 7.12 ファイルを時間の新しい順にソートしたいのですが
- 7.13 ファイル中の単語の出現頻度を調べたいのですが
- 7.14 条件に文字列を使ったとき、文字列が空("")の時にも`true`になります
- 7.15 英語文字列の配列を辞書順にソートしたいのですが
- 7.16 `"abcd"[0]`は、何を返しますか
- 7.17 タブをスペースに展開したいのですが
- 7.18 バックスラッシュをエスケープするにはどうしますか
- 7.19 `sub`と`sub!`はどう違うのですか
- 7.20 \Zのマッチする場所はどこですか
- 7.21 範囲オブジェクトのコンストラクタ`..`と`...`はどう違いますか
- 7.22 関数ポインタはありますか
- 7.23 スレッドとプロセスのフォークはどう使い分けるのですか
- 7.24 Marshalの使い方を教えてください
- 7.25 例外処理はありますか
- 7.26 `trap`はどのように使いますか
- 7.27 ファイルの行数を数えたいのですが
- 7.28 配列からハッシュへの変換はどうすればできますか
- 7.29 文字列からArrayを作るのは %w(...) でできますが、同じように文字列からHashを作るにはどうすればよいですか
- 7.30 例外 NameError が捕捉できません
- 7.31 succがあってprevがないのはなぜですか

## 7.1 `instance_methods(true)`は何を返しますか

`klass`.instance_methods は、あるクラス(またはモジュール) `klass` で定義されたインスタンスメソッドだけを返しますが、 `klass`.instance_methods(true) は、 スーパークラスから引き継いだものも含めてすべてのインスタンスメソッドを返します。 (ただし、public メソッドのみ)

private_instance_methods、 protected_instance_methods の引数の意味も同様です。

*1

## 7.2 `rand`がいつも同じ乱数列を出しますが

ruby 1.4.2以前では`rand`は、プログラムが実行される度に同じ乱数列を生成します。 異なる乱数列を生成させるためには、`srand`で毎回異なる乱数の 種を与えてやる必要があります。srandを引数なしで呼ぶと、その時の 時間を種にしますので、異なる乱数列を生成させることができます。

## 7.3 0から51の中から重複のない5つをランダムに選ぶにはどうしますか

次のメソッドは、0からnまでの数の中からm個をランダムに選んだ配列を返します。

    def sample(n, m)
      if m.zero?
        []
      else
        s = sample(n-1, m-1)
        t = rand(n+1)
        s.concat s.include?(t) ? [n] : [t]
      end
    end

再帰形でなく書けば、次のとおりです。

    def sample(n, m)
      s = []
      ((n-m)...n).each do |j|
        t = rand(j+2)
        s.concat s.include?(t) ? [j+1] : [t]
      end
      s
    end

## 7.4 Fixnum、Symbol、`true`、`nil`、`false`が即値だということですが、参照との違いは何ですか

特異メソッドを定義できないという制限があります。 *2

また、同じ数を表わすFixnumのインスタンスは常に同じものになります ので、インスタンス変数を定義した場合には、それも同じものを示すことにな ります。

## 7.5 `nil`と`false`はどう違いますか

持っているメソッドの違いは、`nil.methods` - `false.methods`と `false.methods` - `nil.methods`を表示してください。

メソッドが真偽を返す時は、`true`、`false`を、そうでない 時は値か`nil`を返すようにすることが好まれます。

`?`のつくメソッドは、真偽を返すのが一般的ですが、 そうでないものもあります(組み込みでは nonzero? のみ)。

## 7.6 ファイルを読み込んで書き換えても変化しません

    open("example", "r+").readlines.each_with_index{|l, i|
      l[0,0] = (i+1).to_s + ": "}

とやっても、`example`に行番号がつきません。 ファイルを書き換えているのではなく、`readlines`で読み込んだ文字列を 変えているだけです。ファイルに書き戻してやらなければいけません。

    io = open("example", "r+")
    ary = io.readlines
    ary.each_with_index{|l, i| l[0,0] = (i+1).to_s + ": "}
    io.rewind
    io.print ary
    io.close

この例の場合、ファイルサイズは増える方向なので問題ないのですが ファイルサイズが小さくなるような変更に対しては

    io.flush
    io.truncate(io.pos)

を io.close の直前に実行する必要があります。

## 7.7 同じ名前のファイルに書き戻したいのですが

コマンドラインオプションの`-i`、もしくは、組み込み変数`$-i` に""を指定することにより、同じ名前のファイルに書き戻すことができます。

上の問題は、次のように書くことができます。

    $ ruby -i -ne 'print "#$.: #$_"' example

元のファイルを残しておきたければ、`-i.bak`などとしてください。

## 7.8 ファイルに書き込んでそのファイルをコピーしましたが、全部コピーされません

    open('file', 'w').print "This is a file.\n"
    system 'cp file copy'

とやったのでは、コピーする時に`file`に内容がフラッシュされて いません。きちんと`close`してからコピーしましょう。

    f = open('file', 'w')
    f.print "This is a file.\n"
    f.close
    system "cp file copy"

## 7.9 パイプで`less`に文字列を渡したのですが、表示されません

    f = open '|less', 'w'
    f.print "abc\n"

とやっても、直ちに終了してしまい、`less`で眺めることができません。 `close`してやると、そこで`less`の終了を待ちます。

    f = open '|less', 'w'
    f.print "abc\n"
    f.close

最初の行は、`f = IO.popen 'less', 'w'`としても同じ結果となります。

## 7.10 参照されないFileオブジェクトはどうなりますか

`open("file").read`というように参照されないFile オブジェクトは、次のガーベッジコレクションで`close`されて 捨てられます。

## 7.11 ファイルを`close`しないのは気持ちが悪いのですが

参照されなくなったFileオブジェクトは、GCで自動的にクローズ されますが、明示的にクローズするには、次の4つの構文から選んで下さい (コードの長さ順に並べました)。

1. ``

       File.foreach('filename') {|line| print line }

2. ``

       File.readlines('filename').each {|line| print line }

3. ``

       File.open('filename') {|f|
         f.each {|line| print line }
       }

4. ``

       begin
         f = File.open('filename')
         f.each {|line| print line }
       ensure
         f.close if f
       end

## 7.12 ファイルを時間の新しい順にソートしたいのですが

    Dir.glob("*").collect{|f| [File.mtime(f), f] }.
            sort{|a,b| b[0]<=>a[0] }.collect{|e| e[1] }

とすると、カレントディレクトリの"."、".."以外のファイルを更新時間の 新しい順にソートした配列を返します。更新時間の古い順にソートする なら、sortの後ろのブロックはなしにしても、いいですね。

    Dir.glob("*").sort{|a,b| File.mtime(b)<=>File.mtime(a)}

でもソートすることができますが、比較する度にファイルにアクセスして 更新時間を調べますので、ソートするのに時間がかかります。

この問題を解決するもう一つの方法に個々のファイルの更新時間を キャッシュするオブジェクトを用意する方法があります。

    cache = {}
    def cache.mtime(x)
      self[x] ||= File.mtime(x)
    end
    Dir.glob("*").sort{|a,b| cache.mtime(b) <=> cache.mtime(a)}
    cache = nil

また ruby 1.7 では前者の例を簡単に実行するためのメソッド Enumerable#sort_by が追加されています。

    Dir.glob("*").sort_by {|f| File.mtime(f)}.reverse

## 7.13 ファイル中の単語の出現頻度を調べたいのですが

ハッシュのデフォルト値に0を指定して、次のようにすることができます。

    freq = Hash.new(0)
    open("file").read.scan(/\w+/){|w| freq[w] += 1}
    freq.keys.sort.each {|k| print k, "--", freq[k], "\n"}

## 7.14 条件に文字列を使ったとき、文字列が空("")の時にも`true`になります

Rubyでは、`nil`と`false`だけが偽で、それ以外はすべて真に なります。文字列が空かどうかを知るには、""と比較、`empty?`を使う、 `length`を`0`と比較するなどの方法があります。

## 7.15 英語文字列の配列を辞書順にソートしたいのですが

    ary.collect{|f| [f.downcase, f]}.sort.collect{|e| e[1]}

とします。`downcase`で等しくなった場合に、元の文字列で比較を行うのが tipsです。

## 7.16 `"abcd"[0]`は、何を返しますか

文字aのコード97(Fixnum)を返します。これが文字aと一致するかどうか 調べるには、?aと比較します。

## 7.17 タブをスペースに展開したいのですが

以下のような定石があります。

    # 非破壊的
    def expand_tab( str )
      str.gsub(/([^\t]{8})|([^\t]*)\t/n) { [$+].pack("A8") }
    end

    # 破壊的
    def expand_tab!( str )
      1 while str.sub!(/(^[^\t]*)\t(\t*)/) { $1 + ' ' * (8-$1.size%8+8*$2.size) }
    end

    # 破壊的 (2)
    def expand_tab!( str )
      1 while str.sub!(/\t(\t*)/) {' ' * (8-$~.begin(0)%8+8*$1.size) }
    end

## 7.18 バックスラッシュをエスケープするにはどうしますか

正規表現の場合は `Regexp.quote('\\')`でエスケープできます。

`gsub`を使う場合には、`gsub(/\\/, '\\\\')`では、置換文字列が 構文解析で一度'\\'に変換され、実際に置き換えるときにもう一度'\'と 解釈されるので、 `gsub(/\\/, '\\\\\\')`とする必要があります。\&がマッチ文字列を あらわすことを使えば、`gsub(/\\/,'\&\&')`と書けます。

`gsub(/\\/){'\\\\'}`とブロックを使う形にすれば、エスケープが1回しか 解釈されませんので、求める結果が得られます。

## 7.19 `sub`と`sub!`はどう違うのですか

`sub`の場合はレシーバの状態は変化しません。文字列のコピーが 作られ、それに置換がほどこされて(置換が必要なければそのまま)返されます。

`sub!`ではレシーバそのものが変更されます。変更がない時には `nil`が返されます。

`sub!`のようにレシーバの状態を変化させるメソッドを 破壊的メソッドと 呼びます。Rubyでは同名のメソッドで破壊的なものとそうでないものがある場 合、破壊的なメソッドには慣例的に`!`をつけます。

    def foo(str)
      str = str.sub(/foo/, "baz")
    end

    obj = "foo"
    foo(obj)
    print obj
    #=> "foo"

    def foo(str)
      str = str.sub!(/foo/, "baz")
    end

    foo(obj)
    print obj
    #=> "baz"

`sub!`のように破壊的なメソッドは予期しない効果をもたらすことがある ので、使用する場合は十分注意してください。

## 7.20 \Zのマッチする場所はどこですか

\Zは、文字列の最後の文字が\nでない時は文字列の末尾に、 \nのときはこの改行の前にマッチします。

\n に関らず、文字列の最後にマッチさせたい場合は \z を使います。

## 7.21 範囲オブジェクトのコンストラクタ`..`と`...`はどう違いますか

`..`は終端を含み、`...`は終端を含みません。

## 7.22 関数ポインタはありますか

`Proc.new`、`proc`、`lambda`でProcオブジェクトを作れば、 関数ポインタのような働きをさせることができます。

また、MethodオブジェクトやUnboundMethodオブジェクトも関数 ポインタに近いものです。

## 7.23 スレッドとプロセスのフォークはどう使い分けるのですか

スレッドとプロセスのフォークにはそれぞれ以下のような特徴があります。

- 違うプロセスはメモリ空間を共有しない
- どちらも切り替わるタイミングは不定
- RubyスレッドはどんなOSでも動く
- Rubyスレッドは、なんらかの理由でブロックすると全体が止まる (non preemptive)
- Rubyスレッドはスラッシング (※) が起きない

※ スラッシングとは、プロセスやスレッドが多くなりすぎたときに、 切り替え処理に時間がかかりすぎて本来の処理が行われなくなってしまう ことを言います。

一般に、プロセスフォークとスレッドは混ぜて使うべきではありません。

またRubyのスレッドはタイムシェアリング方式なので、スレッドを使う ことによって処理が速くなることはまずありません。またユーザレベル スレッドなのでマルチプロセッサの恩恵も受けられません。

## 7.24 Marshalの使い方を教えてください

オブジェクトをバイト列に変換する (シリアライズ serialize する) ための ものです。オブジェクトをファイルに保存しておいて後から復活させたり、 ネットワーク経由で転送することができるようになります。例えばオブ ジェクトobjをバイト列にするには

    Marshal.dump(obj)

とします。このメソッドは文字列を返すので、次のように普通にファイルを 使って書き込めます。

    File.open('filename', 'w') {|f|
        f.write Marshal.dump(obj)
    }

このようにバイト列化したオブジェクトをファイルに書き込むことはよく あるので、以下のような簡約表現も用意されています。

    Marshal.dump(obj, io)

ioには書き込み可能なIOオブジェクトです。またこの形式だと大きなオブ ジェクトをバイト列化するときでも巨大な文字列を作らずに済みます。

一方、バイト列化したオブジェクトを再生するには次のようにします。 まずは文字列から戻す場合です。

    obj = Marshal.load(str)

以下はIOオブジェクトから直接戻す場合です。

    obj = Marshal.load(io)

## 7.25 例外処理はありますか

あります。次のような構文を使います。

    begin
      (例外が発生しそうな処理)
    rescue (例外クラス)
      (例外が発生した場合の処理)
    else
      (例外が発生しなかった場合の処理)
    ensure
      (必ず実行したい処理)
    end

`begin`節で例外が発生すると`rescue`節が実行されます。 例外が発生しなければ`else`が実行されます。 `ensure`節は例外が発生してもしなくても必ず実行されます。`rescue`, `else`,`ensure`節はそれぞれ省略できます。 `rescue`の後ろに例外クラスが 指定されなかった場合は StandardErrorが指定されたものとみなされ、StandardErrorの サブクラスである例外が捕捉されます。

この式の値は、`ensure` 節を実行する直前の値です。

最後に起こった例外はグローバル変数`$!`により参照できます。 発生した例外の種類は`$!.class`により調べることができます。

## 7.26 `trap`はどのように使いますか

以下により、シグナル SIGPIPE が発生するとブロックが実行されます (そして、例外が発生します)。

    trap("PIPE") {raise "SIGPIPE"}

## 7.27 ファイルの行数を数えたいのですが

ファイルの最後にも改行があるものと仮定すれば、次の方法が一番簡単でしょう。

    open("filename").read.count("\n")

## 7.28 配列からハッシュへの変換はどうすればできますか

array という配列があった場合、

    h = Hash[*array]

とすれば、array の奇数番目の値をキー、偶数番目の値を値とした h というハッシュが作られます。このとき array の要素は偶数個 でなければいけません。

    array = [1,2,3,4]
    h = Hash[*array]
    p h

    => {1=>2, 3=>4}

なお、arrayの前の「*」は、メソッド呼び出しのところで紹介されている、 引数の展開用の記号です。

## 7.29 文字列からArrayを作るのは %w(...) でできますが、同じように文字列からHashを作るにはどうすればよいですか

Arrayのように直接Hashを作るリテラルはありません。 そのかわり、

    p h = Hash[*%w(1 foo 2 bar)]

    => {"1"=>"foo", "2"=>"bar"}

などと、いったんArrayを作るようにすれば、そこから簡単にHashを作れます。 (ただし、この場合ハッシュのキーと値は文字列に限定されます)

## 7.30 例外 NameError が捕捉できません

以下のコードは、バージョン 1.4 以前には rescue 節がちゃんと実行されて いましたが、バージョン 1.6 ではできなくなっています。

    begin
      foo
    rescue
      puts "TRAP : #$!"
    end

    -:2: undefined local variable or method `foo' for #<Object:0x401bece0> (NameError)
    ruby 1.6.7 (2002-03-20) [i586-linux]

これは、例外クラス NameError が StandardError のサブクラス ではなくなったためです(例外クラスを指定しない rescue は `StandardError` 配下の例外クラスだけを捕捉します)。 このような場合は明示的に NameError を指定する必要があります。

    begin
      foo
    rescue NameError
      puts "**TRAP** : #$!"
    end

    ruby 1.6.7 (2002-03-20) [i586-linux]
    **TRAP** : undefined local variable or method `foo' for #<Object:0x401bece0>

なお、バージョン 1.7 では NameError クラスは StandardError のサブクラスに 戻っています。どの例外がデフォルトで捕捉できるかは 例外クラス で、クラス階層を確認してください。

## 7.31 succがあってprevがないのはなぜですか

`Integer#prev`は簡単に定義できます。しかし`succ`ほど有用とは思えません。

一方`String#prev`はうまく定義できません。 例えば

    '09'.succ == '9'.succ #=> true

というように、succ前と後の文字列が一対一対応でない場合があるため、 `str.prev.succ == str.succ.prev`が必ずしも成立しないからです。

*1 この項がFAQなのは引数の意味が昔リファレンスに載っていなかったためです
*2 ruby 1.6 feature: true/false/nil に対しては version 1.6 からは定 義できます。

links: [Enumerable] [FAQ::メソッド] [FAQ::組み込みライブラリ] [File] [Fixnum] [Method] [Module] [NameError] [Numeric] [StandardError] [Symbol] [UnboundMethod] [ruby 1.6 feature] [組み込みクラス／モジュール／例外クラス]
reverse links: [FAQ::クラス、モジュール] [FAQ::変数、定数、引数] [Ruby FAQ]