2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Ruby覚え書き (組み込みクラス/モジュール)

Last updated at Posted at 2015-08-12

概要

Ruby覚え書き で保存時に502エラーが出るようになったので分離しました。常時更新です。

数値(Numeric)

  • 全ての数値クラスはNumericクラスから派生している

    • Numeric
      • Integer : 整数
        • Fixnum : 小さな整数
        • Bignum : 大きな整数(環境依存)
      • Float : 浮動小数点数
      • Rational : 有理数
      • Complex : 複素数
  • 整数、符号付き整数 : FixnumまたはBignum : 365, -365, 365_000, 0d365

  • 16進数 : FixnumまたはBignum : 0xFFF

  • 2進数 : FixnumまたはBignum : 0b0101

  • 8進数 : FixnumまたはBignum : 0555, 0o555

  • 浮動小数点数 : Float : 365.65, 36565e-2, 3.65655+2

  • 述語メソッド

    • Numeric#zero? : ゼロかどうか
    • Numeric#nonzero? : ゼロでないかどうか。nilかselfを返す
    • Numeric#integer? : Integerのインスタンスかどうか
    • Numeric#real? : 実数かどうか
  • 算術演算子

    • 10 % 3 : 余り
    • 10 ** 3 : べき乗
  • リテラル表記 (Ruby2.1以降)

    • 有理数 : 6/4r #=> 3/2
    • 複素数 : (1+1i)**4 #=> (-4+0i)
  • 0除算

    • ZeroDivisionErrorが発生する
  • 宇宙船演算子(スペースシップ演算子)

    • <=>
      • 左辺が右辺より小さければ負
      • 等しければ0
      • 大きければ性の数を返す
    • 主にソートのために使われる
  • Enumerable#sort

    • ブロックでソートソート方法をカスタマイズできる
宇宙船演算子
### 文字列の配列を要素の長さでsort
[1] pry(main)> %w( abc de f ).sort {|a,b|
[1] pry(main)*   a.length <=> b.length
[1] pry(main)* }
=> ["f", "de", "abc"]
  • 丸め操作

    • ceil : 自身と等しいか、自身より大きい整数のうち最小のものを返す
    • floor : 自身と等しいか、自身より小さい整数のうち最小のものを返す
    • round : 四捨五入
    • truncate : 自身と0との間で自身に最も近い整数を返す
  • 繰り返し

    • Numeric#step
      • 自身から第一引数に与えられた上限の数値まで繰り返す
step
[1] pry(main)> 3.step 5 do |num|
[1] pry(main)*   puts num
[1] pry(main)* end
3
4
5
=> 3
[2] pry(main)> 1.2.step 2.0, 0.2 do |num|
[2] pry(main)*   puts num
[2] pry(main)* end
1.2
1.4
1.6
1.8
2.0
=> 1.2

Integer

  • 述語メソッド
    • Integer#odd? : 奇数か
    • Integer#even? : 偶数か
    • Integer#next : 次の数を返す
    • Integer#succ : 次の数を返す
    • Integer#pred : 前の数を返す
    • Integer#to_s : 文字列に変換した値を返す
    • Integer#to_i : レシーバを整数として表現した結果を返す
      • 整数以外のオブジェクトを整数に変換するときはKernel.#Integerを用いる
    • Integer#chr : 数値からそれに対応する文字を得る。256以上の数値はRangeError
    • 繰り返し
      • Integer#upto : 与えられた値になる前自身を繰り返しインクリメント
      • Integer#downto : uptoの逆
      • Integer#times : n回繰り返したいとき
      • ブロックが与えられなければEnumeratorというオブジェクトを返す
Integer
[1] pry(main)> 2.next
=> 3
[2] pry(main)> 2.succ
=> 3
[3] pry(main)> 30.to_s
=> "30"
[4] pry(main)> 30.to_s(2)
=> "11110"
[5] pry(main)> 30.to_s(8)
=> "36"
[6] pry(main)> 30.to_s(16)
=> "1e"

### 数値に対応する文字列が返る
[1] pry(main)> (48..55).map {|n| n.chr }
=> ["0", "1", "2", "3", "4", "5", "6", "7"]

[1] pry(main)> '123'.to_i
=> 123
[2] pry(main)> Time.now.to_i
=> 1436136170

### 整数以外のオブジェクトを整数にかえる
[1] pry(main)> Integer(3.14)
=> 3

### 16進数はto_iでは変換できない
[2] pry(main)> '0xFF'.to_i
=> 0
[3] pry(main)> Integer('0xFF')
=> 255

### 繰り返し
[1] pry(main)> 1.upto 3 do |num|
[1] pry(main)*   puts num
[1] pry(main)* end
1
2
3
=> 1
[2] pry(main)> 3.downto 1 do |num|
[2] pry(main)*   puts num
[2] pry(main)* end
3
2
1
=> 3
[3] pry(main)> 3.times do |num|
[3] pry(main)*   puts num
[3] pry(main)* end
0
1
2
=> 3

Float

  • 浮動小数点を表現するクラス
    • 精度は環境依存
  • 0除算
    • NaNまたはInfinityが返る

Rational

  • 有理数を表現する
  • Kernel.#Rational
    • Rationalオブジェクトを得ることができる
  • Numeric#quo
    • 戻り値をRationalオブジェクトとして得ることができる
Rational
### 分数
[1] pry(main)> r = Rational(1,3)
=> (1/3)

### 分母
[2] pry(main)> r.denominator
=> 3

### 分子
[3] pry(main)> r.numerator
=> 1

### レシーバを浮動小数点数に変換
[4] pry(main)> r.to_f
=> 0.3333333333333333

### 戻り値をRationalオブジェクトで得る
[5] pry(main)> r = 3.quo(10)
=> (3/10)

### レシーバをRationalオブジェクトに変換
[6] pry(main)> 0.25.to_r
=> (1/4)

Complex

  • 複素数を表現する
  • Kernel.#Complexで得ることができる
complex
[1] pry(main)> c = Complex(2,3)
=> (2+3i)
[2] pry(main)> c.real
=> 2
[3] pry(main)> c.imaginary
=> 3
[4] pry(main)> Complex(2)
=> (2+0i)
[5] pry(main)> Complex('2+1i')
=> (2+1i)

### レシーバをComplexオブジェクトに変換する
[6] pry(main)> 1.3.to_c
=> (1.3+0i)
[7] pry(main)> '3+5i'.to_c
=> (3+5i)

文字列(String)

  • 基本的な振る舞い

    • String#empty? : 空文字列かどうか
    • String#length : 文字列の長さを返す(日本語も1文字とカウント)
    • String#bytesize : 文字列のバイト数を知る
    • String#include? : 引数の文字列を自身が含んでいるかを確認
    • String#start_with? : 引数の文字列から始まるかを確認
  • シングルクオート、またはダブルクオートで記述する

    • ダブルクオートの中では#{...} で式展開できる
式展開
[2] pry(main)> puts 'result:\t#{1+1}'
result:\t#{1+1}  ### 式展開なし
=> nil
[3] pry(main)> puts "result:\t#{1+1}"
result:	2  ### 式展開あり。バックスラッシュ記法でtabも入っている
=> nil
  • 演算子
    • 連結
    • 繰り返し
    • フォーマットした文字列
    • 破壊的な文字列追加
文字列処理
[1] pry(main)> 'Pine' + 'Apple'
=> "PineApple"
[2] pry(main)> 'Hi' * 3
=> "HiHiHi"
[3] pry(main)> '%04d' % 42
=> "0042"

### 破壊的な文字列追加
[4] pry(main)> str = 'Pine'
=> "Pine"
[5] pry(main)> str << 'Apple'
=> "PineApple"
  • 部分文字列の取得
部分文字列の取得
[1] pry(main)> str = 'The Answer to life, the universe, and everything: 42'
=> "The Answer to life, the universe, and everything: 42"
[2] pry(main)> str.slice(4) ### 5文字目を取得
=> "A"
[3] pry(main)> str.slice(4,6) ### 5文字目から6文字分取得
=> "Answer"
[4] pry(main)> str.slice(4..9)  ### 5文字目から10文字目までを取得
=> "Answer"
[5] pry(main)> str.slice(/[0-9]+/)
=> "42"
[6] pry(main)> str.slice(-2,2)  ### 末尾から数えて2文字目から2文字分取得
=> "42"

### 破壊的に取得
[7] pry(main)> str.slice!(-2,2)
=> "42"

### 戻り値として返した部分はレシーバから削除される
[8] pry(main)> str
=> "The Answer to life, the universe, and everything: "
  • 文字列の整形

    • String#strip : 文字列の前後の空白文字列(tabも含む)を取り除いた文字列を返す
    • String#rstrip : 文字列の後の空白文字列(tabも含む)を取り除いた文字列を返す
    • String#lstrip : 文字列の前の空白文字列(tabも含む)を取り除いた文字列を返す
    • String#chomp : 末尾の改行コードを一つだけ取り除いた文字列を返す
    • String#chop : 文字の種類に関わらず末尾の文字列を一つだけ取り除いた文字列を返す
    • String#squeeze : 文字が連続している場合一つにまとめる
    • String#downcase : 全て小文字にする
    • String#upcase : 全て大文字にする
    • String#swapcase : 大文字と小文字を入れ替える
    • String#capitalize : 先頭の文字だけを大文字にする
    • String#sub : あるパターンに最初にマッチした文字列を置換する
    • String#gsub : あるパターンにマッチした文字列を全て置換する
    • String#reverse : 文字列を反転する
  • 配列への変換

    • String#split : 特定の文字列をセパレータとして分割した配列を返す
    • String#each_char : 文字列から文字の配列を得る (一文字ずつの配列ができる)
    • String#delete : self(メソッドのレシーバとなったオブジェクト)に含まれる文字を取り除いた文字列を返す
  • 繰り返し処理

文字列の繰り返し処理
### 文字ごとの繰り返し
[1] pry(main)> 'ブウウ'.each_char {|c| print "#{c}ー"}
ブーウーウー=> "ブウウ"

### バイトごとの繰り返し
[2] pry(main)> 'る'.each_byte do |byte|
[2] pry(main)*   puts byte
[2] pry(main)* end
227
130
139
=> "る"

### 行ごとの繰り返し
[3] pry(main)> "a\nb\nc".each_line do |line|
[3] pry(main)*   puts line
[3] pry(main)* end
a
b
c
=> "a\nb\nc"
  • ヒアドキュメント
ヒアドキュメント
### 標準的なヒアドキュメント
[1] pry(main)> str = <<EOS   ### (End Of String)
[1] pry(main)* hoge
[1] pry(main)* fuga
[1] pry(main)* EOS
=> "hoge\nfuga\n"
[2] pry(main)> puts str
hoge
fuga
=> nil

### ハイフンをつけると終端の識別子をインデントできる
[3] pry(main)> str = <<-EOS
[3] pry(main)* fuga
[3] pry(main)* haga
[3] pry(main)*  EOS  ### インデントされている
=> "fuga\nhaga\n"
[4] pry(main)> puts str
fuga
haga
=> nil

### 式展開できる
[5] pry(main)> sum = <<EOS
[5] pry(main)* #{1+1}
[5] pry(main)* EOS
=> "2\n"
[6] pry(main)> puts sum
2
=> nil

### 識別子をシングルクオートで囲むと式展開を無効にできる
[7] pry(main)> sum = <<'EOS'  ### ''で囲っている
[7] pry(main)* #{1+1}
[7] pry(main)* EOS
=> "\#{1+1}\n"
[8] pry(main)> puts sum
#{1+1}
=> nil

%記法

  • %q : 式展開とバックスラッシュ記法が無効
  • % : 式展開とバックスラッシュ記法が有効
  • %Q : 式展開とバックスラッシュ記法が有効
    • 上記はクオートを多用するときに使う
  • %w : 配列。式展開とバックスラッシュ記法が無効
  • %i : シンボル。式展開とバックスラッシュ記法が無効(ruby2.0以降)
  • %W : 配列。式展開とバックスラッシュ記法が有効
  • %I : シンボル。式展開とバックスラッシュ記法が有効
  • %r : スラッシュをエスケープする。
