Loading...

初めてのRuby

和田 卓人 (a.k.a @t_wada)

Press key to advance.

Slides controls, press:

  • and to move around.
  • Ctrl/Command and + or - to zoom in and out if slides don’t fit.
  • T to change the theme.
  • H to toggle syntax highlight.

自己紹介

自己紹介

  • 名前 : 和田 卓人(わだ たくと)
  • ブログ : id:t-wada
  • Twitter : @t_wada
  • github / facebook : twada

./../../images/TQ_LOGO_SMALL.png

プログラマが知るべき97のこと

SQLアンチパターン

よろしくお願いします

初めてのRuby

matzによる序文

Yugui さんはこの『初めてのRuby』を「Ruby が初めての人のための本」として明確に定義し、 リファレンスマニュアルほど詳細に立ち入ることなく、プログラミングについてある程度の経験がある人が、 Ruby ならではの雰囲気を感じて、Ruby の世界に旅立つために必要な情報をコンパクトにまとめています。

また、Ruby という言語を特徴づけている 文化 への言及を忘れていないところがにくいところです。 結局、プログラミング言語は、どのような機能があるかではなく、それを取り巻く文化や、 言語やライブラリ設計の背後にある哲学によって差別化されるからです。

章構成

  • 1章 ようこそ、Rubyのある生活へ
  • 2章 配列とハッシュ
  • 3章 数値
  • 4章 文字列
  • 5章 入出力

章構成(2)

  • 6章 変数と式
  • 7章 メソッド
  • 8章 オブジェクトとクラス
  • 9章 本書を越えて

1章 ようこそ、Rubyのある生活へ

1.1 Rubyの特徴

  • 1.1.1 オブジェクト指向言語
  • 1.1.2 より良いPerl
  • 1.1.3 動く擬似コード
  • 1.1.4 ALGOLの皮をかぶったLisp

1.1.1 オブジェクトとは(p.2)

  • アイデンティティを持っている
  • メッセージを受け取る
  • 内部状態を持つ

1.1.4 ブロック付きメソッド

3.times do
  puts "Hello!"
end

これは、別にRubyの言語本体が規定回数繰り返すためのループ構文を用意している訳ではありません。 3.timesというのは整数オブジェクトに対する単なるメソッド呼び出しです。 メソッド呼び出しにdo … endのコードブロックを引き渡しているのです。まるで、メソッドに引数を渡すかのように。

コードブロックを受け取るメソッドのことをRubyでは ブロック付きメソッド と呼びます。

定義も実行文

クラス定義やメソッド定義は通常のプログラムと異質な「宣言文」のようなものでは ありません。 数値同士を足したり文字列を出力したりするのと同じく 実行文 です。 したがって、計算や出力と同じようにして自在に制御することができるのです。

定義も実行文

class Duration
  %w[ days hours minutes seconds ].each do |name|
    attr_accessor name
  end
end

d = Duration.new # Duration オブジェクトを構築
d.days = 3 # 属性days を設定
d.hours = 5 # 属性hours を設定
p d.days #=> 3 属性days を出力

1.3.1 動的性

クラス定義が実行時に行われるというRubyの仕様はこれらとは大きく異なったものです。 何しろ、ユーザーの入力に応じてクラスの定義を切り替えることすら可能なのです。

# ユーザーの入力が 1 なら Bar から、そうでなければ Baz から派生する
class Foo < (user_input == "1" ? Bar : Baz)
  # ......
end

1.3.1 動的性とメタプログラミング

このようなRubyの動的性質は大きな柔軟性をもたらし、 メタプログラミング を可能とします。 人間がプログラムを書くのではなく、 プログラムがプログラムを処理する のです。 メタプログラミングは多くの利点を持ちます。 ソースコードを簡潔に保ってメンテナンスを容易にしますし、 似たようなコードを繰り返すつまらない作業から解放されます。

1.5.5 イテレータ

(1..100).each do |i|
  case i % 15
  when 0 then puts "FizzBuzz"
  when 3, 6, 9, 12 then puts "Fizz"
  when 5, 10 then puts "Buzz"
  else puts i
  end
end

1.5.6 オブジェクト

