Ruby Tips!

RubyのTipsを紹介します

Marshalで配列やハッシュをディープコピーする

Object#dupObject#cloneで配列やハッシュを複製すると、配列やハッシュ自体は複製されるがその要素までは複製されない。これはシャローコピー(浅いコピー)と呼ばれる。

以下は配列を複製した時に、元の配列の要素を破壊的に変更すると、複製した配列にも影響が及ぶ例である。

a = ["foo", "bar", "baz"]
b = a.dup
a[0].upcase!
p a #=> ["FOO", "bar", "baz"]
p b #=> ["FOO", "bar", "baz"]

場合によってはシャローコピーでは都合が悪く、配列やハッシュの要素も含めてこれらのオブジェクトを完全に複製したい場合があるかもしれない。

そのような複製はディープコピー(深いコピー)と呼ばれ、RubyではmarshalライブラリのMarshal.dumpMarshal.loadを使うのが簡単である。

以下のコードは上記のコードをmarshalライブラリによるディープコピーで書き換えた例である。これなら元の配列の要素を破壊的に変更しても複製した配列に影響はない。

require 'marshal'

a = ["foo", "bar", "baz"]
b = Marshal.load(Marshal.dump(a))
a[0].upcase!
p a #=> ["FOO", "bar", "baz"]
p b #=> ["foo", "bar", "baz"]

Rubyのリテラルについて

数値

数字を並べると数値となる。

1234
-1234

数値にはアンダーバーを含めることができる。

1_000_000

接頭詞を付けると10進数以外の数値も表記できる。

0b1010
0o7070
0d9090
0xF0F0

小数点をつけると数値は小数となる。

0.1

小数は仮数部e指数部と表記する方法もある。

2e-1

シンボル

:ではじまる英数字はシンボルとなる。

:foo

空白を含むシンボルは以下のように書ける。

:"foo bar"
%s(foo bar)

文字列

''""で囲った文字は文字列となる。

'foo'
"bar"

%q%Qを使う方法もある。

%q{foo}
%q(bar)

ヒアドキュメントを使えば複数行の文字列を簡単に書ける。

str = <<STR
foo
bar
buz
STR

正規表現

/または%r正規表現が書ける。

/foo/
%r(bar)

配列

[]で配列が書ける。

[1, 2, 3]

また文字列の配列は%wまたは%Wで書ける。

%w(a b c)
%W(d e f)

ハッシュ

{}でハッシュが書ける。中には=>でキーと値の対を書く。

{:a => 1, :b => 2, :c => 3}

Ruby 1.8では=>の代わりに,が使える。

{:a, 1, :b, 2, :c, 3}

Ruby 1.9では:でシンボルがキーのハッシュを簡単に表現できる。

{a:1, b:2, c:3}

コマンド出力

バッククオートで囲った文字はコマンドとして実行される。

`echo foo`

%xを使う方法もある。

%x{echo foo}

Proc

Ruby 1.9ではlambda->で書くことができる。

proc = ->(x, y){ x + y }
p proc.call(1, 2) #=> 3

fileutilsでファイルの操作のドライランをする

ファイル操作を行うfileutilsライブラリのメソッドは、オプションを指定することで、実際の操作は行わず、何を行うのかを出力(ドライラン)することができる。

:noopは実際の操作は行わないようにするオプションで、:verboseは何を行うのかUNIXコマンドの形式で出力するようにするオプションだ。

以下は実際にfileutilsでファイルの操作をドライランする例である。

require 'fileutils'

FileUtils.mkdir('/foo', :mode => 0755, :noop => true, :verbose => true) # mkdir -m 755 /foo
FileUtils.chmod_R(0777, '/foo', :noop => true, :verbose => true) # chmod -R 777 /foo
FileUtils.rm_rf('/foo', :noop => true, :verbose => true) # rm -rf /foo

ハッシュのキーになるクラスを定義する

あるクラスのオブジェクトがハッシュのキーになるためには、Object#hashObject#eql?が適切に再定義されていなければならない。ハッシュの内部において、Object#hashは値を格納するためのハッシュ値の計算に、Object#eql?はキーの同一性判定に使われる。

Object#eql?はオブジェクトが同一の場合に真を返し、オブジェクトが同一でない場合に偽を返すように再定義する。またObject#hashは整数を返し、かつObject#eql?が真を返す時は、同一の値を返すように再定義しなければならない。

以下は2つの数値を保持するクラスClazzを、ハッシュのキーになれるように実装した例である。ハッシュ値の計算には数値の排他的論理和を用いている。

class Clazz
  attr_accessor :a, :b

  def initialize(a, b)
    @a, @b = a, b
  end

  def hash
    @a ^ @b
  end

  def eql?(other)
    @a == other.a and @b == other.b
  end
end

hash = {}

hash[Clazz.new(1, 2)] = 'a'
hash[Clazz.new(2, 3)] = 'b'
hash[Clazz.new(3, 4)] = 'c'

p hash[Clazz.new(2, 1)] #=> nil
p hash[Clazz.new(1, 2)] #=> "a"
p hash[Clazz.new(2, 3)] #=> "b"
p hash[Clazz.new(3, 4)] #=> "c"

ディレクトリを作成する

ディレクトリを作成するならDir.mkdirを使う。引数には作成するディレクトリと、モードを指定する。

Dir.mkdir('foo', 0755)

filetutilsライブラリのFileUtils.mkdir_pを使えば深い階層のディレクトリや、複数のディレクトリをまとめて作成することができる。

require 'fileutils'

FileUtils.mkdir_p('foo/bar/baz', :mode => 755)
FileUtils.mkdir_p(['foo', 'bar', 'buz'], :mode => 755)

新しいファイルを作成する

openのフラグにwまたはaを指定してファイルをオープンすれば新しいファイルを作成することができる。

open('foo.txt', 'w'){|f|
  f.puts 'hoge' 
}

新しい空のファイルを作成したいのであれば、fileutilsライブラリのFileUtils.touchも利用できる。

require 'fileutils'

FileUtils.touch('foo.txt')

Rubyにおける多重継承について

Rubyでは複数のクラスを継承すること(多重継承)はできない。しかし複数のモジュールをインクルード(Mix-in)することはできる。RubyにおいてはMix-inこそ他の言語の多重継承に対応する機能である。以下に例を示す。

module A
  def a; end
end

module B
  def b; end
end

class C
  include A
  include B
end

obj = C.new
obj.a # モジュールAのメソッドが呼べる
obj.b # モジュールBのメソッドも呼べる