%記法
[1] pry(main)> weather = 'rainy'
=> "rainy"
[2] pry(main)> %q(It's \t #{weather})
=> "It's \\t \#{weather}"
[3] pry(main)> %(It's \t #{weather})
=> "It's \t rainy"
[4] pry(main)> %Q(It's \t #{weather})
=> "It's \t rainy"

[5] pry(main)> %w(Alice, Bob, Carol)
=> ["Alice,", "Bob,", "Carol"]
[6] pry(main)> %i(red, green, blue)
=> [:"red,", :"green,", :blue]

### 空白がある場合はバックスラッシュでエスケープする
[7] pry(main)> %w(foo\ bar, hoge\ hoge)
=> ["foo bar,", "hoge hoge"]

### スラッシュをエスケープする
[8] pry(main)> %r(/usr/bin \t)
=> /\/usr\/bin \t/

シンボル(Symbol)

  • 文字列に似ている
  • 先頭にコロンをつけた文字の並びはシンボルリテラル(:ruby)
    • 識別子やキーワード的な単語を表現するのに適している
      • 例) attr_accessor :length
symbol
[1] pry(main)> :ruby
=> :ruby
[2] pry(main)> :CONST
=> :CONST

### ハイフンがある場合はクオートで囲む必要がある
[3] pry(main)> :foo-bar
NameError: undefined local variable or method `bar' for main:Object
from (pry):3:in `__pry__'
[4] pry(main)> :'foo-bar'
=> :"foo-bar"

配列(Array)

  • 概要
    • 複数のオブジェクトを順番に並べたコンテナオブジェクト
    • 一つの配列に異なる型のオブジェクトを入れることができる
    • 配列の中に配列を入れて多次元配列を作ることができる
  • 生成方法
    • []
    • %w記法
    • Array.new
配列
### 生成
[1] pry(main)> %w(a b c)
=> ["a", "b", "c"]
[2] pry(main)> [1, 2, 3]
=> [1, 2, 3]
[3] pry(main)> Array.new(5, 'abc')  ### 第1引数は配列の長さ、第2引数は要素
=> ["abc", "abc", "abc", "abc", "abc"]

### Array.newで生成された配列の要素は全て同じ同じオブジェクト
[4] pry(main)> Array.new(5, 'abc')
=> ["abc", "abc", "abc", "abc", "abc"]
[5] pry(main)> array = Array.new(5, 'abc')
=> ["abc", "abc", "abc", "abc", "abc"]

### 一つの要素を変更すると他の要素も全て変更される
[6] pry(main)> array[0].reverse!
=> "cba"
[7] pry(main)> array
=> ["cba", "cba", "cba", "cba", "cba"]

### ブロックを受け取れる
[8] pry(main)> Array.new(3) {|index| index.succ}
=> [1, 2, 3]

### 異なる方のオブジェクトを入れることができる
[1] pry(main)> array = ['Alice', 1234, 1.23, nil, false]
=> ["Alice", 1234, 1.23, nil, false]
[2] pry(main)> array[0]
=> "Alice"    ### 配列の先頭の要素を参照
[3] pry(main)> array[2]
=> 1.23   ### 配列の三番目の要素を参照
[4] pry(main)> array[-1]
=> false  ### 配列の一番最後の要素を参照

### 2番目に新たな要素を代入
[6] pry(main)> array[1] = 5678
=> 5678
[7] pry(main)> array
=> ["Alice", 5678, 1.23, nil, false]

### 8番目の要素に代入
[8] pry(main)> array[7] = -1234
=> -1234
[9] pry(main)> array
=> ["Alice", 5678, 1.23, nil, false, nil, nil, -1234] ### 6,7番目はnilになる
  • 配列の基本的なメソッド
    • Kernel.#Array : 引数を配列に変換した結果を返す
    • Array#length : 配列の要素数を調べる
    • Array#empty? : 配列がからかどうかを調べる
    • Array#include? : 引数に与えたオブジェクトと等しい要素がレシーバの要素として存在するか
配列メソッド
### Kernel.#Array
[1] pry(main)> Array('Alice')
=> ["Alice"]
[2] pry(main)> Array(['Alice'])
=> ["Alice"]
[3] pry(main)> Array(nil)
=> []

### 要素数を調べる
[4] pry(main)> array = [1,2,3,4]
=> [1, 2, 3, 4]
[5] pry(main)> array.length
=> 4
[6] pry(main)> array.empty?
=> false
[7] pry(main)> [].empty?
=> true

### 引数に与えられたオブジェクトと同一の要素がレシーバの要素として存在しているか
[8] pry(main)> array.include?(4)
=> true
[11] pry(main)> array.include?(5)
=> false

### 配列の連結
[1] pry(main)> [1, 2, 3] + [4, 5]
=> [1, 2, 3, 4, 5]

### 要素を取り除いた配列
[2] pry(main)> [1, 2, 3, 3] - [2, 3]
=> [1]

### 共通の要素からなる配列
[3] pry(main)> [1, 2, 3, 3] & [2, 3]
=> [2, 3]

### n回繰り返した配列
[4] pry(main)> [1, 2, 3, 3] * 2
=> [1, 2, 3, 3, 1, 2, 3, 3]
  • 要素の取得
    • Array#[] : 配列の取得
    • Array#values_at : 複数の添字の要素を配列で返す
    • Array#first : 先頭の要素を得る
    • Array#last : 末尾の要素を得る
    • Array#sample : 自信からランダムに要素を返す
    • Array#assoc : 配列から特定のキーを持つ要素を返す
      • 配列の配列を想定している
要素の取得
[1] pry(main)> array = [4, 2, 2, 3]
=> [4, 2, 2, 3]
[2] pry(main)> array[2]
=> 2

### 開始位置と長さで指定
[3] pry(main)> array[2,2]
=> [2, 3]

### 範囲で指定
[4] pry(main)> array[2..3]
=> [2, 3]

### 添字の要素を配列で返す
[5] pry(main)> array.values_at(1)
=> [2]
[6] pry(main)> array.values_at(1,3)
=> [2, 3]

### 最初の要素を得る
[7] pry(main)> array.first
=> 4

### 末尾の要素を得る
[8] pry(main)> array.last
=> 3
[9] pry(main)> array.last(2)
=> [2, 3]

### ランダムに要素を得る
[10] pry(main)> array.sample
=> 2
[11] pry(main)> array.sample(3)
=> [4, 2, 2]

### 配列から特定のキーを持つ要素を返す
[1] pry(main)> ary = [[:foo, 4], [:bar, 2]]
=> [[:foo, 4], [:bar, 2]]
[2] pry(main)> ary.assoc(:bar)
=> [:bar, 2]
  • 要素の追加と削除
    • Array#[]= : 配列に任意の要素を追加する
    • Array#push : 配列の末尾に要素を一つ追加する
    • Array#<< : 同上
    • Array#pop : 配列の末尾から要素を一つ取り出す
    • Array#shift : 配列の先頭の要素を取り出す
    • Array#unshift : 配列の先頭に要素を追加する
    • Array#select! : 各要素に与えられたブロックを実行して、結果が真となった要素以外を取り除く
      • レシーバが更新されなければnilを返す
    • Array#reject! : 各要素に与えられたブロックを実行して、結果が真となった要素を取り除く
      • レシーバが更新されなければnilを返す
    • Array#keep_if : reject!と同じだが、レシーバが更新されなかった場合でも常にレシーバを返す
    • Array#delete_of : select!と同じだが、レシーバが更新されなかった場合でも常にレシーバを返す
    • Array#delete : 引数の値と等しい要素を削除する
      • 浮動小数点と整数は区別されない
    • Array#delete_at : 指定した添字の要素を削除する
要素の追加と削除
[1] pry(main)> array = [1, 2, 3, 4]
=> [1, 2, 3, 4]

### 1番目の要素を上書き
[2] pry(main)> array[0] = 5
=> 5
[3] pry(main)> array
=> [5, 2, 3, 4]

### 自身のサイズを上回る添字を指定すると間にはnilが入る
[4] pry(main)> array[5] = 6
=> 6
[5] pry(main)> array
=> [5, 2, 3, 4, nil, 6]

### 末尾に要素を追加
[6] pry(main)> array << 7
=> [5, 2, 3, 4, nil, 6, 7]
[7] pry(main)> array.push 8
=> [5, 2, 3, 4, nil, 6, 7, 8]

### 末尾の要素を削除
[8] pry(main)> array.pop
=> 8
[9] pry(main)> array
=> [5, 2, 3, 4, nil, 6, 7]

### 先頭の要素を削除
[10] pry(main)> array.shift
=> 5
[11] pry(main)> array
=> [2, 3, 4, nil, 6, 7]

### 先頭に要素を追加
[12] pry(main)> array.unshift 9
=> [9, 2, 3, 4, nil, 6, 7]

### 真となった要素以外を外す
[13] pry(main)> array[4]=10
=> 10
[14] pry(main)> array.select! {|v| v.even? }
=> [2, 4, 10, 6]

### 真となった要素以外を取り除く
[15] pry(main)> array.reject! {|v| v.even? }
=> []

### 引数と等しい値を削除する
[1] pry(main)> array = [1, 2, 3, 4, 2.0]
=> [1, 2, 3, 4, 2.0]
[2] pry(main)> array.delete 2
=> 2.0
[3] pry(main)> array
=> [1, 3, 4]

### 指定した添字の要素を削除する
[4] pry(main)> array.delete_at 1
=> 3
[5] pry(main)> array
=> [1, 4]
  • 配列の整形
    • Array#compact : 配列の要素からnilを取り除く
    • Array#uniq : 重複した要素を取り除く
    • Array#reverse : 自身の順序を逆にした新しい配列を返す
    • Array#flatten : 多次元配列を平にした新しい配列を返す
    • Array#sort : 配列の内容をソートする
    • Array#sort_by : ある条件によりソートする
    • Array#map : mapの結果の配列で自身を更新する
    • Array#transpose : 配列を行列と見立てて行と列の入れ替えを行う
      • 2次元配列である必要がある
      • 配列のサイズが異なっている場合は例外が発生する
    • Array#zip : 自身と引数に渡された配列から新しい配列を作る
配列の整形
### nilを取り除く
[1] pry(main)> array = [false, nil, 0, '', []]
=> [false, nil, 0, "", []]
[2] pry(main)> array.compact
=> [false, 0, "", []]

### 重複を取り除く。型変換はしない。
[3] pry(main)> array = [1,2,1,5,4,2.0]
=> [1, 2, 1, 5, 4, 2.0]
[4] pry(main)> array.uniq
=> [1, 2, 5, 4, 2.0]

### 逆順にする
[5] pry(main)> array.reverse
=> [2.0, 4, 5, 1, 2, 1]

### 多次元配列を平らにする
[6] pry(main)> array = [1, [2, 3], [4, [5, 6]]]
=> [1, [2, 3], [4, [5, 6]]]
[7] pry(main)> array.flatten
=> [1, 2, 3, 4, 5, 6]

### ソートする
[1] pry(main)> array = [1,2,1,5,4,2.0]
=> [1, 2, 1, 5, 4, 2.0]
[2] pry(main)> array.sort
=> [1, 1, 2, 2.0, 4, 5]

[3] pry(main)> array
=> ["abc", "d", "efg", "hg"]
[4] pry(main)> array.sort_by {|v| v.length}
=> ["d", "hg", "abc", "efg"]

### mapの結果
[5] pry(main)> array.map {|v| v.upcase}
=> ["ABC", "D", "EFG", "HG"]

### 行と列を入れ替える
[1] pry(main)> array = [[1, 2, 3], [4, 5, 6]]
=> [[1, 2, 3], [4, 5, 6]]
[2] pry(main)> array.transpose
=> [[1, 4], [2, 5], [3, 6]]

### 新しい配列の配列を作る
[3] pry(main)> array = [1, 'a']
=> [1, "a"]
[4] pry(main)> array.zip([2, 'b'], [3, 'c'])
=> [[1, 2, 3], ["a", "b", "c"]]
  • 二分探索
    • Array#besearch : 二分探索をする
      • ソート済みである必要がある
二分探索
### 10の次に大きな値を二分探索で探す
[1] pry(main)> array = [1, 3, 6, 12, 15, 25, 30]
=> [1, 3, 6, 12, 15, 25, 30]
[2] pry(main)> array.bsearch { |n| n > 10 }
=> 12
  • 要素の連結
    • Array#join : 要素を連結する
要素の連結
[1] pry(main)> array = [24, 'abc', 1.2]
=> [24, "abc", 1.2]
[2] pry(main)> array.join
=> "24abc1.2"
[3] pry(main)> array.join('/')
=> "24/abc/1.2"

ハッシュ(Hash)

  • いわゆる連想配列
    • 本来ハッシュは順序を持たないが、Ruby1.9以降で順序を保持することを保証するようになった
  • 添字(キー)
    • ハッシュデーブルというデータ構造で実装されている
    • キーとなるオブジェクトにはハッシュを返すhashというメソッドが実装されている必要がある
      • Objectクラスにhashメソッドが定義されているので普通は実装する必要はない
      • キーと値のひも付けはハッシュ値が用いられる
    • キーに使えるもの
      • シンボル
      • 文字列 (ミュータブルなオブジェクトだが利便性のために使われる事が多い)
      • 任意のオブジェクト
    • ミュータブルなオブジェクトはキーに向かない
      • 破壊的操作によりハッシュ値が変わる可能性があるので
      • ミュータブルなオブジェクト
        • 配列
        • ハッシュ
    • ハッシュリテラルはブレース「{}」を用いて記述する
Hash
### ハッシュリテラルの記述方法
{キー => 要素}

### ハッシュの作成
[1] pry(main)> colors = {'red' => 'ff0000', 'green' => '00ff00', 'blue' => '0000ff'}
=> {"red"=>"ff0000", "green"=>"00ff00", "blue"=>"0000ff"}

### 要素の参照
[2] pry(main)> colors['blue']
=> "0000ff"
[3] pry(main)> colors['yellow']
=> nil  ### 値が無ければnil

### 別の値を格納
[4] pry(main)> colors['green'] = '008000'
=> "008000"
[5] pry(main)> colors
=> {"red"=>"ff0000", "green"=>"008000", "blue"=>"0000ff"}
  • ハッシュキーには読み書きのしやすさからシンボルがよく使われる
Hash_Symbol
[1] pry(main)> colors = {:red => 'ff0000', :green => '00ff00', :blue => '0000ff'}
=> {:red=>"ff0000", :green=>"00ff00", :blue=>"0000ff"}
[2] pry(main)> colors[:blue]
=> "0000ff"

### Ruby1.9以降、キーがシンボルの場合は以下のように簡素にかけるようになった
[4] pry(main)> colors = {red: 'ff0000', green: '00ff00', blue: '0000ff'}
=> {:red=>"ff0000", :green=>"00ff00", :blue=>"0000ff"}

### コロンの位置が後ろ (:red -> red:)
### 「=>」が「,」になる (=> -> ,)
  • 繰り返し処理
    • Hash#each :
    • Hash#each_key :
    • Hash#each_value :
ハッシュの繰り返し
### ハッシュの繰り返し
[1] pry(main)> hash = {one: 1, two: 2}
=> {:one=>1, :two=>2}
[2] pry(main)> hash.each do | key, val|
[2] pry(main)*   puts "#{key}: #{val}"
[2] pry(main)* end
one: 1
two: 2
=> {:one=>1, :two=>2}

### keyのみ繰り返す
[3] pry(main)> hash.each_key do |key|
[3] pry(main)*   puts "#{key}"
[3] pry(main)* end
one
two
=> {:one=>1, :two=>2}

### valueのみ繰り返す
[4] pry(main)> hash.each_value do |val|
[4] pry(main)*   puts "#{val}"
[4] pry(main)* end
1
2
=> {:one=>1, :two=>2}
  • 値の更新
    • Hash#[]= :値の更新や追加を行う
    • Hash#delete : キーに対応するエントリを削除する
      • 戻り値は削除された値
Hashの値の更新
### Hashの追加
[1] pry(main)> hash = {}
=> {}
[2] pry(main)> hash[:foo] = 'bar'
=> "bar"
[3] pry(main)> hash
=> {:foo=>"bar"}

### Hashのエントリ削除
[4] pry(main)> hash.delete(:foo)
=> "bar"
[5] pry(main)> hash
=> {}


[2] pry(main)> hash = {foo: 1, bar: 2, baz: 3}
=> {:foo=>1, :bar=>2, :baz=>3}

### 値が奇数のものを出力
[3] pry(main)> hash.select {|key, val| val.odd?}
=> {:foo=>1, :baz=>3}

### 値が奇数のものを取りいて出力
[4] pry(main)> hash.reject {|key, val| val.odd?}
=> {:bar=>2}

### 値が奇数のものを残す。常にselfが返る。
[5] pry(main)> hash.keep_if {|key, val| val.odd?}
=> {:foo=>1, :baz=>3}

### 値が奇数ではないものを取り除く。常にselfが返る。
[6] pry(main)> hash.delete_if {|key, val| val.odd?}
=> {}
  • マージ
    • Hash#merge : 2つのハッシュを一つにまとめる
Hashのmerge
### マージ、キーの重複は引数として渡されたハッシュ値で上書きされる
[1] pry(main)> a = {a: 1, b: 2}
=> {:a=>1, :b=>2}
[2] pry(main)> b = {b: 3, c: 4}
=> {:b=>3, :c=>4}
[3] pry(main)> a.merge(b)
=> {:a=>1, :b=>3, :c=>4}

### 値の追加
[9] pry(main)> a
=> {:a=>1, :b=>2}
[10] pry(main)> a.update c: 3
=> {:a=>1, :b=>2, :c=>3}
[11] pry(main)> a
=> {:a=>1, :b=>2, :c=>3}
  • キー値の入れ替え
    • Hash#invert : キーと値を入れ替える
Hashinvert
### キーと値の入れ替え
[1] pry(main)> a = {a: 1, b: 2}
=> {:a=>1, :b=>2}
[2] pry(main)> a.invert
=> {1=>:a, 2=>:b}

### キーが重複した場合はどちらかの値が消える。どちらが消えるかは不定。
[3] pry(main)> a.update c: 2
=> {:a=>1, :b=>2, :c=>2}
[4] pry(main)> a.invert
=> {1=>:a, 2=>:c}
  • キーや値の取得
    • Hash#keys : レシーバのキーを配列で返す
      • 該当するキーが複数ある場合はどのキーが返るかは不定
    • Hash#values : レシーバの値を返す
    • Hash#values_at : 任意のキーの値を返す
キーや値の取得
[1] pry(main)> a = {a: 1, b: 2, c: 3}
=> {:a=>1, :b=>2, :c=>3}
[2] pry(main)> a.keys
=> [:a, :b, :c]
[3] pry(main)> a.values
=> [1, 2, 3]
[6] pry(main)> a.values_at(:b)
=> [2]
  • デフォルト値
    • 存在しないキーを添え字で参照した時の戻り値を指定できる
      • 何も指定しなければnil
    • デフォルト値は全て同一オブジェクト
      • デフォルト値に破壊的操作をすると思わぬ影響がでる可能性がある
    • ブロックで渡すことができる
    • Hash#default= : すでにあるハッシュオブジェクトにデフォルト値を設定する
    • Hash#default_proc= : デフォルト値を返す手続きオブジェクトを指定する
    • Hash#fetch : ハッシュの値を参照するメソッド
      • 値が存在しない場合の戻り値をブロックで指定できる
      • ブロックを与えなければKeyErrorが発生する
Hashデフォルト値
### デフォルト値の設定
[1] pry(main)> a = Hash.new('hoge')
=> {}
[2] pry(main)> a[':a']
=> "hoge"

### デフォルト値をブロックで渡す
[1] pry(main)> hash_time = Hash.new {|hash, key| Time.now}
=> {}
[2] pry(main)> hash_time['hoge']
=> 2015-07-11 07:55:42 +0000
[3] pry(main)> hash_time['hoge']
=> 2015-07-11 07:55:46 +0000

### すでにあるHashのデフォルト値を変更
[1] pry(main)> hash_default = {}
=> {}
[2] pry(main)> hash_default[:foo]
=> nil
[3] pry(main)> hash_default.default = 'hoge'
=> "hoge"
[4] pry(main)> hash_default[:foo]
=> "hoge"

### デフォルト値を返す手続きオブジェクトを設定
[5] pry(main)> hash_default.default_proc = ->(hash, key) {Time.now}
=> #<Proc:0x007f7bc7f68ef8@(pry):5 (lambda)>
[6] pry(main)> hash_default[:foo]
=> 2015-07-11 08:03:03 +0000
  • ハッシュの変換
    • Hash#to_a : ハッシュを配列に変換
      • Array#assocでアクセスできる
    • Hash.[] : ハッシュを作る
      • 要素が偶数の配列や、配列の配列からハッシュを作成できる
配列からハッシュを作成
### 要素が偶数の配列からハッシュを作成
[1] pry(main)> a = ['a', 1, 'b', 2]
=> ["a", 1, "b", 2]
[2] pry(main)> Hash[*a]
=> {"a"=>1, "b"=>2}

### 多次元配列からハッシュを作成
[3] pry(main)> b = [['a', 1], ['b',2]]
=> [["a", 1], ["b", 2]]
[4] pry(main)> Hash[b]
=> {"a"=>1, "b"=>2}

範囲(Range)

  • 範囲を表すオブジェクトが有る
    • 用途
      • ある値が範囲に含まれているか確かめる
      • 幅を表現する
      • 1..5
        • 1〜5を表す
      • 1...5
        • 1〜4を表す
    • 日時や文字列も指定できる
Range
[1] pry(main)> (1..5).include?(5)
=> true
[2] pry(main)> (1...5).include?(5)
=> false

### 日時の範囲。Unix timeからTimeオブジェクトを作成している
[3] pry(main)> vacation = Time.at(1343746800)..Time.at(1346425199)
=> 2012-07-31 15:00:00 +0000..2012-08-31 14:59:59 +0000
[4] pry(main)> vacation.begin ### Range#beginで始端の値を返す
=> 2012-07-31 15:00:00 +0000
[5] pry(main)> vacation.end  ### Range#endで終端の値を返す
=> 2012-08-31 14:59:59 +0000

### Range#eachで繰り返し処理ができる
[7] pry(main)> abc = ('a'..'g')
=> "a".."g"
[8] pry(main)> abc.each do |i|
[8] pry(main)*   puts i
[8] pry(main)* end
a
b
c
d
e
f
g
=> "a".."g"

正規表現(Regexp)

  • 用途
    • ある文字列が特定の文字列を含んでいるか確認
    • 文字列から特定のパターンにマッチする箇所を探す
  • Regexp#===
    • あるパターンに文字列がマッチするかを知りたいときに使う
    • 戻り値としてtrue or falseを期待する場合はこちらを使う
  • Regexp#=~
    • 引数の文字列が正規表現にマッチした位置を返す
    • マッチしなければnilを返す
    • 文字列がマッチするかを確かめる述語メソッドとして使われることが多い
  • Regexp#match
    • マッチしなければnilを返す
    • マッチした場合はMatchDataオブジェクトを返す
パターンマッチ

### 0-9が含まれないのでfalse
[1] pry(main)> /[0-9]/ === 'ruby'
=> false

### 0-9が含まれるのでtrue
[2] pry(main)> /[0-9]/ === 'ruby5'
=> true

### 0-9がマッチした位置を返す
[3] pry(main)> /[0-9]/ =~ 'ruby5'
=> 4

### マッチしなければnilを返す
[4] pry(main)> /[0-9]/ =~ 'ruby'
=> nil

### MatchDataオブジェクトを返す
[5] pry(main)> str = 'ruby5'
=> "ruby5"
[6] pry(main)> if matched = /[0-9]/.match(str)
[6] pry(main)*   p matched
[6] pry(main)* end
#<MatchData "5">
=> #<MatchData "5">
正規表現
### 正規表現のリテラル
/pattern/  ### 両端を/で囲む

### 0から9にマッチする正規表現
[1] pry(main)> pattern = /[0-9]+/
=> /[0-9]+/ 

### マッチしたかを真偽値で返す
[2] pry(main)> pattern === 'HAL 900'
=> true 
[3] pry(main)> pattern === 'hoge'
=> false

### 最初にマッチした位置を返す。1文字目は「0」
[4] pry(main)> pattern =~ 'HAL 9000'
=> 4
[5] pry(main)> pattern =~ 'hoge'
=> nil

### リテラル中で式展開可能
[1] pry(main)> name = 'alic'
=> "alic"
[2] pry(main)> /hello, #{name}/
=> /hello, alic/
  • MatchDataオブジェクト
    • MatchData#[0] : マッチした文字列
    • MatchData#[1] : マッチした正規表現の中の一番目の括弧にマッチした文字列
    • MatchData#pre_match : マッチした文字列よりも前の文字列
    • MatchData#post_match : マッチした文字列よりも後の文字列
match
[1] pry(main)> /(123).+(456)/ =~ 'abc123def456ghi'
=> 3
[2] pry(main)> a = Regexp.last_match
=> #<MatchData "123def456" 1:"123" 2:"456">
### マッチした文字列より前の文字列
[3] pry(main)> a.pre_match
=> "abc"
[4] pry(main)> a[0]
=> "123def456"
### マッチした正規表現の中の一番目の括弧にマッチした文字列
[5] pry(main)> a[1]
=> "123"
### マッチした正規表現の中の二番目の括弧にマッチした文字列
[6] pry(main)> a[2]
=> "456"
### 最後の括弧にマッチした文字列
[7] pry(main)> a[-1]
=> "456"
### マッチした文字列よりも先の文字列
[8] pry(main)> a.post_match
=> "ghi"
  • String#scan : ブロックを受け取りマッチした部分文字列の数だけ繰り返しを実行する
  • Regexp.escape : エスケープする
  • Regexp.quote : 同上
scan
[1] pry(main)> str = 'Yamazaki Hamada Niizaki'
=> "Yamazaki Hamada Niizaki"
[2] pry(main)> str.scan(/\w+zaki/)
=> ["Yamazaki", "Niizaki"]
[3] pry(main)> str.scan(/(\w+)zaki/)
=> [["Yama"], ["Nii"]]

### ヒットした文字列のみ大文字にする
[4] pry(main)> str.scan(/\w+zaki/) {|s| puts s.upcase }
YAMAZAKI
NIIZAKI

### \wは英数字にマッチ(0-9A-Za-z_)
[1] pry(main)> str = 'Yamada Suzuki Hamada'
=> "Yamada Suzuki Hamada"
[2] pry(main)> str.scan(/\w+mada/) { |s| puts s.upcase}
YAMADA
HAMADA
=> "Yamada Suzuki Hamada"
  • グルーピングと後方参照、部分式呼び出し
    • 後方参照
      • ()でグルーピングした箇所は後から\1や\2で参照できる
      • n番目のグループにマッチした文字列に置き換える
    • 部分呼び出し
      • n番目のグループの式そのもので置き換える
グルーピング
### \1でb, \2でcを参照できる
[1] pry(main)> /(b) to (c) to \1 to \2/ === 'b to c to b to c'
=> true

### ラベル(hoge)をつけて参照する
[2] pry(main)> /(?<hoge>[0-9]+)/ === 'abc-123'
=> true
[3] pry(main)> Regexp.last_match[:hoge]
=> "123"

### 部分呼び出し
[1] pry(main)> phone = "080-1234-5678"
=> "080-1234-5678"
### \g<1>は([0-9]+)に置き換えられる
[2] pry(main)> /([0-9]+)-\g<1>-\g<1>/ === phone
=> true
### \1は0に置き換えられる
[3] pry(main)> /([0-9]+)-\1-\1/ === phone
=> false

  • 先読みと後読み
    • 先読み(lookahead)
    • 後読み(lookbehind)

Enumerable

  • オブジェクトの集まりを表現するクラスにはEnumerableがincludeされている
    • Array
    • Hash
    • Range
  • Enumerable#each_with_index : 繰り返しのたびにインクリメントされる値と一緒に繰り返す
  • Enumerable#reverse_each : 末尾から逆順に繰り返す
  • Enumerable#each_slice : 要素をnこずつ区切って繰り返す
  • Enumerable#each_cons : 要素をひとつずつずらしながら繰り返す
  • Enumerable#map : 与えられたブロックを評価した結果の配列を返す
  • Enumerable#collect : 同上
Enumerable
### indexつき繰り返し
[1] pry(main)> %w(a b c).each_with_index do |name, index|
[1] pry(main)*   puts "#{index}, #{name}"
[1] pry(main)* end
0, a
1, b
2, c
=> ["a", "b", "c"]

### 逆から繰り返し
[2] pry(main)> (1..3).reverse_each do |val|
[2] pry(main)*   puts val
[2] pry(main)* end
3
2
1
=> 1..3

### 要素をn個ずつで区切って繰り返し
[3] pry(main)> (1..5).slice_ do |val|
1..5).slice_after   1..5).slice_before  1..5).slice_when
[3] pry(main)> (1..5).each_slice 2 do |a, b|
[3] pry(main)*   p [a, b]
[3] pry(main)* end
[1, 2]
[3, 4]
[5, nil]
=> nil