p 1.class           #=> Fixnum 固定長整数クラス
p 1.object_id       #=> 3 オブジェクトの、プロセス内で一意な番号
p 1.methods         #=> [:to_s, :id2name, ... 持っているメソッドの一覧
p "str".class       #=> String 文字列クラス
a, b = "str", "str" # 多重代入
p a.object_id       #=> 84650
p b.object_id       #=> 84620
p a == b            #=> true 文字列として同値
p a.equal? b        #=> false オブジェクトとしては同一でない。

1.5.6 クラス

p Fixnum.class               #=> Class クラスは、Class クラスのインスタンス
p Fixnum.object_id           #=> 108610 勿論、一意な番号を持つ
# 祖先クラスを問い合わせる
p Fixnum.ancestors           #=> [Fixnum, Integer, Precision, Numeric, ....
# インスタンスメソッドの一覧
p Fixnum.instance_methods    #=> [:to_s, :id2name, ....
# インスタンスのクラスへの所属判定
p 1.kind_of? Fixnum          #=> true
p "str".kind_of? Fixnum      #=> false
p Fixnum.kind_of? Class      #=> true
p Fixnum.kind_of? Object     #=> true

1.5.6 演算子の多態性

p "str" == "str"    #=> true 文字列の同値性比較
p 1 == 1.0          #=> true 数値の同値性比較
p 1.14 + 2          #=> 3.14 数値の加算
p "str" + "ing"     #=> "string" 文字列結合
p 1 << 2            #=> 4 ビットシフト
p ["a", "b"] << "c" #=> ["a", "b", "c"] 要素挿入
$stdout << "hoge"   #=> hoge ストリームへの出力

2章 配列とハッシュ

2章 配列とハッシュ

配列とハッシュは Ruby プログラミングにおける最も基本的なコンテナオブジェクトです。 共に他のオブジェクトへの参照を貯めておくことができます。

配列は他のオブジェクトへの参照を順番に並べたものです。 一方、ハッシュはあるオブジェクトを他のオブジェクトへ対応させます。

この2種類のオブジェクトに共通するのは、他のオブジェクトへの参照を保持するコンテナである点です。

2.1.1 配列の構築

a = 1
b = "str"
# 配列リテラル
c = [a, b, 3, " 文字列"] #=> [1, "str", 3, " 文字列"]
# ネストもできる
d = [a, c, [1, 2, 3]] #=> [1, [1, "str", 3, " 文字列"], [1, 2, 3]]

2.1.2 添字参照

p c[0] #=> 1
p c[1] #=> "str"
p c[2] #=> 3
p c[3] #=> " 文字列"
p c[4] #=> nil
p d[2] #=> [1, 2, 3]

2.1.2 負の添字

p c[-1] #=> " 文字列" c[c.length-1]と等価
p c[-2] #=> 3 c[c.length-2]と等価
p c[-5] #=> nil 最初の要素よりも前。範囲外。

ちなみに、配列への添字参照式は Array#[] メソッドを呼び出す式のシンタックスシュガーです。 a[0] という式は a.[](0) というメソッド呼び出しを人間が読みやすいように表記しただけのものです。 Ruby処理系は内部では両者をまったく同じように扱います。 演算子式の大半はメソッド呼び出しのシンタックスシュガーです。

2.1.2 範囲添字

p c[0..1] #=> [1, "str"] 末端を含む範囲
p c[0...1] #=> [1] 末端を含まない範囲
p c[-2..-1] #=> [3, " 文字列"] 負の添字との組み合わせ
p c[-2..3] #=> [3, " 文字列"]
p c[-2...3] #=> [3] 末端を含まない範囲
p c[4..5] #=> [] 配列の範囲外

2.1.3 添字代入

a = [1, 2]
a[0] = 3 #=> [3, 2]
a[4] = "4" #=> [3, 2, nil, nil, "4"]
a[0, 3] = 'a', 'b', 'c' #=> ["a", "b", "c", nil, "4"]
a[0, 3] = 'a', 'b', 'c', 'd' #=> ["a", "b", "c", "d", nil, "4"]
a[1..2] = 1, 2 #=> ["a", 1, 2, "d", nil, "4"]
a[0, 2] = "?" #=> ["?", 2, "d", nil, "4"]
a[0..2] = "A" #=> ["A", nil, "4"]
a[-1] = "Z" #=> ["A", nil, "Z"]

