Marshalで配列やハッシュをディープコピーする
Object#dup
やObject#clone
で配列やハッシュを複製すると、配列やハッシュ自体は複製されるがその要素までは複製されない。これはシャローコピー(浅いコピー)と呼ばれる。
以下は配列を複製した時に、元の配列の要素を破壊的に変更すると、複製した配列にも影響が及ぶ例である。
a = ["foo", "bar", "baz"] b = a.dup a[0].upcase! p a #=> ["FOO", "bar", "baz"] p b #=> ["FOO", "bar", "baz"]
場合によってはシャローコピーでは都合が悪く、配列やハッシュの要素も含めてこれらのオブジェクトを完全に複製したい場合があるかもしれない。
そのような複製はディープコピー(深いコピー)と呼ばれ、RubyではmarshalライブラリのMarshal.dump
とMarshal.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
配列
[]
で配列が書ける。
[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}
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#hash
とObject#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')