### n個の連続した要素を1つずつずらしながら繰り返す
[4] pry(main)> (1..4).each_cons 2 do |a, b|
[4] pry(main)*   p [a, b]
[4] pry(main)* end
[1, 2]
[2, 3]
[3, 4]
=> nil

### map
[5] pry(main)> ['ruby', 'rails'].map{|str| str.upcase}
=> ["RUBY", "RAILS"]

  • 要素が特定の条件を満たしているか
    • メソッド
      • all?
      • none?
      • any?
      • one?
    • これらのメソッドはブロックを受け取る
    • 要素では無くブロックの戻り値を真偽値として使う
    • 要素ごとに繰り返し実行される
      • 全ての要素を調査する前に真偽が判明するとそれ以降の処理は行われない
要素の確認
### 全てtrueなら真
[1] pry(main)> [true, true, true].all?
=> true
[2] pry(main)> [true, false, true].all?
=> false

### 全てfalseなら真
[3] pry(main)> [false, false, false].none?
=> true

### ひとつでもtrueなら真
[4] pry(main)> [false, false, true].any?
=> true

### ひとつだけtrueなら真
[5] pry(main)> [true, false, false].one?
=> true
[6] pry(main)> [true, false, true].one?
=> false

### 要素が全て整数であればtrueを返す。
[1] pry(main)> [1, 2, 3].all? {|v| v.is_a?(Integer) }
=> true
[2] pry(main)> [1, 2, '3'].all? {|v| v.is_a?(Integer) }
=> false