2.1.3 添字代入の裏側

実は、添字代入式は添字参照式とはまったくの別物です。 後者は Array#[] メソッドの呼び出しでしたが、前者は Array#[]= メソッドの呼び出しです。 両者はできるだけ一貫性を保つように実装されています。 けれども、本質的には異なるメソッドなので細かな挙動には違いがあります。例えば、以下のようになります。

a = ["a", "b"]
a[-3] #=> nil 範囲外のアクセスに対してはnil を返す
a[-3] = 1 #=> IndexError 例外を発生

2.1.5 様々なメソッド

array = ["a", "b", "c"]
p array.length #=> 3
p array.size #=> 3 (length の同義語)
p array *= 2 #=> ["a", "b", "c", "a", "b", "c"]
p array.include? "c" #=> true 特定の値を含むかどうか
p array.sort #=> ["a", "a", "b", "b", "c", "c"] 新しい配列を生成して返す
p array #=> ["a", "b", "c", "a", "b", "c"] 元の配列は変化していない
p array.uniq #=> ["a", "b", "c"] 重複要素を削除した配列を生成して返す
array.uniq! # 元の配列自体を更新する。
p array #=> ["a", "b", "c"]

配列に特定の値が含まれているかどうかを調べるメソッドは Array#include? です。 真偽値を返すメソッド にはこのように 最後に疑問符「?」を付けて命名する慣習 があります。

2.1.6 ブロック付きメソッドとイテレータ

array = ["a", "b", "c"]
array.each do |item|
  print item + " " #=> a b c
end

2.1.6 インデックス付きの繰り返し

array.each_with_index do |item, index|
  p [item, index]
end
#=> ["a", 0]
#   ["b", 1]
#   ["c", 2]

2.1.6 写像(map)

acids = ["Adenin", "Thymine", "Guanine", "Cytosine"]
signs = acids.map{|acid| acid[0,1] }
p signs #=> ["A", "T", "G", "C"]

ブロックの評価値は、原則としてブロック内における最後の式の値です。 ただし、6章で見るようにnext 式を用いるとブロックの途中で値を返すことができます。

2.2 ハッシュ

prefix = "yapoo-"
abbreviation = {
  "CAT" => "Condensed-Abridged Tiger",
  "Yapomb" => prefix + "womb",
  "pilk" => prefix + "milk"
}

Ruby 1.9では少し短いリテラルも導入されました。これはシンボルをキーとするハッシュの略記法です。

params = { rin: 5, kimiko: 7, kayo: nil }
p params #=> {:rin => 5, :kimiko => 7, :kayo => nil}

2.2.1 メソッド呼び出しとハッシュ

register_user { :rin => 'clara', :kayo => 'pauline' }

実は、メソッド呼び出しの最後の引数としてハッシュリテラルを渡す場合、波括弧を省略できます。 これを用いればブロックと間違えられることはありません。

register_user :rin => 'clara', :kayo => 'pauline'
register_user rin:'clara', kayo:'pauline' # Ruby 1.9 のみ

2.2.2 添字演算式

book_to_author = {
  "Ruby in Nutshell" => "Flanagan",
  "Programming Ruby" => "Thomas",
  "AWDwR" => "Thomas"
}
p book_to_author["Programming Ruby"] #=> "Thomas"
# 存在しないキーの場合
p book_to_author["Programming Perl"] #=> nil
# 既存のキーの更新
book_to_author["Ruby in Nutshell"] = ["Flanagan", "Matz"]
# 新しいキーの登録
book_to_author["The Ruby Way"] = "Fulton"

2.2.3 ハッシュの比較

hash1 = { "a" => 1, "b" => 2 }
hash2 = { "a" => 1, "b" => 2 }
p hash1 == hash2 #=> true
p hash1 == { "b" => 2, "a" => 1 } #=> true 順序は関係ない
p hash1 == { "a" => 9, "b" => 2 } #=> false 値が違う
p hash1 == { "z" => 1, "b" => 2 } #=> false キーが違う
p hash1 == { "a" => 1, "b" => 2, "c" => 3 } #=> false: 余分な要素

2.2.4 様々なメソッド

