7. 組込みライブラリ

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

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

private_instance_methodsprotected_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 Fixnumtruenilfalseが即値だということですが、参照との違いは何ですか

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

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

7.5 nilfalseはどう違いますか

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

メソッドが真偽を返す時は、truefalseを、そうでない 時は値か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では、nilfalseだけが偽で、それ以外はすべて真に なります。文字列が空かどうかを知るには、""と比較、empty?を使う、 length0と比較するなどの方法があります。

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 subsub!はどう違うのですか

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.newproclambdaでProcオブジェクトを作れば、 関数ポインタのような働きをさせることができます。

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

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

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

一般に、プロセスフォークとスレッドを混ぜて使うのはよくないようです。

また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 例外処理はありますか

他の先進的な言語と同様にRubyも例外処理をサポートします。

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

begin節で例外が発生するとrescue節が実行されます。 例外が発生しなければelseが実行されます。 ensure節は例外が発生してもしなくても必ず実行されます。rescue, else,ensure節はそれぞれ省略できます。 rescureの後ろに例外クラスが 指定されなかった場合は 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 が捕捉できません

以下は、昔(version 1.4以前)は rescue 節がちゃんと実行されていましたが、 version 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]

これは、例外クラス NameErrorStandardError のサブクラス ではなくなったためです(例外クラスを指定しない 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>

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

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

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

String#prevstr.prev.succ == str.succ.prevとすると

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

のような場合に一意に定義できないという問題もあります。


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