### 最初に真が出ればtrueを返す。「1」でtrueが出た時点で処理は終了している。
[3] pry(main)> [1, 2, '3'].any? {|v| v.is_a?(Integer) }
=> true
  • 部分的な要素の取得
    • Enumerable#grep : 引数と要素を===で比較した時真となる要素の配列を返す
    • Enumerable#detect : ブロックの各要素に対して実行し、戻り値が最初に真となった要素を返す
    • Enumerable#find : 同上
    • Enumerable#select : ブロックを各要素に対して実行し、ブロックの戻り値が真となった要素を返す
    • Enumerable#find_all : 同上
    • Enumerable#reject : ブロックを各要素に対して実行し、ブロックの戻り値が偽となった要素を返す
    • Enumerable#take : 先頭から任意の要素を取り出して渡す
    • Enumerable#drop : 先頭から任意の数の要素をスキップして取り出す
    • Enumerable#take_while : ブロックが最初に偽を返すまで要素の配列を繰り返す
    • Enumerable#drop_while : ブロックが最初に偽を返してからの要素の配列を返す
grep
### 大文字小文字区別せずaが含まれている要素
[1] pry(main)> %w(Alice Bob Charlie).grep(/a/i)
=> ["Alice", "Charlie"]

### 文字列である要素。「//」で囲まない。
[2] pry(main)> ['ab',12,'b'].grep(String)
=> ["ab", "b"]

### オブジェクトのメソッドで?が含まれているものを抽出
[1] pry(main)> o = Object.new
=> #<Object:0x007f436cea5bd0>
[2] pry(main)> o.methods.grep(/\?/)
=> [:nil?,
 :eql?,
 :tainted?,
 :untrusted?,
 :frozen?,
 :instance_variable_defined?,
 :instance_of?,
 :kind_of?,
 :is_a?,
 :respond_to?,
 :equal?]

### 最初に真になった値を返す
[1] pry(main)> array = [ 1, 2, 3, 4, 5 ]
=> [1, 2, 3, 4, 5]
[2] pry(main)> array.detect {|v| v.even? }
=> 2

### 真になったものを全て返す
[3] pry(main)> array.select {|v| v.even? }
=> [2, 4]

### 偽となった要素を全て返す
[4] pry(main)> array.reject {|v| v.even? }
=> [1, 3, 5]

### 先頭から任意の要素を配列として返す
[5] pry(main)> array.take(3)
=> [1, 2, 3]

### 先頭から任意の要素をスキップして配列として返す
[6] pry(main)> array.drop(3)
=> [4, 5]

### ブロックが最初に偽を返すまで要素の配列を繰り返す
[7] pry(main)> array.take_while {|n| n < 3 }
=> [1, 2]

### ブロックが最初に偽を返してからの要素の配列を返す
[8] pry(main)> array.drop_while {|n| n < 3 }
=> [3, 4, 5]
  • 畳み込み演算
    • Enumerable#inject : レシーバに対して畳み込み演算を行う
    • Enumerable#reduce : 同上
inject
### resultの初期値はinjectの引数の「2」が入っている
[1] pry(main)> [1, 2, 3, 4].inject(2) {|result, num|
[1] pry(main)*   result + num
[1] pry(main)* }
=> 12

### 単純に足し合わせる場合は以下のように書ける。(呼び出すメソッド名をシンボルで受け取れる)
[2] pry(main)> [1, 2, 3, 4].inject(:+)
=> 10
  • 繰り返しとオブジェクトの更新
    • Enumerable#each_with_object : 要素を繰り返しながら1つのオブジェクトを更新してく
      • 初期値となるオブジェクトを引数で渡す
each_with_object
### 配列の各要素の長さを要素とするハッシュを作成。初期値のオブジェクトは空のハッシュ
[1] pry(main)> %w(abc de f).each_with_object({}) {|name, result|
[1] pry(main)*   result[name] = name.length
[1] pry(main)* }
=> {"abc"=>3, "de"=>2, "f"=>1}
  • 要素グルーピング
    • Enumerable#group_by : ブロックを評価した結果の戻り値をキーとした新しいハッシュを作成する
    • Enumerable#partition : ブロックの戻り値が真か偽かによってレシーバを2つのグループに分けた新しい配列を返す
要素グルーピング
### 整数と浮動小数点に分ける
[1] pry(main)> array = [1, 2.0, 3.0, 4]
=> [1, 2.0, 3.0, 4]
[2] pry(main)> array.group_by {|val| val.class}
=> {Fixnum=>[1, 4], Float=>[2.0, 3.0]}

### 整数と奇数で分ける
[3] pry(main)> array = [1, 2, 3, 4]
=> [1, 2, 3, 4]
[4] pry(main)> array.partition {|n| n.even?}
=> [[2, 4], [1, 3]]
  • 最大値と最小値
    • Enumerable#max : 最大値
    • Enumerable#min : 最小値
    • Enumerable#minmax : 最大値と最小値
    • Enumerable#max_by : 何かの最大を得る
    • Enumerable#min_by : 何かの最小を得る
    • Enumerable#minmax_by : 何かの最大値と最小値を得る
minmax
[1] pry(main)> range = (1..10)
=> 1..10
[2] pry(main)> range.max
=> 10
[3] pry(main)> range.min
=> 1
[4] pry(main)> range.minmax
=> [1, 10]

### 配列の要素の長さの最大と最小を得る
[6] pry(main)> hoge.min_by {|s| s.length }
=> "f"
[7] pry(main)> hoge.max_by {|s| s.length }
=> "abc"
[8] pry(main)> hoge.minmax_by {|s| s.length }
=> ["f", "abc"]
  • ソート
    • Enumerable#sort : レシーバの各要素をソートし、結果を新しい配列で返す
      • 比較のたびに2つの要素に対してメソッドを呼び出す
    • Enumerable#sort_by : レシーバの各要素をソートし、結果を新しい配列で返す
      • メソッド呼び出しは要素ごとに一回だけ
      • 要素ごとのメソッド実行はこちらのほうが時間が短くて住む
    • sortやsort_byは安定なソートではない
      • 比較結果が同じ要素がソート前と同じ順序で並ばない可能性がある
sort
[1] pry(main)> hoge = %w(abc de f)
=> ["abc", "de", "f"]

### 要素でソートする
[2] pry(main)> hoge.sort
=> ["abc", "de", "f"]

### 要素の長さでソートする
[3] pry(main)> hoge.sort {|a,b| a.length <=> b.length }
=> ["f", "de", "abc"]

### 要素の長さでソートするその2
[4] pry(main)> hoge.sort {|var| var.length}
=> ["f", "de", "abc"]

Enumerator

  • Enumeratorクラス
    • 繰り返し処理を行う様々なメソッドに対してEnumerableの機能を提供する
      • 例)
      • StringクラスはEnumerableをincludeしていない
        • each_charやeach_lineを通じてEnumerableの恩恵を受けている
    • 外部イテレータを提供
      • 柔軟な繰り返し処理
    • eachなど繰り返し処理を行うメソッドをブロックなしで呼び出した場合にはEnumeratorオブジェクトが返る
      • Enumerableをincludeしているクラスに限らない
Enumeratorオブジェクト
[1] pry(main)> [].each  ### Array#each
=> #<Enumerator: ...>
[2] pry(main)> {}.each  ### Hash#each
=> #<Enumerator: ...>
[3] pry(main)> (1..10).each   ### Range#each
=> #<Enumerator: ...>
[4] pry(main)> ''.each_char  ### String#each_char
=> #<Enumerator: ...>
[5] pry(main)> 10.times  ### Integer#times
=> #<Enumerator: ...>
[6] pry(main)> loop  ### Kernel.#loop
=> #<Enumerator: ...>
[7] pry(main)> [1,2,3].to_enum
=> #<Enumerator: ...>
[8] pry(main)> 'hoge'.enum_for(:each_char)
=> #<Enumerator: ...>

### Enumeratorオブジェクトは生成元のメソッドを使ってEnumerableのメソッドを提供
[1] pry(main)> line = <<EOM
[1] pry(main)* hoge
[1] pry(main)* hage2
[1] pry(main)* fugahaga
[1] pry(main)* EOM
=> "hoge\nhage2\nfugahaga\n"
[2] pry(main)> enum = line.each_line
=> #<Enumerator: ...>
[3] pry(main)> enum.map {|line| line.length}
=> [5, 6, 9]
  • Enumerable#each_with_index : 単に繰り返す処理に使う
  • Enumerator#with_index : 繰り返しのたびにインクリメントされる値と一緒に繰り返し処理を行う
繰り返し処理
### Enumerable#each_with_index
[1] pry(main)> %w(a bcd efgh).each_with_index do |name, index|
[1] pry(main)*   puts "#{index}: #{name}"
[1] pry(main)* end
0: a
1: bcd
2: efgh
=> ["a", "bcd", "efgh"]

### Enumerable#selectとEnumerator#with_indexの組み合わせ
### 添字が1以上のものを出力
[3] pry(main)> %w(a bcd efgh).select.with_index do |name, index|
[3] pry(main)*   index > 0
[3] pry(main)* end
=> ["bcd", "efgh"]
  • イテレータ
    • 内部イテレータ
      • eachなどのメソッドに渡したブロックの中で行われる繰り返し方式
        • 繰り返しを簡単に記述でき、本質的な処理に集中できる
    • 外部イテレータ
      • 繰り返しのタイミングを制御できる方式
      • 内部イテレータではうまくいかない例
        • 2つの配列を同時に繰り返す処理
          • 繰り返し自体はArray#eachの中で行われており、プログラマには制御できない
    • Enumerator#next : 次の要素を返し、内部で指している要素の位置をひとつ進める
    • Enumerator#rewind : はじめから繰り返しを行う