book_to_author.each do |book, author|
  puts "#{book} by #{author}"
end
p book_to_author.map{|book,author|
  "#{book} by #{author}"
}
#=> ["Ruby in Nutshell by Flanagan", "Programming Ruby by Thomas", ....

2.3 Enumerableモジュール

モジュール とは、 共通のクラスを継承することなしに実装を共有するための仕組み です。 例えば、each が定義されている任意のクラスにおいて、map は次のように機械的に導出できます。

class Foo
  def each
    # ... 何らかの実装
  end
  def map
    result = []
    self.each {|item| result << yield(item)}
  end
end

2.3 Enumerableモジュール

Enumerable はこのようなeach メソッドから導出可能なメソッドを集めたモジュールです。 each という名前のイテレータを持っているクラスは、Enumerableを継承(Rubyの言い方ではinclude)すると、 eachから導かれた便利なメソッドを多数得ることができます。

3章 数値

数値

Rubyは組み込みで 多倍長整数 をサポートしています。 このため 桁あふれを心配せずに大きな数を扱う ことができます。 Rubyで数値を計算する上で最大の難点は計算が遅いことでしたが、これはRuby 1.9 において大幅に改善されました。

3.1 数値リテラル

1 # 正の整数(10 進数表記)
-2 # 負の整数(10 進数表記)
+1 # 正の整数(符号を明示)
1000000000000000000000000000 # とても大きな整数
100_000_000_000 # 可読性のための任意のアンダースコア

リテラルの先頭に記号を付け加えることで2 進数、8 進数、16 進数のリテラルを記述できます

0xDEADBEEF # 16 進数表記の整数(3735928559)
-0xCAFE # 負の16 進数(-51966)
0xDEAD_BEEF_CAFE_BABE # 大きな16 進数(16045690984503098046)
01755 # 0 から始まる数は8 進数表記と解釈される(1005)
0b1010111 # 2 進数表記(87)

3.1.2 浮動小数点リテラル

0.5
2.71828182845905
-273.15 # 負の小数
1 # これは整数リテラルと解釈される。
1.0 # Float オブジェクトを構築するにはこう書く
6.00221415e23 # 指数表記。6.00221415 × 1023
6.626_068_96e-34 # 指数表記。6.62606896 × 10-34

3.2 数値演算

10 + 2 #=> 12 加算
5.25 - 7 #=> -1.75 減算
4 * 5 #=> 20 乗算
3 ** 5 #=> 243 べき乗
7 / 2 #=> 3 整除
7 % 2 #=> 1 剰余
7.0 / 2 #=> 3.5 実数除算
7 / 0 #=> ZeroDivisionError ゼロ除算例外
7.0 / 0 #=> Infinity

3.2.5 型と自動変換

Rubyは 強く型付けされた言語 なので、データ型が自動変換されることはほとんどありません。 データはいつでも特定のクラスに属しており、演算の最中にいつの間にか他のクラスに属しているということはありません。

1 + "2".to_i # => 3

整数と浮動小数点値の演算は例外的に自動変換されるケースの1つです

1.0 + 1 #=> 2.0
7 / 2.0 #=> 3.5

3.3 比較

1 == 1 #=> true 同値
1 == 1.0 #=> true 整数と浮動小数点の同値
1 != 1 #=> false 非同値
1 < 1 #=> false 小さい
1 <= 1 #=> true 小さいかまたは等しい
2 > 1 #=> true 大きい
2 >= 1 #=> true 大きいかまたは等しい

3.3.1 宇宙船演算子

1 <=> 2 #=> -1
2 <=> 2 #=> 0
3 <=> 2 #=> 1

sortメソッドには要素の大小関係を判定するためのブロックを渡すことができます。 このブロックを記述する際にも宇宙船演算子が活躍します

array.sort{|x,y| # 比較すべき2 つの要素がブロックパラメータとして渡される
  # 属性some_attribute の大小関係によってx とy の大小関係を決定する
  x.some_attribute <=> y.some_attribute
}

4章 文字列

4.1.1 文字列リテラル

"Hello" #=> "Hello"
" 日本語" #=> " 日本語"
'single quoted' #=> "single quoted"
' 改行
を含む文字列' #=> " 改行\n を含む文字列"