外部イテレータ
### 次の要素を返し、要素の位置をひとつ進める
[1] pry(main)> a = [1,2,3,4].to_enum
=> #<Enumerator: ...>
[3] pry(main)> a.next
=> 1
[4] pry(main)> a.next
=> 2
[5] pry(main)> a.next
=> 3
[6] pry(main)> a.next
=> 4
### 最後まで進むとStopIteration
[7] pry(main)> a.next
StopIteration: iteration reached an end
from (pry):7:in `next'

### rewindで最初からやりなおす
[8] pry(main)> a.rewind
=> #<Enumerator: ...>
[9] pry(main)> a.next
=> 1
[10] pry(main)> a.next
=> 2
[11] pry(main)> a.next
=> 3
[12] pry(main)> a.next
=> 4

### Kernel.#loopはStopIterationを捕捉してループを終了させる
[13] pry(main)> a.rewind
=> #<Enumerator: ...>
[14] pry(main)> loop do
[14] pry(main)*   puts a.next
[14] pry(main)* end
1
2
3
4
=> nil

### 2つの配列の処理を同時に繰り返す
[1] pry(main)> abc = %w(a b c).to_enum
=> #<Enumerator: ...>
[2] pry(main)> num = [1, 2, 3].to_enum
=> #<Enumerator: ...>
[3] pry(main)> loop do
[3] pry(main)*   str = abc.next
[3] pry(main)*   number = num.next
[3] pry(main)*   puts "#{str}: #{number}"
[3] pry(main)* end
a: 1
b: 2
c: 3
=> nil
  • ブロックの戻り値を使用するタイプのメソッドを呼び出す場合
    • Enumerator#feed : 戻り値に当たる値を渡す
    • StopIteration#result : メソッド自体の戻り値を得る
feed
[1] pry(main)> enum = %w(alice bob chalie).select
=> #<Enumerator: ...>
[2] pry(main)> loop do
[2] pry(main)*   begin
[2] pry(main)*     person = enum.next
[2] pry(main)*     enum.feed /li/ === person  ### enum.feedでブロックの戻り値「alice」など呼び出す
[2] pry(main)*   rescue StopIteration => e ### selectの戻り値が表示される
[2] pry(main)*     p e.result
[2] pry(main)*     break
[2] pry(main)*   end
[2] pry(main)* end
["alice", "chalie"]
=> nil
  • Enumerator::Lazy
    • Ruby2.0以降に追加
    • 繰り返し処理の実行を遅延させることができる
    • 大きな配列や無限の要素を持つオブジェクトを手軽に扱うことができる
    • Enumerator::Lazy.fore : 必要な分だけ計算された結果を得る
      • Lazyを使う場合とそうでない場合で処理の過程が異なる事に注意
Lazy
### メモリが足りない!
[1] pry(main)> (0..10000000000000).map{|n| n.succ }.select {|n| n.odd?}.take(3)
NoMemoryError: failed to allocate memory

### 処理できる
[2] pry(main)> a = (0..10000000000000).lazy.map{|n| n.succ }.select {|n| n.odd?}.take(3)
=> #<Enumerator::Lazy: ...>  ### この時点では計算されていない
[3] pry(main)> a.force
=> [1, 3, 5]

### Enumerator::Lazyは要素を繰り返すたびにmapとselect両方の処理が行われている
[1] pry(main)> a = (0..10).lazy.map{|n|
[1] pry(main)> (0..10).lazy.map{|n|
[1] pry(main)* puts "map #{n}"
[1] pry(main)* n.succ
[1] pry(main)* }.select {|n|
[1] pry(main)* puts "select #{n}"
[1] pry(main)* n.odd?
[1] pry(main)* }.take(3).force
map 0
select 1
map 1
select 2
map 2
select 3
map 3
select 4
map 4
select 5
=> [1, 3, 5]

### Enumeratorだけで行うと、mapの処理が全て終わった後でmapの戻り値に対してselectの処理が実行される

手続きオブジェクト(Proc)

  • 手続きオブジェクト
    • 関数をオブジェクトとして表現したもの
  • Proc.newにブロックを渡すことで手続きオブジェクトを生成できる
手続きオブジェクト
[1] pry(main)> greeter = Proc.new {|name|  ### callに渡した引数は仮引数(name)に代入される
[1] pry(main)* puts "Hello, #{name}!"
[1] pry(main)* }
=> #<Proc:0x007f36b47a0ed0@(pry):1>

### Proc#callを呼び出すと手続きが実行される
[2] pry(main)> greeter.call 'Ruby'
Hello, Ruby!
=> nil

### 手続きオブジェクトはprocメソッドやlambdaメソッドを使って短く記述できる
[1] pry(main)> by_proc = proc {|name| puts "Hello, #{name}!"}
=> #<Proc:0x007f9f6c9bd3a0@(pry):1>
[2] pry(main)> by_proc.call 'Nick'
Hello, Nick!
=> nil
[3] pry(main)> by_lambda = lambda {|name| puts "Hello, #{name}!"}
=> #<Proc:0x007f9f6d13f010@(pry):3 (lambda)>
[4] pry(main)> by_lambda.call 'Nancy'
Hello, Nancy!
=> nil

### Ruby1.9以降では「->」がよく使われる。
[5] pry(main)> by_literal = ->(name) {puts "Hello, #{name}!"}
=> #<Proc:0x007f9f6d17d590@(pry):5 (lambda)>
[6] pry(main)> by_literal.call 'Tom'
Hello, Tom!
=> nil

Time

  • オペレーティングシステムが提供するシステムの時刻機能をRubyから使用するためのクラス

    • Timeオブジェクト
      • 1970/01/01(UNIXエポック)からの経過秒数として時刻を保持している
      • TimeクラスはUNIXエポックよりも過去や遠い未来の時刻にも対応している
Time基本
### 時刻を得る
[1] pry(main)> Time.now
=> 2015-07-13 23:24:15 +0000
[2] pry(main)> Time.new
=> 2015-07-13 23:24:17 +0000

### タイムゾーンをUTCにする
[3] pry(main)> now = Time.now
=> 2015-07-13 23:26:43 +0000
[4] pry(main)> now.utc
=> 2015-07-13 23:26:43 UTC
[5] pry(main)> now.zone
=> "UTC"

### エポックからの経過秒数を得る
[6] pry(main)> now.to_i
=> 1436830003
[7] pry(main)> now.to_f
=> 1436830003.0362952
[8] pry(main)> now.to_r
=> (359207500759073773/250000000)
[9] pry(main)> now.to_s
=> "2015-07-13 23:26:43 UTC"

### 年月日や時分秒を得る
[10] pry(main)> now.year
=> 2015
[11] pry(main)> now.month
=> 7
[12] pry(main)> now.day
=> 13
[13] pry(main)> now.hour
=> 23
[14] pry(main)> now.min
=> 26
[15] pry(main)> now.sec
=> 43
### 1秒以下の時間も取得しているので同じ秒に2回オブジェクトを生成したものを==で比較するとfalseになる
[16] pry(main)> now.nsec
=> 36295092
### 日曜0、土曜を6とした時の曜日
[17] pry(main)> now.wday
=> 1
### 曜日判定
[18] pry(main)> now.monday?
=> true
### 1月1日を1とした経過日数
[19] pry(main)> now.yday
=> 194

### +で秒数を足すことができる
[1] pry(main)> a = Time.now
=> 2015-07-14 12:30:32 +0000
[2] pry(main)> a + 1
=> 2015-07-14 12:30:33 +0000

### -で時間差を出すことができる
[3] pry(main)> b = Time.now
=> 2015-07-14 12:30:45 +0000
[4] pry(main)> b - a
=> 12.504797007

### エポックを取得
[5] pry(main)> Time.at(0)
=> 1970-01-01 00:00:00 +0000
[6] pry(main)> Time.at(0).getutc
=> 1970-01-01 00:00:00 UTC

### 年数や時刻からTimeオブジェクトを得る
[7] pry(main)> Time.utc(2011, 4, 1, 5, 30, 20, 100)
=> 2011-04-01 05:30:20 UTC
[8] pry(main)> Time.local(2011, 4, 1, 5, 30, 20, 100)
=> 2011-04-01 05:30:20 +0000

### 配列に格納する
[9] pry(main)> Time.now.to_a
=> [8, 36, 12, 14, 7, 2015, 2, 195, false, "UTC"]

### 文字列表現
[1] pry(main)> a = Time.now
=> 2015-07-14 12:39:15 +0000
[2] pry(main)> a.strftime('%Y/%m/%d %H:%M:%S')
=> "2015/07/14 12:39:15"

[3] pry(main)> a.strftime('%m')
=> "07"
[5] pry(main)> a.strftime('%-m')
=> "7"

IO・File

  • ファイルシステム上のファイルを扱うためのクラス
    • IOを継承したFileクラス
    • クラスメソッド
      • ファイルの持つ情報を得る
      • ファイルを操作する
  • IO#read : ファイルを読み込む
  • IO#close : ファイルを閉じる
  • IO#gets : 1行ずつ読み込みながら処理する
  • IO#each_line : 終端まで1行ずつ読み込みながら処理をする
File
### ファイルを開く
[1] pry(main)> file = File.open('hoge.rb')
=> #<File:hoge.rb>
### ファイルの内容を全て読み込んで出力する
[2] pry(main)> puts file.read
without block
=> nil
### ファイルを閉じる
[3] pry(main)> file.close
=> nil

### 1行ずつ読み込みながら処理をする
[4] pry(main)> File.open 'hoge.rb' do |f|
[4] pry(main)*   while line = f.gets
[4] pry(main)*     puts line
[4] pry(main)*   end
[4] pry(main)* end
without block
=> nil

### 終端まで1行ずつ繰り返しながら処理をする
[1] pry(main)> File.open 'hoge.rb' do |f|
[1] pry(main)*   f.each_line do |line|
[1] pry(main)*     puts line
[1] pry(main)*   end
[1] pry(main)* end
without block
=> #<File:hoge.rb (closed)>

### 終端まで1文字ずつ繰り返しながら処理をする
[2] pry(main)> File.open 'hoge.rb' do |f|
[2] pry(main)*   f.each_char do |line|
[2] pry(main)*     puts line
[2] pry(main)*   end
[2] pry(main)* end
w
i
t
h
o
u
t

b
l
o
c
k

=> #<File:hoge.rb (closed)>

### byte
[3] pry(main)> File.open 'hoge.rb' do |f|
[3] pry(main)*   f.each_byte do |line|
[3] pry(main)*     puts line
[3] pry(main)*   end
[3] pry(main)* end
119
105
116
104
111
117
116
32
98
108
111
99
107
10
=> #<File:hoge.rb (closed)>

### 単純にファイルの中身を文字列として取得する
[5] pry(main)> File.read('hoge.rb')
=> "without block\n"
  • ファイルへの書き込み
    • IO#write : ファイルに書き込む
    • File.openはデフォルトでは読み込みモード
      • 読み込みモードで書き込もうとすると例外IOErrorが発生する
      • モード一覧
        • r : 読み込みモードで開く
        • r+ : 読み書き両用モードで開く
        • w : 新規作成して書き込みモードで開く
        • w+ : 新規作成して読み書き両用モードで開く
        • a : 追記書き込みモードで開く
        • a+ : 読み込み、追記書き込みモードで開く
          • bをつけくわえるとバイナリモード
      • 定数を使ってモードを細かく指定できる
        • File::APPEND : 追記
        • File::CREATE : 無ければ新規作成
        • File:NOATIME : 最終アクセス日時を更新しない
        • File:WRONLY : 書き込み専用
        • File:RSYNC : 同期モードで書き込む
        • etc...
    • IO#write以外の書き込みメソッドもある
      • 内部でIO#writeを呼び出している
        • IO#puts : 末尾に改行がつくように書き込む
        • IO#print : 改行なしで書き込む
        • IO#flush : バッファに蓄積されたデータを書き込む
          • 非同期モードの時はいったんバッファに蓄積する
        • IO#rsync= : trueを渡すと同期モードになる
ファイルへの書き込み
### ファイルへの書き込み
[1] pry(main)> File.open 'hoge.txt', 'w' do |f|
[1] pry(main)*   f.write 'Hello'
[1] pry(main)* end
=> 5
[2] pry(main)> File.read('hoge.txt')
=> "Hello"

### 書き込みモードでファイルを開く。ファイルが無ければ新規作成
[1] pry(main)> File.open 'hoge.txt', File::WRONLY | File::CREAT do |f|
[1] pry(main)*   f.puts 'puts'  ### puts¥nと書き込む
[1] pry(main)*   f.puts   ### 改行のみ
[1] pry(main)*   f.print 'print'  ### printと書き込む
[1] pry(main)*   f.printf '%03d', 7  ### 007と書き込む
[1] pry(main)*   f.putc 97   ### aと書き込む
[1] pry(main)* end
=> 97
[2] pry(main)> File.read('hoge.txt')
=> "puts\n\nprint007a"

### 手軽にファイルに書き込む方法
[1] pry(main)> File.write 'hoge.txt', 'hello'
=> 5
[2] pry(main)> File.read('hoge.txt')
=> "hello"
  • アクセス位置の操作
    • IOオブジェクトは今自身がどの位置に居るかというアクセスについての位置情報を持っている
      • IO#writeやIO#readはこのアクセス位置を基準として読み込みや書き込みを行ってアクセス位置を進める
    • IO#rewind : アクセス位置を先頭に戻す
    • IO#seek : アクセス位置を任意の位置に移動
      • 特定の場所から相対的な位置を指定する
        • 先頭 (IO::SEEK_SET)
        • 現在位置 (IO::SEEK_CUR)
        • ファイルの末尾 (IO::SEEK_END)
    • IO#pos= : 任意の位置に移動させる
    • IO#pos : 現在のアクセス位置を整数で返す
アクセス位置の操作
### ファイルを読み込み、追記モードで開く
[1] pry(main)> File.open 'hoge.txt', 'a+' do |f|
[1] pry(main)*   f.puts 'hoge'  ### hogeを追記する
[1] pry(main)*   f.rewind   ### ファイルの先頭に戻る
[1] pry(main)*   puts f.read   ### 先頭から末尾まで文字列を取得して出力する
[1] pry(main)* end
hello
akjefoa
awejfoe
aea
ldw
end
hoge
=> nil

[1] pry(main)> File.open 'hoge.txt' do |f|
[1] pry(main)*   f.seek 5  ### 先頭から5進む f.seek(10, IO::SEEK_SET)と同じ
[1] pry(main)*   f.seek -3, IO::SEEK_CUR   ### 現在置から2戻る
[1] pry(main)*   f.pos   ### 現在の位置を返す
[1] pry(main)* end
=> 2 ### 現在置

### 任意の位置に移動する
[1] pry(main)> File.open 'hoge.txt' do |f|
[1] pry(main)*   f.pos=10
[1] pry(main)*   f.pos
[1] pry(main)* end
=> 10
  • エンコーディングの違い
    • IOオブジェクトは2種類のエンコーディング情報を持つ
      • 外部エンコーディング
      • 内部エンコーディング
    • 具体的な例
      • EUC-JPのテキストファイルをRubyではUTF-8として扱う
        • 外部エンコーディング : EUC-JP
        • 内部エンコーディング : UTF-8
エンコーディング
### ファイルを開いてエンコーディングを設定
[1] pry(main)> File.open 'hoge.txt' do |f|
[1] pry(main)*   f.set_encoding('EUC-JP', 'UTF-8')  ### 外部はEUC-JP、内部はUTF-8
[1] pry(main)*   f.set_encoding('EUC-JP:UTF-8') ### 上と同じ
[1] pry(main)* end
=> #<File:hoge.txt (closed)>

### ファイルを開く時に指定
[1] pry(main)> File.open 'hoge.txt', 'r:euc-jp:utf-8' do |f|
[1] pry(main)*   puts f.external_encoding
[1] pry(main)*   puts f.internal_encoding
[1] pry(main)* end
EUC-JP
UTF-8
=> nil

Fileオブジェクトの操作

  • ファイルのロック
    • 複数のプロセスがファイルにアクセスする可能性があるときにロックをして安善に取り扱う
      • 意図しないタイミングで他のプロセスから新たな書き込みが内容にする
    • File#flock : ファイルをロックする
      • アドバイザリロック (flockを使うプロセス同士で有効、他のプロセスは無効)
      • 定数
        • File::LOCK_EX 一つのプロセスだけがファイルをロックできる排他ロック
        • File::LOCK_NB 他のプロセスにブロックされている場合即座にfalseを返す
        • File::LOCK_SH 複数のプロセスが同時に読み込みのためにロックを共有できる
        • File::LOCK_UN 明示的にアンロックする
    • flockの呼び出しがブロックされるケース
      • 他のプロセスがすでに排他ロックを行っている時に排他ロック、共有ロックを行った時
      • 他のプロセスが既に共有ロックを行っている時に排他ロックをしようとした時
flock
### 排他ロックしつつファイル内の数値をインクリメントして上書き
[1] pry(main)> File.open 'hoge.txt', File::RDWR | File::CREAT do |f|
[1] pry(main)*   f.flock File::LOCK_EX
[1] pry(main)*   count = f.read.to_i
[1] pry(main)*   f.rewind
[1] pry(main)*   f.write count.succ
[1] pry(main)* end
  • ファイルの情報取得
    • ファイルの持つ日時に関する情報やサイズなどをFileオブジェクトから取得する
  • File#lstat : File::Statオブジェクトを返す
  • IO#stat : File::Statオブジェクトを返す
ファイルの情報取得
[1] pry(main)> File.open 'hoge.txt' do |f|
[1] pry(main)*   puts f.atime ### 最終アクセス日時
[1] pry(main)*   puts f.ctime ### 最後に状態を変更した日時
[1] pry(main)*   puts f.mtime ### 最終更新日時
[1] pry(main)*   puts f.size
[1] pry(main)* end
2015-07-20 03:38:36 +0000
2015-07-20 03:38:36 +0000
2015-07-20 03:38:36 +0000
5
=> nil

### File::Statオブジェクトから情報を取得
[1] pry(main)> hoge = File.open('hoge.txt', &:stat)
=> #<File::Stat
 dev=0x801,
 ino=2936,
 mode=0100664 (file rw-rw-r--),
 nlink=1,
 uid=500 (vagrant),
 gid=500 (vagrant),
 rdev=0x0 (0, 0),
 size=5,
 blksize=4096,
 blocks=8,
 atime=2015-07-20 03:38:36 +0000 (1437363516),
 mtime=2015-07-20 03:38:36 +0000 (1437363516),
 ctime=2015-07-20 03:38:36 +0000 (1437363516)>
[2] pry(main)> hoge.ftype
=> "file"
[3] pry(main)> hoge.file?
=> true
[4] pry(main)> hoge.directory?
=> false
[5] pry(main)> hoge.symlink?
=> false
[6] pry(main)> hoge.pipe?
=> false
[7] pry(main)> hoge.socket?
=> false
[8] pry(main)> hoge.writable?
=> true
[9] pry(main)> hoge.readable?
=> true
[10] pry(main)> hoge.executable?
=> false
[11] pry(main)> hoge.owned?
=> true
[12] pry(main)> hoge.gid
=> 500
[13] pry(main)> hoge.uid
=> 500
[14] pry(main)> hoge.ino
=> 2936
[15] pry(main)> hoge.dev
=> 2049

ファイルの操作

ファイル操作
### ファイル名変更。同じファイルがあれば上書きされる
[1] pry(main)> File.rename 'fuga.txt', 'haga.txt'
=> 0
[2] pry(main)> `ls haga.txt`
=> "haga.txt\n"

### ファイルの移動
[3] pry(main)> File.rename 'haga.txt', 'tmp/haga.txt'
=> 0
[4] pry(main)> `ls tmp/haga.txt`
=> "tmp/haga.txt\n"

### ファイルの削除
[5] pry(main)> File.unlink 'tmp/haga.txt'
=> 1
[6] pry(main)> `ls tmp/haga.txt`
ls: cannot access tmp/haga.txt: そのようなファイルやディレクトリはありません
=> ""

### symlinkの作成 'オリジナルファイルのPATH', 'symlinkを作成先'
[7] pry(main)> File.symlink '/home/user/vagrant/hoge.txt', 'tmp/hoge.txt'
=> 0

### パーミッションの変更
[8] pry(main)> File.chmod 0600, 'hoge.txt'
=> 1

### 所有者の変更。idで指定 'ユーザ', 'グループ'
[18] pry(main)> File.chown 1243, 1000, 'hoge.txt'
=> 1

ファイルパス操作

ファイルパス操作
[1] pry(main)> fname = '/etc/resolve.conf'
=> "/etc/resolve.conf"

### ファイルのあるディレクトリのパス
[2] pry(main)> File.dirname(fname)
=> "/etc"

### ファイル名
[3] pry(main)> File.basename(fname)
=> "resolve.conf"

### 拡張子を除いたファイル名
[4] pry(main)> File.basename(fname, '.*')
=> "resolve"

### ファイルの拡張子
[5] pry(main)> File.extname(fname)
=> ".conf"

### ファイルパス同士を連結
[6] pry(main)> File.join('/usr/local', '/bin/ruby')
=> "/usr/local/bin/ruby"

### dirnameとbasenameの配列
[7] pry(main)> File.split('/usr/local/bin/ruby')
=> ["/usr/local/bin", "ruby"]

###  ディレクトリ(/hoge/hage)を指定して絶対パスを得る
[11] pry(main)> File.expand_path('fuga', '/hoge/hage')
=> "/hoge/hage/fuga"

### ディレクトリを省略するとカレントディレクトリのパスを得る
[12] pry(main)> File.expand_path('fuga')
=> "/home/vagrant/work/ruby/tmp/fuga"

### 現在実行しているファイルのディレクトリパスを得る
[13] pry(main)> File.expand_path('..', __FILE__)
=> "/home/vagrant/work/ruby"

### Ruby2.0では__dir__でも得られる (pryでは「.」になる)
[14] pry(main)> __dir__
=> "."

### ~を展開した絶対パスを得る
[15] pry(main)> File.expand_path('~')
=> "/home/vagrant"

### シンボリックリンクから実際のファイルの絶対パスを得る
[16] pry(main)> File.realpath('script_path_symlink')
=> "/home/vagrant/work/ruby/ruby.rb"

Kernel.#open

  • ファイルはKernel.#openを用いて開く事もできる
  • コマンドの実行やサブプロセスへのパイプの生成にも使える
kernelopen
### Kenel.#openを使う
[1] pry(main)> open('ruby.rb') {|f| puts f.read }
(ファイルの中身)
=> nil

### 「|」から始まる場合はコマンドとして実行される
[2] pry(main)> open('|uname -n') {|io| puts io.read}
vagranthost.local
=> nil

### 「|-」から始まる場合は実行中のプロセスの子プロセスが生成され、ブロックの引数にはその子プロセスの標準入出力を表すIOオブジェクトが与えられる
[3] pry(main)> open '|-', 'w' do |io|
[3] pry(main)*   if io
[3] pry(main)*     io.puts 'hello'  ### 親プロセスではブロック引数がIOオブジェクト
[3] pry(main)*   else
[3] pry(main)*     puts gets ### 子プロセスではブロック引数がnil
[3] pry(main)*   end
[3] pry(main)* end
hello
=> nil

Dir

  • ディレクトリ操作のためのクラス
Dir
### カレントディレクトリの表示
[1] pry(main)> Dir.pwd
=> "/home/vagrant/work/ruby"

### ホームディレクトリの表示
[2] pry(main)> Dir.home
=> "/home/vagrant"

### ブロックを受け取って実行が完了後は元のディレクトリに戻る
[3] pry(main)> Dir.chdir('tmp') do |path|
[3] pry(main)*   puts path
[3] pry(main)*   puts Dir.pwd
[3] pry(main)* end
tmp
/Users/200135/work/ruby/tmp
=> nil
[4] pry(main)> Dir.pwd
=> "/Users/200135/work/ruby"

### ディレクトリの移動
[5] pry(main)> Dir.chdir '/tmp'
=> 0
[6] pry(main)> Dir.pwd
=> "/tmp"

### ファイル一覧の取得(Dir.entries)
[1] pry(main)> Dir.entries('.')
=> [".", "..", "resman4", "ruby.rb", "script_path", "tmp"]

### ファイル一覧の取得(ディレクトリを開いて一覧を取得)
[2] pry(main)> Dir.open '.' do |dir|
[2] pry(main)*   dir.entries
[2] pry(main)* end
=> [".", "..", "resman4", "ruby.rb", "script_path", "tmp"]

### マッチするファイルパス一覧の配列を返す(Dir.glob)
[3] pry(main)> Dir.glob('./r*')
=> ["./resman4", "./ruby.rb"]

### マッチするファイルパス一覧の配列を返す(Dir.[])
[4] pry(main)> Dir['./r*']
=> ["./resman4", "./ruby.rb"]

### 再帰的に得る
[1] pry(main)> Dir['./tmp/*']
=> ["./tmp/dir1", "./tmp/dir2"]

### 再帰的に得る(dir2配下のファイルが得られてない?)
[2] pry(main)> Dir.glob('./tmp/*/*')
=> ["./tmp/dir1/fuga1", "./tmp/dir1/fuga2", "./tmp/dir1/hoge1", "./tmp/dir1/hoge2"]
[3] pry(main)> Dir.glob('./tmp/**/*')
=> ["./tmp/dir1", "./tmp/dir1/fuga1", "./tmp/dir1/fuga2", "./tmp/dir1/hoge1", "./tmp/dir1/hoge2", "./tmp/dir2"]

### Dirオブジェクトを作成
[2] pry(main)> dir = Dir.open('/usr')
=> #<Dir:/usr>

### 自身の開いているディレクトリ名
[3] pry(main)> dir.path
=> "/usr"

### ファイル一覧の繰り返し
[4] pry(main)> dir.each do |file|
[4] pry(main)*   puts file
[4] pry(main)* end
..
src
tmp
include
etc
lib64
lib
games
local
bin
libexec
my.cnf
.
sbin
share
=> #<Dir:/usr>

### 読み込み位置を先頭に戻す
[5] pry(main)> dir.rewind
=> #<Dir:/usr>

### 読み込み位置の確認
[6] pry(main)> dir.pos
=> 0

### 現在の位置のファイル名を返し、読み込み位置を一つ進める
[7] pry(main)> dir.read
=> ".."
[8] pry(main)> dir.read
=> "src"
[9] pry(main)> dir.read
=> "tmp"

### 読み込み位置を任意の場所に移動する
[10] pry(main)> dir.pos = 0
=> 0

### ディレクトリを閉じる
[11] pry(main)> dir.close
=> nil

### ディレクトリの作成
[1] pry(main)> Dir.mkdir 'foo', 0755
=> 0

### ディレクトリのリネーム
[2] pry(main)> File.rename 'foo', 'bar'
=> 0

### ディレクトリの存在確認
[3] pry(main)> Dir.exist?('foo')
=> false
[4] pry(main)> Dir.exist?('bar')
=> true

### ディレクトリの削除
[5] pry(main)> Dir.rmdir 'bar'
=> 0
[6] pry(main)> Dir.exist?('bar')
=> false

Thread

  • 同じプロセス上でメモリを共有しつつ処理を並列に実行することができる
    • 異なる処理を同時に実行できる
    • 有る場面では処理を高速に実行できる
    • スレッドの実装は処理系による

スレッドの生成

  • Thread.forkで生成する
    • またはThread.new, Thread.startでもよい
  • Thread#value : スレッドの終了を待ち、戻り値を得る
  • Thread#join : 単にスレッドの終了を待つ
    • タイムアウトとなる秒数を指定できる
    • タイムアウトするとnilを返す
  • 全てのThreadオブジェクトはThreadクラスから得ることができる
スレッドの生成
### 複数のファイルの行数を出力する
[1] pry(main)> files = %w(hoge fuga haga)
=> ["hoge", "fuga", "haga"]
[2] pry(main)> threads = files.map {|file|
[2] pry(main)*   Thread.fork {
[2] pry(main)*     num = File.readlines(file).length
[2] pry(main)* "#{file}: #{num}"
[2] pry(main)* }
[2] pry(main)* }
=> [#<Thread:0x007fc9622110c8@(pry):3 sleep>, ### Threadオブジェクトを返す
 #<Thread:0x007fc962210fb0@(pry):3 run>,
 #<Thread:0x007fc962210e98@(pry):3 run>]

### 戻り値を得る
[3] pry(main)> threads.map(&:value)
=> ["hoge: 7", "fuga: 14", "haga: 5"]

### メインスレッド
[4] pry(main)> Thread.main
=> #<Thread:0x007fc9618c03c0 run>

### 現在実行中のスレッド
[5] pry(main)> Thread.current
=> #<Thread:0x007fc9618c03c0 run>

### 全てのスレッドの配列
[6] pry(main)> Thread.list
=> [#<Thread:0x007fc9618c03c0 run>]

変数の扱い

  • スレッドは同じプロセス上の他のスレッドとメモリを共有する
    • 複数のスレッドが同じ変数にアクセスするときは注意が必要
スレッドのブロック引数として受け取る
$ vim thread.rb
============================
for item in %w(foo bar baz)
  Thread.fork do ### 各スレッドで変数itemを共有
    sleep 1
    puts item  ### itemが出力されるタイミングはループが終わった後
  end
end

(Thread.list - [Thread.current]).each &:join  ### 子スレッドの終了を待つ
============================

### 全てのスレッドが最後の要素bazのみ出力している
$ ruby thread_ng.rb
============================
baz
baz
baz
============================

$ vim thread_ok.rb
============================
for item in %w(foo bar baz)
  Thread.fork item do |value| ### ブロックの仮引数として値を受け取る(ブロックローカル変数となるため他のスレッドと共有されない)
    sleep 1
    puts value ### 全てのスレッドで各要素の値が出力される
  end
end

(Thread.list - [Thread.current]).each &:join  ### 子スレッドの終了を待つ
============================

$ ruby thread_ok.rb
============================
foo
baz
bar
============================

スレッド固有のデータ

スレッド固有のデータ
### Thread.currentで現在実行中のThreadオブジェクトを返す
[1] pry(main)> thread = Thread.fork {
[1] pry(main)*   Thread.current[:status] = 'in progress'
[1] pry(main)*   sleep 0.5
[1] pry(main)* }
=> #<Thread:0x007fd7220ccff0@(pry):1 sleep>

### ハッシュや文字列のキーでアクセスする
[2] pry(main)> thread[:status]
=> "in progress"
[3] pry(main)> thread['status']
=> "in progress"

ライフサイクル

  • Thread#statusが返すスレッドの状態
    • run : 実行可能 or 実行中
    • sleep : 停止している
      • Kernel.#sleepにより休止している
      • 他のスレッドの終了を待っている
      • Thread.stopにより停止している
      • IO待ち
    • aborting : 終了処理中
    • false : 正常終了
    • nil : 異常終了
ライフサイクル
### 5秒間sleepする
[1] pry(main)> t = Thread.fork { sleep 5 }
=> #<Thread:0x007f9042321b60@(pry):1 sleep>

### 状態の確認
[2] pry(main)> t.status
=> "sleep"

### 5秒経つとスレッドが正常終了する
[3] pry(main)> t.alive?
=> true
[4] pry(main)> t.alive?
=> false
[5] pry(main)> t.stop?
=> true
[6] pry(main)> t.status
=> false   ### falseは正常終了

スレッドの操作

  • Thread.stop : 自分自身を停止状態にする
    • 他のスレッドを停止する事はできない
    • 他のスレッドから実行可能 or 実行状態にしない限り停止しつづける
  • Thread#wakeup : 停止しているスレッドを実行可能状態にする
  • Thread#run : 即座に処理をそのスレッドに切り替える
    • wakeupやrunは、指定されたsleepの時間が経過しない場合でも即実行が再開される
スレッドの操作
### スレッドを停止状態にする
[1] pry(main)> t = Thread.fork {
[1] pry(main)*   Thread.stop
[1] pry(main)*   puts 'hi'
[1] pry(main)* }
=> #<Thread:0x007fd0ac0b1290@(pry):1 sleep>

[2] pry(main)> t.status
=> "sleep"

### 停止しているスレッドを実行可能状態にする
[3] pry(main)> t.run
hi
=> #<Thread:0x007fd0ac0b1290@(pry):1 dead>

例外の扱い

  • あるスレッドで例外が発生し、そおスレッド無いで捕捉されなかった場合スレッドは終了する

    • Thread#joinやThread#valueなどで待っているスレッドが例外により終了した場合待っている側のスレッドでも同じ例外が発生する
  • Thread#abort_on_exception=

  • Thread.abort_on_exception=

    • あるスレッドで例外が発生した時trueを渡すと実行中のRubyプロセス自体を終了できる
  • Thread#raise : そのスレッドに対して強制的に例外を発生させる

    • Kernel.#raiseと同様に例外の種類やメッセージ、バックトレース情報を渡せる
例外の扱い
### 例外を発生させる
[1] pry(main)> t = Thread.fork { raise }
=> #<Thread:0x007fef8caa31a0@(pry):1 dead>

### 例外発生
[2] pry(main)> t.join
RuntimeError:
from (pry):1:in `block in __pry__'

スレッドの優先順位

  • 優先度を数値でつける事ができる
    • デフォルトは0
    • 数値が高いほど優先順位が高くなる
    • 新たに生成したスレッドの優先順位は親スレッドから引き継がれる
  • Thread#priority : 優先順位の確認
  • Thread#priority= : 優先順位を新たに設定する
スレッドの優先順位
[1] pry(main)> current = Thread.current
=> #<Thread:0x007fe77b0bc3f8 run>

### 現在の優先度
[2] pry(main)> current.priority
=> 0
[3] pry(main)> current.priority = 2
=> 2
[4] pry(main)> current.priority
=> 2

### 生成したスレッドの優先度は親と同じ
[5] pry(main)> Thread.fork do
[5] pry(main)*   puts Thread.current.priority
[5] pry(main)* end
=> #<Thread:0x007fe77d12b1c0@(pry):5 run>
[6] pry(main)> 2

ThreadGroup

  • いくつかのスレッドをまとめて一つのグループとして扱う事ができる
    • あるグループのスレッドにだけ何らかの操作をするということができる
  • 全てのスレッドは必ずどこかのグループに属している
    • デフォルトはThreadGroup::Defaultに属する
    • 子スレッドは親スレッドと同じグループに属する
    • Thread#groupにより自身が属するグループを得られる
    • ThreadGroup#addによりグループに追加できる
  • あるスレッドが一度に属する事ができるグループはひとつだけ
    • あるgroupにaddすると元のグループから抜ける事になる