4.1.3 式展開

a = 2
"a の値は#{a}" #=> "a の値は2"
"a の5 乗は#{a**5}" #=> "a の5 乗は32"
" 現在時刻: #{Time.now}" #=> " 現在時刻: 2008-03-03 00:59:01 +09:00
" 標準入力の文字列表現: #{$stdin}" #=> " 標準入力の文字列表現: #<IO:0x2b7f0>"

4.1.6 ヒアドキュメント

count = database_connector.get_int(<<"EOS" % author.id)
  SELECT COUNT(*)
  FROM book
  WHERE book.author_id = %d
EOS

4.3 正規表現

/I went (Lilliput|Brobdingnag|Laputa)/
if /<a href="(.*?)"[ >]/ =~ str
  puts $1 + " へのリンクを発見"
end

4.5.2 文字列操作: 文字列の分解

"a,bb, ccc, dddd".split(/,\s?/) #=> ["a", "bb", "ccc", "dddd"]
"string".split(//) #=> ["s", "t", "r", "i", "n", "g"]

4.5.2 文字列操作: その他

"string".reverse #=> "gnirts"
"\n \rstring ".strip #=> "string"
"string".length #=> 6

4.6 イテレータ

story.each_line do |line|
  print line
end

4.7 フォーマット

sprintf("%04d", 3) #=> "0003" 数値を0 埋めで4 桁に。
sprintf("%08.4f", Math::PI*10) #=> "031.4159"
sprintf("hex=%X, oct=%o", 10, 10) #=> "hex=A, oct=12" 16 進、8 進表記
"%04d" % 3 #=> "0003"
"%08.4f" % (Math::PI*10) #=> "031.4159"
"hex=%X, oct=%o" % [10, 10] #=> "hex=A, oct=12"

4.8 シンボル

Rubyにおける シンボル(Symbol) とは、大まかに言ってintern された文字列のようなものです。 シンボルオブジェクトを取得するには、このようにシンボルリテラルを用いるのが一般的です。 コロンの後に続けて、引用符で括って内容を記述します。

:"Anna Terras" # 空白を含むシンボル
:"#{$$}" # 式展開
:"\xE3\x81\x82" # バックスラッシュ記法
:'#{$$}' # 一重引用符では、式展開などは無効
:if # Ruby の識別子として妥当
:some_method_name # Ruby の識別子として妥当

4.8.1 シンボルの性質と用途

symbol1 = :ruby
symbol2 = :ruby
symbol1 == symbol2 #=> true
symbol1.equal? symbol2 #=> true

Rails における疑似キーワード引数の例

Yapomb.find :select => "name", :conditions => ["argentaqua_state > ?", 90]
Yapomb.find select: "name", conditions: ["argentaqua_state > ?", 90]

4.10 Ruby 1.9とマルチバイト文字列

" あいう".encoding #=> #<Encoding:UTF-8>
" あいう".length #=> 3 文字数
" あいう".bytesize #=> 9 従来のlength のようなバイト長
Encoding.name_list
  #=> ["ASCII-8BIT", "UTF-8", "US-ASCII", "Big5", "CP949", "EUC-JP", ....
Encoding.find("CP1258")
  #=> #<Encoding:Windows-1258>

4.10.2 マジックコメントとコード変換

# -*- coding: utf-8 -*-
utf = " 日本"
p utf.encoding #=> #<Encoding:UTF-8>
sjis = utf.encode("Shift_JIS")
p sjis.encoding #=> #<Encoding:Shift_JIS>

5章 入出力

5章 入出力

  • 5.1 プログラムへの引数
  • 5.2 ファイル
  • 5.3 標準入出力
  • 5.4 その他の入出力オブジェクト
  • 5.5 エンコーディング

割愛!

6章 変数と式

6.1.2 変数の種類

p.99 の表を読みましょう

6.1.3 スコープ

def next_of(origin)
  value = origin + 1
  p value
end
def prev_of(origin)
  value = origin - 1
  p value
end

next_of(2) # => 3
next_of(2) # => 3
prev_of(2) # => 1
p value # => NameError

p undefined_variable # => NameError

6.2.1 再定義可能な演算子

| ^ & <=> == === =~ > >= < <= << >>
+ - * / % ** ~ +@ -@ [] []=

自己代入演算子は再定義できません。ただし、a += b は常にa = a+b のように解釈されるので、 二項演算子+を再定義すればそれに合わせて実質上意味を変更できます

+= -= *= /= %= **= <<= >>= |= &= ^= &&= ||=

6.2.2 再定義不能な演算子

= ?: .. ... ! not && and || or ::

6.3.2 case式

array = [4, 5]
case value
when 1 then
  do_something1 # value が1 の場合
when 2, 3 then
  do_something2 # value が2 または3 の場合
when 3 # then は省略可
  not_executed # when 節は上から順にチェックされるのでここに来ることはない
when *array # 条件を外部から持ってくる場合、配列展開可能
  do_something3 # 4 または5 の場合
else
  do_something_other
end

6.3.2 case式

value = 3
case value
when 0 then '0'
when 1..9 then '1 けた'
when 10..99 then '2 けた'
end
value = "3"
case value
when '0' then '0'
when /\A\d\Z/ then '1 けた'
when /\A\d{2}\Z/ then '2 けた'
else ' それ以外'
end

6.3.2 case式

case
when number.prime? then process_prime(number)
when number.fermat? then process_carmichel(number)
when number.odd? then process_odd_composite(number)
else process_even_composite(number)
end

6.3.6 イテレータ

3.times{ puts "Yahoo" }
3.times{|i| puts i }
1.upto(3) do |i|
  puts i
end

6.3.7 脱出

3.times do
  3.times do
    break
  end
  # ← ここに脱出する
end
1.upto(3) do |i|
  next if i == 2
  puts i
end
# => 1
# 3

6.4 例外処理

begin
  do_something # 例外を生じる可能性のある処理
rescue ArgumentError => error then # then は省略可能
  puts error.message
rescue TypeError # 例外補捉変数は省略可能
  # 何かの処理
rescue => another_error # クラスは省略可能
  puts another_error.message
else
  puts " 例外なし" # 例外がなかった場合
ensure
  puts "ensure 節"
end

7章 メソッド

7.1.1 括弧の省略

a.some_method 1, "str"
# 曖昧な例
p a.some_method 1, "str"
  # => warning: ambiguous first argument; put parentheses or even spaces

7.1.2 メソッド連鎖

escaped_str = str.gsub(/&/, '&amp;').gsub(/</, '&lt;').gsub(/>/, '&gt;')

7.2 メソッドの定義: def式

def sum(x, y)
  x + y
end
def diff x, y # 括弧を省略するスタイル
  x - y
end
def prod(x, y) x * y end # 1 行にまとめるスタイル
def quo x, y; x / y end # 括弧を省略して1 行にまとめるスタイル

sum(1, 2) #=> 3
diff(1, 2) #=> -1
prod(1, 2) #=> 2
quo(1, 2) #=> 0

7.2.2 return

def fact(n)
  return 1 if n == 0 # 0 の場合は打ち切って1 を返す。
  product = 1
  (1 .. n).each do |i|
    product *= i
  end
  return product
end

ちなみに、returnは必須ではありません。 returnに出会うことなくメソッド末尾に到達すると、 最後の式の値が戻り値になります

7.2.3 デフォルト値

# c は省略可能。デフォルト値は1
def some_method(a, b, c = 1)
end

7.2.4 可変長引数

def some_method(a, b, *c)
  p [a, b, c]
end
# 余分な引数の3, 4, 5 は仮引数c に割り当てられる
some_method(1, 2, 3, 4, 5) #=> [1, 2, [3, 4, 5]]

1.9 からは

def some_method(a, *b, c)
  p [a, b, c]
end
some_method(1, 2, 3, 4, 5) #=> [1, [2, 3, 4], 5]

7.3 ブロック付きメソッド

verse = " 桃之夭夭 灼灼其華\n 之子于帰 宜其室家\n"
count = 0
verse.each_line do |line|
  print line
  count += 1
end

7.3.2 ブロック引数とブロックローカル変数

a = "str"
[1, 2, 3].each{|a| p a } # このa は、上のa とは別物
p a #=> "str"
a, b = "str", "ing"
[1, 2, 3].each do |i; a, b| # このa, b は上のa, b とは別物
  a, b = 1, 2 # 外側のa,b には影響しない
end
p a, b #=> "str", "ing"

7.3.3 ブロック付きメソッドの定義

def foo_bar_baz
  yield "foo"
  yield "bar"
  yield "baz"
end

foo_bar_baz do |item|
puts item
end
#=> foo
# bar
# baz

7.3.4 Proc

proc = Proc.new { puts "Proc was called" }
3.times(&proc)
#=> Proc was Called
# Proc was Called
# Proc was Called

8章 オブジェクトとクラス

8.1 クラス定義

# クラスDuration を定義
class Duration
  1 + 1 # 任意の式を書いて良い
end

継承

class Duration < Range
end

8.1.2 インスタンスメソッドの定義

class Duration
  def display
    puts self
  end
end

duration = Duration.new
duration.display #=> #<Duration:0x3c1e8c>

8.1.3 クラスメソッドの定義

class Duration
  def Duration.print(x)
    p x
  end
end
Duration.print 1 #=> 1
class Duration
  def self.print(x)
    p x
  end
end
Duration.print 1 #=> 1

8.1.4 インスタンス化

class Duration
  def initialize(since, till)
    @since = since
    @until = till
  end
  attr_accessor :since, :until # 属性へのgetter とsetter を定義する
end

duration = Duration.new(Time.now, Time.now + 3600)
p duration.until #=> Fri Feb 01 18:56:19 +0900 2008
p duration.since = Time.now #=> 属性の設定

duration.since += 10
# duration.since=(duration.since() + 10) と同じ

8.1.5 attr_accessor の内部で生成されるメソッド

class Duration
  def initialize(since, till)
    @since = since
    @until = till
  end
  attr_accessor :since, :until # 属性へのgetter とsetter を定義する
end
def since; return @since end # インスタンス変数から値を取得
def since=(value); @since = value end # インスタンス変数に値を設定
def until; return @until end
def until=(value); @until = value end#+END_EXAMPLE

8.1.6 オープンクラス

class String # 文字列クラスの定義を再開する
  # カエサル暗号を施すメソッドを追加する
  def caesar
    tr 'a-zA-Z', 'n-za-mN-ZA-M'
  end
end

puts "Learning Ruby".caesar #=> Yrneavat Ehol

8.1.6 定義の上書き

class Fixnum
  alias original_addition + # 元の定義を別のメソッド名で退避する
  def +(rhs) # 再定義
    original_addition(rhs).succ
  end
end

p 1+1 #=> 3
p 5+2 #=> 8

8.3 アクセス権限

  • public どこからでも呼び出すことができます。
  • protected そのクラスまたはサブクラスのインスタンスメソッドからしか呼び出すことができません。

8.3 アクセス権限

  • private レシーバ省略形式でしか呼び出すことができません。したがってselfに対してしか呼び出すことができません。これは、同じクラスに属するオブジェクトであっても、他のオブジェクトに対しては呼び出せないということを意味します。

8.4.1 特異メソッド

message = "Hello" # 文字列オブジェクト

def message.build_greeting(target)
  "#{self}, #{target}."
end

message.build_greeting("world") #=> Hello, world.
message2 = "Hello" # 値は同じだが異なるオブジェクト
message2.build_greeting("world") #=> NoMethodError

8.4.3 クラスメソッド

class Duration # 再オープン
  def self.a_week_from(from)
    return self.new(from, from+7*24*60*60)
  end
end

p Duration.a_week_from(Time.now)

8.4.3 メタクラス

class Duration # 再オープン
  class << self # self(=Duration)の特異クラスを定義
    def a_week_from(from)
      return self.new(from, from+7*24*60*60)
    end
  end
end

p Duration.a_week_from(Time.now)

8.5 モジュール

class Foo
  include Comparable
  def <=>(rhs)
    # 何らかの実装
  end
end

foo1 = Foo.new
foo2 = Foo.new
foo1 <= foo2 # Comparable が提供。

8.5.2 名前空間

class Service
end

module Library
  class Service
  end
end
::Service # トップレベルのService
Library::Service # Library のService

8.6 メソッドの探索

p.163 の 図8-6 を参照

Q&A

ご清聴ありがとうございました