ThreadGroup
### スレッドグループの作成
[1] pry(main)> group = ThreadGroup.new
=> #<ThreadGroup:0x007fe22a0dcaf0>

### スレッドの作成
[2] pry(main)> thread = Thread.fork {
[2] pry(main)*   sleep 1
[2] pry(main)* }
=> #<Thread:0x007fe229a71170@(pry):2 sleep>

### スレッドグループに追加
[3] pry(main)> group.add thread
=> #<ThreadGroup:0x007fe22a0dcaf0>

### 属しているスレッドの確認
[4] pry(main)> group.list
=> [] ### ここにgroupが表示されるはずだけど?

Mutex

  • Mutex (mutual exclusion)
    • 相互排他処理を行うためのロックを提供
      • 複数のスレッドが同時にひとつのデータを更新・参照する処理に必要
mutex
### ファイルの数値を読み込み1を加えて保存するプログラム
$ cat mutex_no.rb
============================
def countup
  File.open 'counter', File::RDWR | File::CREAT do |f|
  last_count = f.read.to_i

  f.rewind
  f.write last_count + 1
  end
end

10.times.map {
  Thread.fork { countup }
}.map(&:join)

puts File.read('counter').to_i
============================

### 複数のスレッドが同時に読み込む。タイミングによって値が違う
$ruby thread_ok.rb
============================
(毎回足される数が違う結果が違う)
5
7
10
============================


$ cat mutex.rb
============================
def countup
  File.open 'counter', File::RDWR | File::CREAT do |f|
  last_count = f.read.to_i

  f.rewind
  f.write last_count + 1
  end
end

mutex = Mutex.new  ### mutexオブジェクトの生成。一度に1つのスレッドだけロックを取得できる

10.times.map {
  Thread.fork {
    mutex.synchronize { countup }  ### ロックを取得してブロックを実行し、開放する
  }
}.map(&:join)

puts File.read('counter').to_i
============================

$ruby thread_ok.rb
============================
(毎回足される結果が同じ)
11
21
31
============================

デッドロック

デッドロック
### mutexオブジェクトの生成
[1] pry(main)> m1, m2 = Mutex.new, Mutex.new
=> [#<Mutex:0x007fdba8b4f7b8>, #<Mutex:0x007fdba8b4f790>]

### スレッドt1がmutexオブジェクトm1のロックを取得
[2] pry(main)> t1 = Thread.fork {
[2] pry(main)*   m1.lock
[2] pry(main)*   sleep 1
[2] pry(main)*   m2.lock
[2] pry(main)* }
=> #<Thread:0x007fdba8ace820@(pry):2 sleep>

### スレッドt2がmutexオブジェクトm2のロックを取得
[3] pry(main)> t2 = Thread.fork {
[3] pry(main)*   m2.lock
[3] pry(main)*   sleep 1
[3] pry(main)*   m1.lock
[3] pry(main)* }
=> #<Thread:0x007fdba8a1fc30@(pry):7 sleep>

### t1がm2のロックを取得しようとしているがブロックされてしまっている
[4] pry(main)> t1.join
=> #<Thread:0x007fdba8ace820@(pry):2 dead>

### t2がm1のロックを取得しようとしているがブロックされてしまっている
[5] pry(main)> t2.join
=> #<Thread:0x007fdba8a1fc30@(pry):7 dead>

Fiber

  • ある処理を途中まで実行してその後任意のタイミングで前回の続きから処理を行うということができる
    • Ruby1.9以降対応
    • ノンプリエンティブな軽量スレッドを提供する
      • プリエンティブとは
        • スレッドが生成されてから直ぐに処理が実行され、プログラマが制御できないタイムスライスで実行されるような実行方法
  • ファイバの切り替えはスレッドを超えて行う事はできない
    • 他のスレッドのファイバをresumeした際や、ファイバの中のスレッドからFiber.yieldした際はFiberErrorが発生する

基本的な振る舞い

  • Fiber#resume : 実行を開始する
    • スレッドと違い、ファイバの切り替えは自動的に行われない
    • 親子関係がある
      • Fiber#resumeを呼び出した側が親、呼び出されたファイバが子になる
  • Fiber.yield : resumeされたファイバから親に切り替える
    • 途中で何個もはさむ事ができる
基本的な振る舞い
[1] pry(main)> fiber = Fiber.new {
[1] pry(main)*   puts 'Hello'
[1] pry(main)*   Fiber.yield
[1] pry(main)*   puts 'Hello Again'
[1] pry(main)* }
=> #<Fiber:0x007fa93d06bd30>

### ブロックのはじめからFiber.yieldまで実行され、処理が親に戻っている
[2] pry(main)> fiber.resume   
Hello
=> nil

### Fiber.yieldから処理が再開され、ブロックの最後まで実行して親に戻る
[3] pry(main)> fiber.resume
Hello Again
=> nil

### もう実行する部分が無いのでFiberErrorが発生している
[4] pry(main)> fiber.resume
FiberError: dead fiber called
from (pry):8:in `resume'

引数と戻り値

引数と戻り値
[1] pry(main)> fiber = Fiber.new {|first|
[1] pry(main)*   puts first
[1] pry(main)*   second = Fiber.yield('goodbye')
[1] pry(main)*   puts second
[1] pry(main)*   'goodbye again'
[1] pry(main)* }
=> #<Fiber:0x007f88118cd548>

### 
[2] pry(main)> puts fiber.resume('hello')
hello  ### resumeに与えた引数(hello)が、Fiber.newのブロック引数となり、それを出力
goodbye   ### Fiber.yieldに渡した引数(goodbye)が、resumeの戻り値となり、それを出力
=> nil
[3] pry(main)> puts fiber.resume('hello again')
hello again  ### resumeに与えた引数(hello again)が、Fiber.yieldの戻り値となり、それを出力
goodbye again   ### ブロックの戻り値(goodbey againt)は、resumeの戻り値となり、それを出力
=> nil

ジェネレータ

  • ジェネレータとは
    • 部分的な結果だけを計算して返し、次回また続きから計算できるサブルーチン
      • 膨大な計算結果の配列を一度に構築せず、計算結果を最小限で済ます事ができる
フィボナッチ数を返すジェネレータ
[1] pry(main)> fib = Fiber.new {
[1] pry(main)*   a, b = 0, 1
[1] pry(main)*   loop do
[1] pry(main)*     a, b = b, a + b
[1] pry(main)*     Fiber.yield(a)
[1] pry(main)*   end
[1] pry(main)* }
=> #<Fiber:0x007fca24085a98>

### フィボナッチ数を5つ返す
[2] pry(main)> 5.times.map { fib.resume }
=> [1, 1, 2, 3, 5]

### 実行毎に1つフィボナッチ数を返す
[3] pry(main)> puts fib.resume
8
=> nil
[4] pry(main)> puts fib.resume
13
=> nil
[5] pry(main)> puts fib.resume
21
=> nil

Process

  • プロセスを管理するためのモジュール
    • デーモンのプロセスを生成することができる
    • プロセスを操作する事ができる
    • 子プロセスを複数用意して時間のかかる処理を複数のプロセスで処理する事ができる

子プロセスの生成

  • 子プロセスは親プロセスのコピーとしてスタートする
    • スレッドと違いメモリは共有されない
      • 親と子の間で同じ値を更新や参照することはできない
子プロセスの生成
### 子プロセスの生成
[1] pry(main)> pid = Process.fork {
[1] pry(main)*   puts 'From child process'
[1] pry(main)* }
From child process
=> 93272 ### 子供プロセスのpid

$ cat proccess.rb
=======================
pid = Process.fork

if pid
  puts "parent: #{$$}"
else
  puts "child: #{$$}"
end
=======================

$ ruby process.rb
=======================
parent: 95193
child: 95591
=======================

子プロセスの待ち合わせ

  • 親プロセスで子プロセスの終了を見届ける事で安全に終了できる
    • 子プロセスが終わる前に親プロセスが終了すると孤児プロセスになる
  • Process.waitpid : 親が終了する際に子の終了を待ち合わせる
  • Process.wait : 自身の子プロセスのうちいずれかが終了するのを待つ
    • 戻り値は終了した子プロセスのpid
  • Process.waitall : 全ての子プロセスが終了するのを待つ
    • 戻り値は終了した子プロセスのpidとProcess::Statusオブジェクトの配列の配列
  • Process.waitpid2, Process.wait2 : 終了した子プロセスのpidとProcess::Statusオブジェクトの配列を返す
子プロセスの待ち合わせ
$ cat proccess2.rb
=======================
pid = Process.fork {
   sleep 10
}

Process.waitpid pid
=======================

プログラムのデーモン化

  • Process.daemon : Rubyプログラムをデーモンとして起動させる
    • 実行中のプロセスは制御端末から切り離される
    • カレントディレクトリは/に移動する
    • 標準入出力、標準エラー出力は/dev/nullにリダイレクトされる
    • 引数
      • 第一引数 : falseにするとカレントディレクトリを/に移動しない
      • 第二引数 : falseにすると標準入出力、標準エラー出力は/dev/nullにリダイレクトしない
プログラムのデーモン化
### 現在時刻を返すTCPサーバ
$ cat nowtime.rb
=======================
require 'socket'

Process.daemon ### プロセスをデーモンとして動作させる

TCPServer.open 'localhost', 4423 do |server|
  loop do
    client = server.accept  ### クライアントからの接続を待ち受ける
    client.puts Time.now   ### 現在時刻を返す
    client.close
  end
end
=======================

### 時刻を取得
$ curl localhost:4423
=======================
2015-08-13 09:56:51 +0900
=======================

Struct

  • 構造体
    • 1つ以上のフィールドを持つクラスとして表現される
    • 複数のフィールドを持つ単純なクラスとしを簡潔に定義できる
    • フィールド名はシンボルで指定する
      • 文字列で指定する事もできるが、newの第一引数が文字列だった場合それが生成するクラスの名前(定数名)と解釈される

基本的な使い方

基本的な使い方
### Humanオブジェクトの作成
[1] pry(main)> Human = Struct.new(:age, :gender)
=> Human

### フィールドにアクセス
[2] pry(main)> human = Human.new(10, 'male')
=> #<struct Human age=10, gender="male">
[3] pry(main)> human.age
=> 10
[4] pry(main)> human.gender
=> "male"
[5] pry(main)> human.age = 20
=> 20

### ハッシュライクな使い方
[6] pry(main)> human[:age]
=> 20

### フィールド一覧の取得
[7] pry(main)> human.members
=> [:age, :gender]

### 繰り返し処理
[1] pry(main)> Foo = Struct.new(:one, :two, :three)
=> Foo
[2] pry(main)> foo = Foo.new('a','b','c')
=> #<struct Foo one="a", two="b", three="c">

### 各フィールドの値を出力する
[3] pry(main)> foo.each {|value| puts value}
a
b
c
=> #<struct Foo one="a", two="b", three="c">

### 各フィールド名の配列
[4] pry(main)> foo.members
=> [:one, :two, :three]

### フィールド名とペアで繰り返し
[5] pry(main)> foo.each_pair {|field, value| puts field, value}
one
a
two
b
three
c
=> #<struct Foo one="a", two="b", three="c">

### ブロックを実行した結果の配列を得る
[6] pry(main)> foo.map {|value| value.upcase}
=> ["A", "B", "C"]

### Structオブジェクトからハッシュを得る
[7] pry(main)> Hash[foo.each_pair.to_a]
=> {:one=>"a", :two=>"b", :three=>"c"}

メソッドの定義

メソッドの定義
### Struct.newにブロックを渡し、その中にインスタンスメソッドを定義する
[1] pry(main)> Human = Struct.new('Human', :age, :gender) {
[1] pry(main)*   def teen?
[1] pry(main)*     (13..19).include?(age)
[1] pry(main)*   end
[1] pry(main)* }
=> Struct::Human
[2] pry(main)> Human.new(10).teen?
=> false
[3] pry(main)> Human.new(14).teen?
=> true
[4] pry(main)> Human.ancestors
=> [Struct::Human,
 Struct,
 Enumerable,
 Object,
 PP::ObjectMixin,
 Kernel,
 BasicObject]

### Struct.newが返す無名のクラスを直接継承することでブロックではなくクラスの定義文脈で式を記述する事もできる
[1] pry(main)> class Human < Struct.new('Human', :age, :gender)
[1] pry(main)*   def teen?
[1] pry(main)*     (13..19).include?(age)
[1] pry(main)*   end
[1] pry(main)* end
=> :teen?

Marshal

  • オブジェクトを永続化するための機能を提供するモジュール
    • Marshal.dump : オブジェクトを永続か可能な文字列に変換する
    • Marshal.load : 文字列からオブジェクトを復元する
  • 使い方
    • Marshal.dumpで生成された文字列をファイルに書き込む
    • 別プロセスからそれを読み込む
    • Marshal.loadでオブジェクトを復元する
  • 永続化できないオブジェクト
    • 無名のクラス/モジュール、及びそれらを継承したクラスのインスタンス
    • Dir、File::Stat、IO、Socketなど、状態をシステムが保持しているオブジェクト
    • Proc、Method、UnboundMethod、Thread、ThreadGroup、Continuation、MatchData、Dataのインスタンス
    • 得意メソッドの定義されたオブジェクトで、marshal_dump、marshal_loadメソッドが定義されていないもの
    • 上記のいずれかを内包しているオブジェクト
  • バージョン
    • Marshal.dumpで書き出されたデータはMarshalのバージョン情報を含んでいる
    • 以下の場合Marshal.loadによりオブジェクトを生成することはできない
      • 自身のバージョンよりもdumpされたデータの方が新しい
      • メジャーバージョンが異なる
Marshal
[1] pry(main)> array = [1, '2', [3], Time.now]
=> [1, "2", [3], 2015-08-14 09:17:17 +0900]

### オブジェクトを文字列として書き出す
[2] pry(main)> m = Marshal.dump(array)
=> "\x04\b[\ti\x06I\"\x062\x06:\x06ET[\x06i\bIu:\tTime\r\xC0\xDD\x1C\x80\x8D\xB7\x17E\a:\voffseti\x02\x90~:\tzone\"\bJST"

### 書き出された文字列から元のオブジェクトを復元する
[3] pry(main)> Marshal.load(m)
=> [1, "2", [3], 2015-08-14 09:17:17 +0900]

### バージョンの確認
[4] pry(main)> Marshal::MAJOR_VERSION
=> 4
[5] pry(main)> Marshal::MINOR_VERSION
=> 8
  • カスタマイズ
    • 目的
      • Marshal.dump時に必要な情報だけを書き出す
    • 方法
      • Object#marshal_dumpとObject#marshal_loadをオーバーライドする
カスタマイズ
### Somethingクラスからインスタンス変数@sourceの情報だけ書き出す
[1] pry(main)> class Something
[1] pry(main)*   attr_accessor :source, :temporary
[1] pry(main)*   def marshal_dump
[1] pry(main)*     source
[1] pry(main)*   end
[1] pry(main)*   def marshal_load(s)
[1] pry(main)*     self.source = s
[1] pry(main)*   end
[1] pry(main)* end
=> :marshal_load
[2] pry(main)> origin = Something.new
=> #<Something:0x007f9a590ebf50>
[3] pry(main)> origin.source = 'necessary'
=> "necessary"
[4] pry(main)> origin.temporary = 'drop me'
=> "drop me"

### dump
[5] pry(main)> data = Marshal.dump(origin)
=> "\x04\bU:\x0ESomethingI\"\x0Enecessary\x06:\x06ET"

### リストア
[6] pry(main)> restored = Marshal.load(data)
=> #<Something:0x007f9a59142198 @source="necessary">

### @sourceの情報は復元されている
[7] pry(main)> restored.source
=> "necessary"

### @temporaryの情報は無い
[8] pry(main)> restored.temporary
=> nil

ObjectSpace

  • 実行中のRubyプロセス中に存在するあらゆるオブジェクトを操作するためのモジュール
    • ObjectSpace.each_object : kind_of?の関係にある全てのオブジェクトに対して繰り返し処理を行う
      • 引数が無ければすべてのオブジェクトを繰り返す
      • 結果には即値であるFixnum、Symbol、TrueClass、FalseClass、NilClassのオブジェクトは含まれない
    • Object#object_id : オブジェクトIDを出得する
      • Object#id も同様
    • ObjectSpace.id2ref : オブジェクトIDから実際のオブジェクトを得る
    • ObjectSpace.#define_finalizer : あるオブジェクトがGCやスクリプト終了時に解放される際に実行する処理を登録
      • 第一引数 : 対象オブジェクト
      • 第二引数 : ファイナライザとして実行するProcオブジェクト
ObjectSpace
### 定義済みのクラスを出力する
[1] pry(main)> ObjectSpace.each_object(Class) {|c| p c}
Pry::REPL
#<class(Pry::Command "reload-method")>
Gem::Dependency
Pry::Command::Wtf
Pry::Command::Gist
CodeRay::Scanners::Ruby
#<class(Pry::Command "@")>
...
#<class(Pry::Command "?")>
Pry::Method::Disowned
Gem::Requirement
#<class(Pry::Command "show-command")>
#<class(Pry::Command "edit-method")>
Pry::Method::WeirdMethodLocator
Pry::Command::DisablePry
=> 427

[2] pry(main)> a = [4,4,2,3]
=> [4, 4, 2, 3]

### オブジェクトIDの取得
[3] pry(main)> a.object_id
=> 70334829473900

### オブジェクトIDから実際のオブジェクトを得る
[4] pry(main)> ObjectSpace._id2ref(70334829473900)
=> [4, 4, 2, 3]

[5] pry(main)> o = Object.new
=> #<Object:0x007ff034972738>

### オブジェクト終了時の処理を登録
[6] pry(main)> ObjectSpace.define_finalizer(o, proc { puts 'finalizeing...'})
=> [0, #<Proc:0x007ff0349c1630@(pry):6>]
2
2
5

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?