LoginSignup
11
14

More than 5 years have passed since last update.

Ruby覚え書き

Last updated at Posted at 2015-05-26

概要

Ruby覚え書き。常時更新です。

Rubyについて

Rubyとは

  • シンプルな文法と動的な型付け機構を持ち、オブジェクト指向をベースとしたプログラミング言語
    • 全てがオブジェクト
    • プリミティブ型は存在しない
      • 1などの数値やtrue/falseのような真偽値もオブジェクト
      • 一見プリミティブ型のような値もオブジェクトのためメソッドを呼び出す事が可能
script1
### to_sメソッドを呼び出し
p 1.to_s  # "1"
p true.to_s  # "true"

バージョンと歴史

  • 1.0〜1.8系 1996 - 2008
  • 1.9〜2.X系 2007 -
    • 1.8系までとの違い
      • RubyGemsが使える
      • RakeがRuby本体にバンドルされる

記法について

  • Array#each : Arrayクラスのインスタンスメソッドeach
  • Thread.fork : Threadクラスのクラスメソッドfork
  • Math.#sqrt : Mathモジュールのモジュール関数sqrt

ドキュメント

文字コード

  • 1.9系以降は文字列(String)自体にエンコード情報を持つ
    • 他の文字コード同士での文字列連結を行うと例外が発生する
    • 1.9系はマルチバイトを含む場合、文字コードを明示しないとUS-ASCIIと判断される
      • UTF-8を使う場合はマジックコメントの「# encoding:utf-8」をスクリプトの先頭に記載する必要がある
    • 2.0.0以降はデフォルトの文字コードがUTF-8で認識されるようになった

pryを使う

  • REPLと呼ばれる対話形式でRubyを動作確認することができるコマンド
    • 手軽に構文やメソッドの動作を調べる事ができる
script3
[1] pry(main)> puts "hello"
hello
=> nil
[2] pry(main)> class Sample
[2] pry(main)*   def say
[2] pry(main)*     puts "hello"
[2] pry(main)*   end
[2] pry(main)* end
=> :say
[3] pry(main)> sample = Sample.new
=> #<Sample:0x007f52b3f119b0>
[4] pry(main)> sample.say
hello
=> nil

詳細は Ruby関連ツール を参照。

コーディングスタイル

Ruby実装

Rubyには複数の実装が存在する

  • MRI(Matz' Ruby Implementation) または CRuby
    • 本家のRuby。主にC言語で実装されているのでCRubyと呼ぶ事もある。
  • JRuby
    • Javaで実装されている。Javaライブラリを利用する事が可能。
  • MacRuby
    • Mac上のCocoaフレームワークなどをシームレスに扱えるよう変更を加えたRuby実装。
  • Rubinius
    • Rubyによって実装されたRuby実装。
  • mruby
    • 組み込み用途向けの軽量版Ruby

コミュニティ

メソッド

  • オブジェクトに何らかの処理を行わせる場合はメソッドとして定義しておく
    • クラスの中で定義する

メソッドの定義式

メソッド定義式
# encoding:utf-8

def hello(names)  ### メソッドを定義。[def メソッド名(仮引数...)...end]
  names.each do |name|  ### 配列の要素を繰り返す制御をしている。「|name|」のnameはブロックの仮引数
    puts "HELLO, #{name.upcase}"  ### ダブルクオートで囲むと#{...}でRubyのコードを埋め込める。upcaseメソッドは文字列を大文字にする。[レシーバ.メソッド名]でメソッドを呼び出せる。
  end
end

rubies = ['MRI', 'jruby', 'rubinius'] ### 変数への代入。[変数名 = 式]。rubiesはローカル変数。

hello(rubies) ### メソッドを呼び出し。引数として直前に定義した変数rubiesを渡している。

トップレベル

  • クラスの定義式やモジュールの定義式など、定義式の外は「トップレベル」と呼ばれる
  • トップレベルで定義されたメソッドはグローバルなサブルーチンのように使用できる

    • メソッド呼び出しの際レシーバを記述しない
    • いわゆる関数のように、どこからでもグローバルに呼び出す事ができる(一部の例外を除く)
  • 参考

メソッドの定義と呼び出し

  • 様々な記法がある

    • 適切な記法を適切な場面で使うことで読みやすいプログラムになる
  • メソッド呼び出しと括弧

    • 括弧は省略することができる
      • 省略するかどうかはプログラマによる。
        • 一つの基準
          • 戻り値を得るための式として記述する場合は括弧をつける
          • 手続きを実行するための文として記述するときは省略する
メソッド呼び出しと括弧
### メソッド呼び出し
[1] pry(main)> puts ('hello')
hello
=> nil

### 括弧を省略
[2] pry(main)> puts 'hello'
hello
=> nil  ### 戻り値が無いものは括弧を省略

### 戻り値を使う式として書く場合
[3] pry(main)> member = find_member_by_name(name)

### 戻り値を使わない宣言文としてのメソッド呼び出しの場合
[4] pry(main)> set_current member

### 引数無しのメソッド呼び出しの場合
[5] pry(main)> member = find_member_first
  • メソッド呼び出しとローカル変数
    • レシーバと括弧、引数の無いメソッド呼び出しは同じ名前のローカル変数と区別がつかない
      • メソッドを呼び出したい場合は括弧を明示する
メソッド呼び出しとローカル変数
[1] pry(main)> sweet = 'honey' ### ローカル変数
=> "honey"
[2] pry(main)> def sweet  ### 引数の無いメソッド
[2] pry(main)*   'salt'
[2] pry(main)* end
=> :sweet

### 括弧が無いとローカル変数へのアクセス
[3] pry(main)> sweet
=> "honey"

## 括弧をつけるとメソッドを呼び出せる
[4] pry(main)> sweet()
=> "salt"
  • メソッドと定数
    • メソッド名はhello_worldのようにスネークケースで記述するのが一般的
    • HelloWorldのように大文字でも間違いではない
      • KernelモジュールにはInteger()のようなメソッドもある
      • 大文字で始まるメソッドをレシーバ・引数無しで呼び出す場合括弧を省略できない
メソッドと定数
### 大文字でメソッドを定義
[1] pry(main)> def HelloWorld
[1] pry(main)*   puts "Hello, World!!"
[1] pry(main)* end
=> :HelloWorld

### 括弧を省略するとエラーになる
[2] pry(main)> HelloWorld
NameError: uninitialized constant HelloWorld
from (pry):6:in `__pry__'

### 括弧を付けるとメソッドが呼び出せる
[3] pry(main)> HelloWorld()
Hello, World!!
=> nil
  • メソッドと戻り値
    • メソッドの戻り値は、そのメソッドの中で最後に評価された式の値になる
      • returnを記述する必要は無い
    • returnを記述するとそれがメソッドの戻り値となる
    • このようなメソッド呼び出しのレシーバはselfと呼ばれる疑似変数でから得られる
メソッドの戻り値
[1] pry(main)> def add(a,b)
[1] pry(main)*   a + b    ### returnは不要
[1] pry(main)* end
=> :add
[2] pry(main)> add(1,2)
=> 3

[3] pry(main)> def add2(a,b)
[3] pry(main)*   a + b
[3] pry(main)*   puts "hello"
[3] pry(main)* end
=> :add2
[4] pry(main)> add2(1,2)
hello     ### 最後のputsの結果が出る
=> nil

[5] pry(main)> def add3(a,b)
[5] pry(main)*   return a + b
[5] pry(main)*   puts "hello"
[5] pry(main)* end
=> :add3
[6] pry(main)> add3(1,2)
=> 3  ### returnのa+bが戻り値
  • 省略可能な仮引数
    • デフォルト値として任意の式を与える事ができる
      • デフォルト値のある仮引数は「省略可能な仮引数」と呼ばれる
省略可能な仮引数
### messageは省略可能な仮引数
[1] pry(main)> def greet(name, message = 'Hi')
[1] pry(main)*   "#{message}, #{name}."
[1] pry(main)* end
=> :greet

### デフォルト値の「Hi」が仮引数に代入されている
[2] pry(main)> greet 'Ruby'
=> "Hi, Ruby."

### 仮引数に値を代入
[3] pry(main)> greet 'Ruby', 'Hello'
=> "Hello, Ruby."

### 仮引数にnilを渡しても仮引数は使われない。nilが使われる。
[4] pry(main)> greet 'Ruby', nil
=> ", Ruby."

### suffixをもつメソッドを定義。省略可能な仮引数はいくつでも記述できる
[5] pry(main)> def greet(name, message = 'Hi', suffix = '.')
[5] pry(main)*   "#{message}, #{name + suffix}"
[5] pry(main)* end
=> :greet
[6] pry(main)> greet 'Ruby'
=> "Hi, Ruby."
[7] pry(main)> greet 'Ruby', 'Hello'
=> "Hello, Ruby."
[8] pry(main)> greet 'Ruby', 'Hello', ':)'
=> "Hello, Ruby:)"

### 途中の引数を省略して呼び出す事はできない
[9] pry(main)> greet 'Ruby', ':)'
=> ":), Ruby."
  • 可変長引数
    • 先頭に「*」をつける事で任意の数の引数を配列として受け取る事ができる
    • 一つのメソッドに一つだけ指定する事ができる
可変長引数
### 「Hello」と「こんにちは」が配列として受け取る
[1] pry(main)> def greet(name, *messages)
[1] pry(main)*   messages.each do |messages|
[1] pry(main)*     puts "#{messages}, #{name}."
[1] pry(main)*   end
[1] pry(main)* end
=> :greet
[2] pry(main)> greet 'Ruby', 'Hello', 'こんちちは'
Hello, Ruby.
こんちちは, Ruby.
=> ["Hello", "こんちちは"]

### 単に引数を無視したいときに「*」が使える
[2] pry(main)> def greet(name, *)
[2] pry(main)*   puts "#{name}"
[2] pry(main)* end
=> :greet
[3] pry(main)> greet 'Ruby', 'Hello', 'こんちちは'
Ruby
=> nil

### 可変長引数を利用
[1] pry(main)> def foo(*a)
[1] pry(main)*   a
[1] pry(main)* end
=> :foo

### 結果は配列で返る
[2] pry(main)> foo(1)
=> [1]
[3] pry(main)> foo(1,hello)
=> [1, "hello"]

### 通常の引数
[4] pry(main)> def bar(b)
[4] pry(main)*   b
[4] pry(main)* end
=> :bar

### 結果はその型で返る
[5] pry(main)> bar(1)
=> 1

### 複数していするとエラーになる
[6] pry(main)> bar(1,2)
ArgumentError: wrong number of arguments (2 for 1)
from (pry):6:in `bar'

### bは引数をひとつしか取れない
  • 配列の展開
    • 実引数の頭に「*」をつけると配列を複数の引数として渡す事ができる
    • 配列をメソッド呼び出しの引数リストとして渡したいときに便利
配列の展開
### greet_twiceメソッドは引数を三つ取る
[1] pry(main)> def greet_twice(name, first_message, second_message)
[1] pry(main)*   puts "#{first_message}, #{name}"
[1] pry(main)*   puts "#{second_message}, #{name}"
[1] pry(main)* end
=> :greet_twice
[2] pry(main)> greetings = %w(Hello Hola)
=> ["Hello", "Hola"]

### 呼び出し時には2つの変数しか渡していない
### 配列greetingsが*によって複数の引数として展開され、要素がそれぞれ代入されている
[3] pry(main)> greet_twice 'Ruby', *greetings
Hello, Ruby
Hola, Ruby
=> nil

ブロック

  • 式の並び・手続きをメソッド呼び出しのパラメータとして利用するための仕組み
    • メソッドはブロックを受け取ることができる
    • メソッドは受け取ったブロックを任意のタイミングで任意の回数実行することができる
eachメソッドとブロック
### 配列のeachメソッドはブロックを受け取り、要素の数だけブロックを実行する
[1] pry(main)> %w(Alive Bob Charlie).each do |name|
[1] pry(main)*   puts "Hello, #{name}."
[1] pry(main)* end
Hello, Alive.
Hello, Bob.
Hello, Charlie.
=> ["Alive", "Bob", "Charlie"]
  • 記法
    • メソッド呼び出しでブロックを渡す
      • do ... end
        • ブロックの中が複数行になるとき
        • ブロックを、手続きを実行する文として書く場合
      • ブレース {...}
        • ブロックの中が1行でおさまるとき
        • ブロックを、値を返す式として書く場合
        • do...endよりも強く結合する
記法
### ブロックはmethod2に適用される
method1 argment, method2 {...}

### 括弧を省略しなければmethod1の引数として使える
method1(argment, method2) {...}

### もしくはdo...endを使う
method1 argment, method2 do
  ...
end
  • yield
    • メソッドの中で呼び出すと受け取ったブロックを実行する
    • ブロックを渡さないと例外が発生する
      • block_given?メソッドを使うと回避できる
      • Fileクラスのopenメソッドはこの仕組を使っている
    • ブロックの戻り値を返す
      • ブロックの戻り値は最後に評価された式の値
yield
[1] pry(main)> def block_sample
[1] pry(main)*   puts 'stand up'
[1] pry(main)*   yield    ### ここでブロックが実行される
[1] pry(main)*   puts 'sit down'
[1] pry(main)* end
=> :block_sample

### ブロックを渡してメソッドを呼び出す
[2] pry(main)> block_sample do
[2] pry(main)*   puts 'walk1'
[2] pry(main)*   puts 'walk2'
[2] pry(main)* end
stand up
walk1   ### ブロックが実行される
walk2
sit down
=> nil

### ブロックを渡さないと例外が発生する
[3] pry(main)> block_sample
NameError: undefined local variable or method 'block_sample' for main:Object
from (pry):1:in '__pry__'

### ブロックが無くても例外が発生しないようにするにはblock_given?メソッドを使う
[4] pry(main)> def block_sample
[4] pry(main)*   puts 'stand up'
[4] pry(main)*   yield if block_given?
[4] pry(main)*   puts 'sit down'
[4] pry(main)* end
=> :block_sample
[5] pry(main)> block_sample
stand up
sit down
=> nil

### ブロックを使わずにFile.openでファイルに書き込み
[1] pry(main)> file = File.open('hoge.txt','w')
=> #<File:hoge.txt>  ### 戻り値としてFileオブジェクトを返す
[2] pry(main)> file.puts 'without block'
=> nil
[3] pry(main)> file.close  ### 自分でファイルを閉じる必要がある
=> nil
$ cat hoge.txt
================
without block
================

### ブロックを使ってFile.openでファイルに書き込み
[4] pry(main)> File.open 'hoge.txt', 'w' do |file|
[4] pry(main)*   file.puts 'without block2'
[4] pry(main)* end  ### ブロックの終了と同時に自動的にファイルは閉じられる
=> nil
$ cat hoge.txt
================
without block2
================

### 戻り値の確認
[1] pry(main)> def hoge
[1] pry(main)*   puts yield
[1] pry(main)* end
=> :hoge

[2] pry(main)> hoge do
[2] pry(main)*   123
[2] pry(main)*   456
[2] pry(main)* end
=> 456

### サンプルメソッド
[1] pry(main)> def block_sample
[1] pry(main)*   puts 'a'
[1] pry(main)*   yield
[1] pry(main)*   puts 'd'
[1] pry(main)* end
=> :block_sample

### nextを入れると処理はyieldの呼び出し元(block_sampleメソッド)に戻る
[2] pry(main)> block_sample do
[2] pry(main)*   puts 'b'
[2] pry(main)*   next
[2] pry(main)*   puts 'c'
[2] pry(main)* end
a
b   ### cは出力されない
d
=> nil

### breakを入れるとblock_sampleメソッドの呼び出し元に戻る
[3] pry(main)> block_sample do
[3] pry(main)*   puts 'b'
[3] pry(main)*   break
[3] pry(main)*   puts 'c'
[3] pry(main)* end
a
b  ### cもdも出力されない
=> nil

### yieldの呼び出し時に渡した値はブロックの引数になる
[1] pry(main)> def with_current_time
[1] pry(main)*   yield Time.now
[1] pry(main)* end
=> :with_current_time
[2] pry(main)> with_current_time do |now|
[2] pry(main)*   puts now.year
[2] pry(main)* end
2015
=> nil

### ブロック引数が無くても多すぎてもエラーにはならない
[3] pry(main)> with_current_time do
[3] pry(main)*   puts 'hoge'
[3] pry(main)* end
hoge
=> nil
[4] pry(main)> with_current_time do |now,something|
[4] pry(main)*   puts something.inspect
[4] pry(main)* end
nil
=> nil

### メソッドの仮引数のように、デフォルト値を指定できる
[1] pry(main)> def default_argument_for_block
[1] pry(main)*   yield
[1] pry(main)* end
=> :default_argument_for_block
[2] pry(main)> default_argument_for_block do |val = 'Hi'|
[2] pry(main)*   puts val
[2] pry(main)* end
Hi
=> nil

### メソッドの仮引数のように、可変長引数を指定できる
[3] pry(main)> def flexible_arguments_for_block
[3] pry(main)*   yield 1,2,3
[3] pry(main)* end
=> :flexible_arguments_for_block
[4] pry(main)> flexible_arguments_for_block do |*params|
[4] pry(main)*   puts params.inspect
[4] pry(main)* end
[1, 2, 3]
=> nil
  • 仮引数としてブロックを受け取る
    • 受け取ったブロックをその場で実行せずに他のメソッドへ渡すとき
    • 仮引数の先頭に&をつけることでメソッドに渡されたブロックを仮引数として受け取れる
    • 1メソッドに1つだけ指定できる
    • Proc#callメソッドを呼び出してyieldと同じようにブロックを呼び出すことができる
      • Proc#callに渡した引数はブロックの引数となる
      • ブロックの戻り値はcallの戻り値として受け取ることができる
    • yieldとの違い
      • 変数に代入して扱える
      • 別のメソッドにブロックとして受け渡すこともできる
仮引数としてブロックを受け取る
[1] pry(main)> def block_sample(&block) ### 変数blockにはブロックがProcオブジェクトとして代入される 
[1] pry(main)*   puts 'a'
[1] pry(main)*   block.call if block ### Proc#callメソッドを呼び出してブロックを呼び出す
[1] pry(main)*   puts 'c'
[1] pry(main)* end
=> :block_sample

### ブロックがあるとき
[2] pry(main)> block_sample do
[2] pry(main)*   puts 'b'
[2] pry(main)* end
a
b
c
=> nil

### ブロックがないとき
[3] pry(main)> block_sample
a
c
=> nil
  • オブジェクトをブロックとして渡す
    • メソッド呼び出し時に&をつけるとProcオブジェクトとして渡すことができる
  • container.map(&:method_name)
    • ブロック引数に対して引数無しのメソッド呼び出しを行いたいときによく使われる
オブジェクトをブロックとして渡す
[1] pry(main)> people = %w(a b c)
=> ["a", "b", "c"]
[2] pry(main)> block = Proc.new {|name| puts name}
=> #<Proc:0x007f7558a1e0a8@(pry):2>

### ProcオブジェクトをArray#eachに渡す
[3] pry(main)> people.each &block
a
b
c
=> ["a", "b", "c"]

### mapで要素の文字列を全て大文字にして新しい配列を作る
[4] pry(main)> people.map {|person| person.upcase}
=> ["A", "B", "C"]
### 以下のようにSymbol#to_proc簡単に書ける
[5] pry(main)> people.map(&:upcase)
=> ["A", "B", "C"]

[6] pry(main)> p1 = Proc.new {|val| val.upcase }
=> #<Proc:0x007f7558c4a700@(pry):4>
### シンボル(:upcase)に対してto_procメソッドを呼び出す
[7] pry(main)> p2 = :upcase.to_proc
=> #<Proc:0x007f7558c5e4d0>

### 同じ動きをする
[8] pry(main)> p1.call('hi')
=> "HI"
[9] pry(main)> p2.call('hi')
=> "HI"
  • 繰り返し以外に用いられるブロック
    • 準備->本質的な処理->後片付け
      • ファイルのオープン/クローズ
      • DBへの接続/切断
      • トランザクションの開始/終了
      • ロックと開放
現在時刻を書き込むプログラム
[1] pry(main)> def write_with_lock
[1] pry(main)*   File.open 'time.txt','w' do |f|
[1] pry(main)*     f.flock File::LOCK_EX
[1] pry(main)*     yield f
[1] pry(main)*     f.flock File::LOCK_UN
[1] pry(main)*   end
[1] pry(main)* end
=> :write_with_lock
[2] pry(main)>
[3] pry(main)> write_with_lock do |f|
[3] pry(main)*   f.puts Time.now
[3] pry(main)* end
=> 0
[4] pry(main)> exit

$ cat time.txt
============================
2015-07-04 15:25:55 +0000
============================
  • ブロックローカル変数
    • ブロックの中の変数と同名のローカル変数がブロックの外に存在しない時
    • ブロックの外からアクセスすることはできない
    • ブロックの外に同名のローカル変数がある場合、ブロックの中からそのローカル変数にアクセスや更新ができる
ブロックローカル変数

[1] pry(main)> people = []
=> []

### ブロック内からローカル変数を上書き
[2] pry(main)> %w(a b c).each do |person|
[2] pry(main)*   people << person  ### 配列の末尾に要素を追加
[2] pry(main)* end
=> ["a", "b", "c"]
[3] pry(main)> people
=> ["a", "b", "c"]


### ブロック外のローカル変数someone
[1] pry(main)> someone = 'Dave'
=> "Dave"
[2] pry(main)> people = []
=> []
[3] pry(main)> %w(a b c).each do |someone|
[3] pry(main)*   people << someone ### ブロックローカル変数someone
[3] pry(main)* end
=> ["a", "b", "c"]
[4] pry(main)> people
=> ["a", "b", "c"]
[5] pry(main)> someone
=> "Dave"  ### ブロック外のローカル変数はブロックローカル変数とは別オブジェクト

### ブロック外のローカル変数に影響無いようにする
[1] pry(main)> someone = 'Dave'
=> "Dave"

### ブロックの仮引数リストの後ろにセミコロンを置いてその後ろに変数名を記述するとブロックローカル変数(someone)となる
[2] pry(main)> %w(a b c).each do |person; someone|
[2] pry(main)*   someone = person
[2] pry(main)* end
=> ["a", "b", "c"]

### ローカル変数someoneには影響していない
[3] pry(main)> someone
=> "Dave"

参考

キーワード引数、擬似キーワード引数

  • キーワード引数
    • Ruby2.0からサポートされる
    • 名前のついた引数
    • メソッド定義の仮引数に直接キーワードを記述できる
    • キーワードとして存在しない引数が渡された際は例外ArgumentErrorが発生
擬似キーワード引数
### 引数として受け取った内容をそのまま返すメソッド
[1] pry(main)> def keywords(hash = {})
[1] pry(main)*   hash
[1] pry(main)* end
=> :keywords

### ハッシュのブレースを省略できる
[2] pry(main)> keywords(alice: 'アリス', bob: 'ボブ')
=> {:alice=>"アリス", :bob=>"ボブ"}
### 括弧も省略できる。メソッド呼び出しというよりは宣言のように見える
[3] pry(main)> keywords alice: 'アリス', bob: 'ボブ'
=> {:alice=>"アリス", :bob=>"ボブ"}

### デフォルト値を設定する
[1] pry(main)> def keywords(hash = {})
[1] pry(main)*   defaults = {alice: 'アリス', bob: 'ボブ'}  ### デフォルト値のハッシュ
[1] pry(main)*   hash = defaults.merge(hash)
[1] pry(main)*   hash
[1] pry(main)* end
=> :keywords
[2] pry(main)> keywords bob: 'ぼぶ'
=> {:alice=>"アリス", :bob=>"ぼぶ"}
  • 擬似キーワード引数
    • Ruby1.9まではこちらを使っていた
    • 仮引数はあくまでハッシュを受け取る事を想定した一つの引数
擬似キーワード引数
[1] pry(main)> def keywords(alice: nil, bob: nil)
[1] pry(main)*   {alice: alice, bob: bob}
[1] pry(main)* end
=> :keywords
[2] pry(main)> keywords alice: 'アリス', bob: 'ボブ'
=> {:alice=>"アリス", :bob=>"ボブ"}

### デフォルト値の設定
[3] pry(main)> def keywords(alice: 'ありす', bob: 'ボブ')
[3] pry(main)*   {alice: alice, bob: bob}
[3] pry(main)* end
=> :keywords
[4] pry(main)> keywords
=> {:alice=>"ありす", :bob=>"ボブ"}

### キーワードとして存在しないものをothersで受け取る
[1] pry(main)> def keywords(alice: nil, bob: nil, **others) ### **をつけた仮引数で受け取る
[1] pry(main)*   {alice: alice, bob: bob, others: others}
[1] pry(main)* end
=> :keywords
[2] pry(main)> keywords alice: 'ありす', bob: 'ぼぶ', jone: 'じょん'
=> {:alice=>"ありす", :bob=>"ぼぶ", :others=>{:jone=>"じょん"}}

### 引数の順番を変えることができる
[3] pry(main)> keywords july: 'じゅりー', alice: 'ありす', bob: 'ぼぶ', jone: '=> {:alice=>"ありす", :bob=>"ぼぶ", :others=>{:july=>"じゅりー", :jone=>"じょん"}}

### デフォルトでは空のハッシュが入る
[4] pry(main)> keywords alice: 'ありす', bob: 'ぼぶ'
=> {:alice=>"ありす", :bob=>"ぼぶ", :others=>{}}
  • 仮引数の順序

    • 通常の引数/省略可能な引数
    • *で指定できる引数
    • キーワード引数
    • **で指定できるハッシュの引数
    • &で指定できるブロックの引数
  • ::を使ったメソッドの呼び出し

    • メソッドの呼び出しはドットではなく::でも呼び出すことが可能
      • ::は定数の参照に使われることが多い
      • メソッドの呼び出しに使うことは滅多に無い
      • これはメソッドの呼び出しだということに気づければそれで良い
  • メソッド定義の取り消し

    • undefでメソッド定義を取り消せる
    • 複数のメソッドを同時に指定できる
    • KernelやObjectなど組み込みクラスに定義されているメソッド定義も取り消すことができる
  • メソッドに別名をつける

    • aliasを使う
    • 別名ではあるが、元のメソッド定義が取り消されても別名のメソッドは取り消されない
メソッドの別名と取り消し
### puts2という別名をつける
[1] pry(main)> alias puts2 puts
=> nil

### putsのメソッド定義を取り消し
[2] pry(main)> undef puts
=> nil

### putsが使えなくなっている
[3] pry(main)> puts 'hoge'
NoMethodError: undefined method 'puts' for main:Object
from (pry):3:in '__pry__'

### puts2は使える
[4] pry(main)> puts2 'hoge'
hoge
=> nil

組み込み関数

  • 組み込み関数
    • putsのようにレシーバを省略して呼び出すタイプの組み込みメソッド
    • サブルーチンのように呼び出せるので組み込み関数と呼ばれる
  • Kernelモジュールの組み込み関数

    • 入出力やファイルの読み込み、外部コマンドの実行などの既往を提供数する
  • Kernel.#puts

    • 末尾に改行がない場合は改行をつけて出力
    • 受け取った引数を順番に標準出力に出力する
    • 受け取った引数が文字列でない場合、to_sメソッドの戻り値を出力する
  • Kernel.#print

    • 引数に受け取ったオブジェクトを順番に標準出力に出力
    • 改行は挿入されない
    • 受け取った引数が文字列でない場合、to_sメソッドの戻り値を出力する
  • Kernel.#sprintf

    • 文字列を特定のフォーマットした結果を文字列で返す
    • フォーマットに用いられる記号は基本的にC言語のsprintfと同じ
    • 受け取った引数が文字列でない場合、to_sメソッドの戻り値を出力する
    • ``` ruby:sprintf,printf ### 結果を文字列で返す [1] pry(main)> sprintf('%04d',1) => "0001"

String#%という演算子のようなクラス・メソッドはsprintfのショートハンドルとして使うことができる

[2] pry(main)> '%04d' % 1
=> "0001"

標準出力に出力する

[3] pry(main)> printf '%04d',1
0001=> nil
```

  • Kernel.#p
    • 文字列や数値を区別できるように出力
    • 実行中のプログラムの様子を確認。主に人間が理解しやすいように出力するデバッグ用
    • ruby1.X(<-いつか調べる)以前は日本語は文字化けするので文字コードを指定して実行する
      • ruby -Ku : UTF-8
      • ruby -ks : Shift_JIS
      • ruby -Ke : EUC
    • inspectメソッドの戻り値を出力する
  • Kernel.#pp

    • pretty print
      • ライブラリをrequireメソッドで読み込んで使う
      • pより読みやすく整形してくれるらしい
  • Kernel.#warn

    • 与えられた引数を$stderrに出力する
    • 引数に渡されたオブジェクトはto_sメソッドにより文字列へ変換され末尾に改行がなければ改行を追加する
    • 使われ方
      • 将来サポートされない予定のDEPRECATEDなAPIを使用した際に警告
      • もはやサポートされていないOBSOLETEなAPIを使用した際に警告
  • Kernel.#gets

    • 定数ARGFから1行読み込んで返す
      • 値を対話的に受け取って処理するプログラムに使える
gets
[1] pry(main)> a = Integer(gets)
3
=> 3
[2] pry(main)> b = Integer(gets)
5
=> 5
[3] pry(main)> puts "#{a + b}"
8
=> nil

外部コマンドの実行

  • Kernel.#`
    • メソッド名はだが呼び出すときは外部コマンド`のように使う
    • 戻り値は外部コマンドの標準出力
    • 標準エラー出力は実行中のRubyの標準エラー出力として出力される
    • 外部コマンドの終了まで処理を同期的に待ち合わせる
  • Kernel.#system
    • 外部コマンドの出力が必要でない時に使う
      • 実行に成功したかどうかだけわかれば良いとき
    • コマンドの終了ステータスが0のときはtrue、それ以外はfalseを返す
    • 外部コマンドの出力は実行中のRubyの標準出力 or標準エラー出力として出力される
    • 外部コマンドの終了まで処理を同期的に待ち合わせる
外部コマンド実行
### .bashrcの末尾を表示
[1] pry(main)> num = 1
=> 1
[2] pry(main)> `head -#{num} ~/.bashrc`
=> "# .bashrc\n"

### system関数でコマンドを実行
[6] pry(main)> system('uname')
Linux
=> true  ### trueを返す
[7] pry(main)> $?  ### 直前のコマンドの終了ステータスを表示
=> #<Process::Status: pid 28425 exit 0>
  • Kernel.#exec
    • 引数に与えられた外部コマンドを実行する
    • 実行中のRubyプロセスは外部コマンドのプロセスに変わる
    • 外部コマンドの実行が終了しても制御は戻らずにプロセスが終了する
exec
[1] pry(main)> exec 'uname'
Linux
$  ### pryを抜けてプロンプトに戻っている
  • Kernel.#spawn

    • 外部コマンドの終了を待たず即座に子プロセスのPIDを返す
    • 外部コマンドの出力は、実行中のRubyの標準出力 or 標準エラー出力に出力される
  • Process.#waitpid

    • 特定の子プロセスの終了を待ち合わせる
spawn
[1] pry(main)> pid = spawn('uname')
Linux
=> 28484   ### 子供プロセスのPID
[2] pry(main)> Process.waitpid pid
=> 28484
  • 第一引数にハッシュを指定することができる
    • system, exec, spawn
    • 外部コマンドのプロセスの環境変数を上書きしたり追加したりできる
引数にハッシュを指定
### 環境変数HOGEの設定
[1] pry(main)> ENV['HOGE'] = 'hoge'
=> "hoge"
[2] pry(main)> system('echo $HOGE')
hoge
=> true

### systemで環境変数を上書き
[3] pry(main)> system({'HOGE' => 'fuga'}, 'echo $HOGE')
fuga
=> true

### spawnで環境変数を上書き
[4] pry(main)> pid = spawn({'HOGE' => 'haga'}, 'echo $HOGE')
=> 28518
[5] pry(main)> Process.waitpid pid
=> 28518

### execで環境変数を上書き
[6] pry(main)> exec({'HOGE' => 'hage'}, 'echo $HOGE')
hage

### 第二引数にはハッシュでオプションを渡すことができる
[1] pry(main)> system('echo `pwd`', chdir: '/tmp')
/tmp  ### /tmpに移動した後pwdの結果を出力
=> true

割り込みハンドラを定義する

  • Kernel.#trap
    • 割り込みのシグナルに対応するハンドラを登録できる
    • 第一引数には登録する市グラルをシンボル(or 文字列 or シグナル番号)で指定
    • 第二引数に市グラルを受け取った際に実行する処理をブロック化文字列で指定
    • :EXIT
      • シグナルハンドラの特別な値
      • プログラムが終了する直前に実行される
割り込みハンドラ
[1] pry(main)> trap :INT do
[1] pry(main)*   puts "Interrupted"
[1] pry(main)* end
=> "DEFAULT"

### SIGINTシグナルを送る
[2] pry(main)> loop do
[2] pry(main)*   sleep 1
[2] pry(main)* end
^CInterrupted  ### Ctrl + cを押すとInterruptedが表示される

### :EXIT
$ cat exit.rb
=================
trap :EXIT do
  puts 'END'
end

puts 'START'
=================

### プログラムが終了する直前(putsの後)にENDが出力される
$ ruby exit.rb
=================
START
END
=================

型変換メソッド

  • to_i : 文字列 -> 整数

マルチバイトを含んだコード

  • 全ての文字列はエンコーディング情報を保持している
    • 文字列のエンコーディングは変更することができる
    • 同じ文字列でもエンコーディングが異なれば同値とはならない
    • エンコーディングの異なる文字列同士は連結できない
    • ASCII互換もしくはASCII文字だけで構成された文字列の比較や結合はエンコーディングに関わらず可能
  • String#encoding
    • 文字列のエンコーディング情報をEncodingオブジェクトとして返す
エンコーディング
[1] pry(main)> str = 'あ'
=> "あ"
[2] pry(main)> str.encoding
=> #<Encoding:UTF-8>
[3] pry(main)> str.encode!(Encoding::EUC_JP)
=> "\x{A4A2}"
[4] pry(main)> str.encoding
=> #<Encoding:EUC-JP>
  • ファイルの冒頭でスクリプトエンコーディングを指定する
    • そのコードを書くのに使われているエンコーディング情報を明示する
    • ruby2系はデフォルトでutf-8が使われるのでuft-8を使うのであれば指定は不要
  • マジックコメントでエンコーディングを書く

以下のように様々な書き方がサポートされている

マジックコメント
# coding: utf-8
# encoding: utf-8
# -*- coding: utf-8 -*-
# vim:set fileencoding=utf-8:

変数と定数

ローカル変数

  • ローカル変数は局所変数とも言われる
    • 最もスコープの狭い種類の変数
      • 先頭が小文字のアルファベットがアンダースコアで始まる (ruby, _rubyなど)
      • 予約語はローカル変数として利用できない
    • ローカル変数のスコープ
      • ブロック
      • メソッド定義
      • クラス・モジュール定義
      • トップレベル
        • ローカル変数が定義されたファイル内のトップレベルがスコープとなる
ローカル変数
### メソッド外で定義されたローカル変数をメソッドの中から参照はできない
[2] pry(main)> var1='hoge'
=> "hoge"
[3] pry(main)> def display_var1
[3] pry(main)*   puts var1
[3] pry(main)* end
=> :display_var1
[4] pry(main)> puts var1
hoge
=> nil
[5] pry(main)> desplay_var1 ### var1の中身を参照できない
NameError: undefined local variable or method desplay_var1 for main:Object
from (pry):7:in __pry__

### ブロックの中からブロックの外で定義したローカル変数(greeting)は見える
[7] pry(main)> greeting = "Hello, "
=> "Hello, "
[8] pry(main)> people = [ 'Tom', 'Jim']
=> ["Tom", "Jim"]
[10] pry(main)> people.each do |person|
[10] pry(main)*   puts greeting + person
[10] pry(main)* end
Hello, Tom   ### ブロックの外で定義したローカル変数が見えている
Hello, Jim
=> ["Tom", "Jim"]

### ブロックの中で定義したローカル変数(person)は外では見えない
[11] pry(main)> puts person
NameError: undefined local variable or method `person' for main:Object
from (pry):16:in `__pry__`

personのようなローカル変数をブロックローカル変数という。

グローバル変数

  • 非常に広いスコープを持つ変数
    • どこからでも参照、変更できる
  • グローバル変数は「$」から始まる必要がある
  • 存在しないグローバル変数を参照したときはnilが返る

定数

  • 変化しない値を扱う場合は定数
  • 大文字アルファベットで始まる
  • メソッド中では定数の定義はできない
定数
### 定数を定義する
[1] pry(main)> FOO_BAR = 'bar'
=> "bar"
[2] pry(main)> puts FOO_BAR
bar
=> nil

### 定数に再代入は可能。警告は出る
[3] pry(main)> FOO_BAR = 'foo'
(pry):3: warning: already initialized constant FOO_BAR
(pry):1: warning: previous definition of FOO_BAR was here
=> "foo"
[4] pry(main)> puts FOO_BAR
foo
=> nil

### メソッド中で定数の定義を行う事はできない
[5] pry(main)> def set_foo
[5] pry(main)*   FOO_BAR = 'bar'
[5] pry(main)* end
SyntaxError: (eval):3: dynamic constant assignment
  FOO_BAR = 'bar'
           ^

制御構造

条件分岐と真偽値

if

  • 条件式が真であった場合に処理が実行される
if
[1] pry(main)> if true   ### trueは真を表す疑似変数
[1] pry(main)*   puts 'Ping'
[1] pry(main)* end
Ping
=> nil

[2] pry(main)> if false   ### falseは偽を表す疑似変数
[2] pry(main)*   puts "Ping"
[2] pry(main)* else
[2] pry(main)*   puts "Pong"
[2] pry(main)* end
Pong
=> nil

### Rubyは「false」と「nil」以外の全ての値は真として扱われる
[3] pry(main)> str = "Pong"
=> "Pong"
[4] pry(main)> if str   ### strに値が入っているので真として扱われる
[4] pry(main)*   puts str
[4] pry(main)* end
Pong
=> nil

### 条件式の分岐
[5] pry(main)> n = 13
=> 13
[6] pry(main)> if n.zero?
[6] pry(main)*   puts "0です"
[6] pry(main)* elsif n.even?
[6] pry(main)*   puts "偶数です"
[6] pry(main)* elsif n.odd?
[6] pry(main)*   puts "奇数です"
[6] pry(main)* else
[6] pry(main)*   puts "何かです"
[6] pry(main)* end
奇数です
=> nil

### 最後に評価された値を返す
[1] pry(main)> platform =
[1] pry(main)* if /x86_64-linux/ =~ RUBY_PLATFORM
[1] pry(main)*   'Linux'
[1] pry(main)* else
[1] pry(main)*   'Other'
[1] pry(main)* end
=> "Linux"

### thenをつけると1行に記述できる
[1] pry(main)> n = 0
=> 0
[2] pry(main)> if n.zero? then puts '0' else puts 'not 0' end
0
=> nil

### 通常1行で書く場合は以下のように書く。if,endは書かない、thenは?、elseは:。
[3] pry(main)> result = n.zero? ? '0' : 'not 0'
=> "0"
[4] pry(main)> puts result
0
=> nil

### elseが無い場合は後置ifを使う
[5] pry(main)> puts '0' if n.zero?
0
=> nil

unless

  • ifと逆の動き
  • elseは記述できるがelsifに相当するものは無い
  • ifと同様に1行の後置は使える
unless
[1] pry(main)> n = 1
=> 1
[2] pry(main)> unless n.zero?
[2] pry(main)*   puts 'not 0'
[2] pry(main)* else
[2] pry(main)*   puts '0'
[2] pry(main)* end
not 0
=> nil

### 後置
[3] pry(main)> puts '1' unless n.zero?
=> nil

case

  • 比較対象が一つの場合はcaseを使うとすっきりする
    • 条件が複数ある場合はelsif節を複数記述
  • when節
    • case節の値と比較する値を記述する
    • case節とwhen節の値は===によって比較され、最初にしんとなったWhenに続く式が実行される
    • 比較結果が真とならなかった場合式は実行されないのでbreakの記述は不要
    • 勘まで区切って複数の値を与える事ができる
      • どれかが真の場合式が実行される
  • 条件分岐を自然に記述できる
    • 範囲オブジェクトに、ある値が含まれているか
    • 正規表現が、ある文字列にマッチするか
case
### 分岐の例
[1] pry(main)> stone = 'ruby'
=> "ruby"
[2] pry(main)> case stone
[2] pry(main)* when 'ruby'
[2] pry(main)*   puts '7月'
[2] pry(main)* when 'peridot', 'sardonyx'
[2] pry(main)*   puts '8月'
[2] pry(main)* else
[2] pry(main)*   puts 'それ以外'
[2] pry(main)* end
7
=> nil

### 正規表現による分岐の例
[1] pry(main)> stone = 'ruby'
=> "ruby"
[2] pry(main)> case stone
[2] pry(main)* when /ruby/  ### /ruby/ === stoneのような比較が行われている
[2] pry(main)*   puts '7月'
[2] pry(main)* when /peridot|sardonyx/
[2] pry(main)*   puts '8月'
[2] pry(main)* else
[2] pry(main)*   puts 'それ以外'
[2] pry(main)* end
7
=> nil

### 代入やメソッド引数に使用できる
[1] pry(main)> stone = 'ruby'
=> "ruby"
[2] pry(main)> detected =
[2] pry(main)>   case stone
[2] pry(main)*   when 'ruby'
[2] pry(main)*     puts '7月'
[2] pry(main)*   when 'peridot', 'sardonyx'
[2] pry(main)*     puts '8月'
[2] pry(main)*   else
[2] pry(main)*     puts 'それ以外'
[2] pry(main)*   end
7
=> nil
[3] pry(main)> puts detected
7
=> nil

### thenを使えば1行に書ける
[1] pry(main)> stone = 'ruby'
=> "ruby"
[2] pry(main)> case stone
[2] pry(main)* when /ruby/              then '7月'
[2] pry(main)* when /peridot|sardonyx/  then '8月'
[2] pry(main)* else 'それ以外'
[2] pry(main)* end
7
=> nil

### caseに式を渡さない使い方
[1] pry(main)> stone = 'ruby'
=> "ruby"
[2] pry(main)> case
[2] pry(main)*   when stone === 'ruby'
[2] pry(main)*     puts '7月'
[2] pry(main)*   when stone === 'peridot' || stone === 'sardonyx'
[2] pry(main)*     puts '8月'
[2] pry(main)*   else
[2] pry(main)*     puts 'それ以外'
[2] pry(main)* end
7
=> nil

繰り返し

  • while
    • 繰り返し処理を行うための最も基本的な制御構文
    • 与えられた条件式が真のあいだ、中野式を繰り返し実行を続ける
    • 後置可能
  • until
    • whileと逆の動き
    • 与えられた値が真を返すまで実行を続ける
    • 後置可能
  • begin...end
    • 複数行の式の並びを囲むことができる
    • whileやuntilと組み合わせて記述する
  • each
    • 繰り返しをブロックで記述する
    • 配列の繰り返しはeachがよく使われる
  • for
    • eachメソッドを持つオブジェクトはforを使って各要素に対して処理ができる
      • eachメソッドを持つオブジェクトの例
        • 配列
        • ハッシュ
    • forに渡す式はブロックではない
      • eachはブロック
      • ループの内外で変数のスコープは変わらない
        • forの中で使用している変数はforの曽於から参照できる
    • Rubyではあまり使われない
  • Kernel.#loop
    • 単純な無限ループに使われる
      • 簡単でより意図を明確にできる
  • Integer.times
    • n回の繰り返しに使われる
繰り返し
### whileで配列の要素を先頭から順に出力する
[1] pry(main)> languages = %w(Perl Python Ruby)
=> ["Perl", "Python", "Ruby"]
[2] pry(main)> i = 0
=> 0
[3] pry(main)> while i < languages.length
[3] pry(main)*   puts "Hello, #{languages[i]}."
[3] pry(main)*   i += 1
[3] pry(main)* end
Hello, Perl.
Hello, Python.
Hello, Ruby.
=> nil

### untileで配列の要素を末尾から逆順に出力
[1] pry(main)> languages = %w(Perl Python Ruby)
=> ["Perl", "Python", "Ruby"]
[2] pry(main)> i = languages.length - 1
=> 2
[3] pry(main)> until i < 0
[3] pry(main)*   puts "Hello, #{languages[i]}."
[3] pry(main)*   i -= 1
[3] pry(main)* end
Hello, Ruby.
Hello, Python.
Hello, Perl.
=> nil

### 後置while, until
sleep 1 while processing? ### 偽を返すまでsleep1を繰り返す
sleep 1 until prepared?  ### 偽を返す間sleepを繰り返す

### 最初に必ずbeginの中が実行される
begin
  process1
  process2
end while needed?  ### needed?は真偽値を返す何らかのメソッド

### forで配列の要素を順番に出力
[1] pry(main)> for name in %w(Alice Bob Carol)
[1] pry(main)*   puts name
[1] pry(main)* end
Alice
Bob
Carol
=> ["Alice", "Bob", "Carol"]

### ループの中で定義された変数を参照
[2] pry(main)> puts name
Carol
=> nil

### ハッシュでforをループ
[1] pry(main)> for val in {a: 1, b: 2}
[1] pry(main)*   puts val[0]
[1] pry(main)*   puts val[1]
[1] pry(main)* end
a  ### val[0]はキーを表示
1  ### val[1]は要素を表示
b
2
=> {:a=>1, :b=>2}

### それぞれ別の変数として使う場合は多重代入の容量で変数をカンマで区切る
[1] pry(main)> for key, val in {a: 1, b: 2}
[1] pry(main)*   puts key
[1] pry(main)*   puts val
[1] pry(main)* end
a
1
b
2
=> {:a=>1, :b=>2}

### 単純な無限ループ
[1] pry(main)> loop do
[1] pry(main)*   puts 'infinity loop'
[1] pry(main)* end

### n回の繰り返し
[1] pry(main)> 2.times do
[1] pry(main)*   puts 'Hello'
[1] pry(main)* end
Hello
Hello
=> 2

ジャンプ構文

  • ジャンプ構文
    • ループやブロックの途中で処理を呼び出し元に戻す
  • break
    • ループの処理を中断する
    • ループの途中である条件を満たし、後続の処理を行う必要がない場合に使う
  • next
    • 直ちに次の繰り返しに映る
  • redo
    • もう一度その処理を繰り返す
  • 大域脱出
    • いくつもネストした処理から一気に抜け出す
      • catch, throwを使う
ジャンプ構文
### 配列の要素としてRubyが見つかったらbreak
[1] pry(main)> languages = %w(Perl Python Ruby Smalltalk JavaScript)
=> ["Perl", "Python", "Ruby", "Smalltalk", "JavaScript"]
[2] pry(main)> languages.each do |language|
[2] pry(main)*   puts language
[2] pry(main)*   if language == 'Ruby'
[2] pry(main)*     puts 'I found Ruby!!'
[2] pry(main)*     break  ### breakに戻り値を渡すこともできる
[2] pry(main)*   end
[2] pry(main)* end
Perl
Python
Ruby
I found Ruby!!
=> nil  ### breakの戻り値に何も渡さなければnilになる。

### nextを呼び出して次の繰り返しに移る
[1] pry(main)> languages = %w(Perl Python Ruby Smalltalk JavaScript)
=> ["Perl", "Python", "Ruby", "Smalltalk", "JavaScript"]
[2] pry(main)> languages.each do |language|
[2] pry(main)*   puts language
[2] pry(main)*   next unless language == 'Ruby' ### 要素がRubyでなかった場合nextが呼び出される
[2] pry(main)*   puts 'I found Ruby!' ### 要素がRubyの時のみ、この処理が実行される
[2] pry(main)* end
Perl
Python
Ruby
I found Ruby!
Smalltalk
JavaScript
=> ["Perl", "Python", "Ruby", "Smalltalk", "JavaScript"]

### redoでもう一度処理を繰り返す
[1] pry(main)> languages = %w(Perl Python Ruby Smalltalk JavaScript)
=> ["Perl", "Python", "Ruby", "Smalltalk", "JavaScript"]
[2] pry(main)> languages.each do |language|
[2] pry(main)*   puts language
[2] pry(main)*   if language == 'Ruby'
[2] pry(main)*     puts 'I found Ruby!'
[2] pry(main)*     redo  ### 要素がRubyだったとき、永遠にputsを実行し続ける
[2] pry(main)*   end
[2] pry(main)* end

### 大域脱出。catchを指定するための名前(ここでは:triple_loop)をシンボルまたは文字列で指定する
[1] pry(main)> catch :triple_loop do ### 
[1] pry(main)*   loop do
[1] pry(main)*     puts 'one'
[1] pry(main)*     loop do
[1] pry(main)*       puts 'two'
[1] pry(main)*       loop do
[1] pry(main)*         puts 'three'
[1] pry(main)*         throw :triple_loop ### catchの呼び出し元に戻る
[1] pry(main)*       end
[1] pry(main)*     end
[1] pry(main)*   end
[1] pry(main)* end
one
two
three
=> nil

### throwに渡す名前によってどのcatchのブロックを中断するかが変わる
[2] pry(main)> catch :entire do
[2] pry(main)*   catch :process do
[2] pry(main)*     throw :entire
[2] pry(main)*   end
[2] pry(main)* end
=> nil

### throwの第二引数にはcatchの戻り値を渡せる。無ければnil
[3] pry(main)> catch(:foo) {
[3] pry(main)*   throw :foo, 'hoge'
[3] pry(main)* }
=> "hoge"

プログラムの終端を明示

  • _END_キーワード
    • ファイルの途中でプログラムの終端を明示する
    • _END_以降はRubyのプログラムとして実行される事は無い
    • _END_の次の行以降のデータはDATAという定数にFileオブジェクトとして保持されている
      • 実行時に参照する事ができる
__END__
### 以下のように記述
$ cat end.rb
============================
DATA.each_line do |line|
  puts line  ### __END__ 以下の値が出力される
end

__END__  ### ここより下はRubyのプログラムとして実行される事は無い
hoge  ### DATAという定数にFileオブジェクトとして保持される
fuga
============================

### 実行
$ ruby end.rb
============================
hoge
fuga
============================

  • Rubyのプログラムは、ほとんどが何らかの値を返す式
    • 変数への代入は右辺の値
    • if文は最後に評価された値
[1] pry(main)> val = 'Hello'
=> "Hello"  ### 右辺の値
[2] pry(main)> if true
[2] pry(main)*   'Hello'
[2] pry(main)* end
=> "Hello"   ### 最後に評価された値
[3] pry(main)> val
=> "Hello"
[4] pry(main)> 4423
=> 4423

様々な代入式

多重代入

多重代入
### 一度に複数の変数に代入
[1] pry(main)> a, b = 1,2
=> [1, 2]
[2] pry(main)> a
=> 1
[3] pry(main)> b
=> 2

### 配列を指定できる。右辺の要素が多い場合、余った要素(ここでは5)は無視される
[4] pry(main)> c, d = [3,4,5]
=> [3, 4, 5]
[5] pry(main)> c
=> 3
[6] pry(main)> d
=> 4

### 「*」をつけると余った要素も配列として代入できる
[7] pry(main)> e, *f = [6,7,8]
=> [6, 7, 8]
[8] pry(main)> e
=> 6
[9] pry(main)> f
=> [7, 8]

### 左辺の変数に対応する要素がなければnilになる
[10] pry(main)> g, h, i = [9,10]
=> [9, 10]
[11] pry(main)> g
=> 9
[12] pry(main)> h
=> 10
[13] pry(main)> i
=> nil

### 変数の交換をするときによく用いられる
[14] pry(main)> j = 'j'
=> "j"
[15] pry(main)> k = 'k'
=> "k"
[16] pry(main)> j, k = k, j
=> ["k", "j"]
[17] pry(main)> j
=> "k"
[18] pry(main)> k
=> "j"

自己代入

自己代入
### 演算結果を自信に代入する
[1] pry(main)> a = 1
=> 1
[2] pry(main)> a += 1  ### a = a + 1 と同じ
=> 2
[3] pry(main)> b ||= 2 ### b = b || 2と同じ (変数が空の時2を代入)
=> 2

演算子

  • 制御構造に与える式は、多くの場面で演算子を使う
  • 演算子には優先順位がある
演算子
### 優先順位を明治する場合は()でくくる
[1] pry(main)> a, b, c = true, true, false
=> [true, true, false]
### b && cが先にfalseで評価され、その後 a or (false) が評価されるのでtrue
[2] pry(main)> a or b && c  
=> true
### a or bが先にtrueで評価され、その後 (true) && c が評価されるのでfalse
[3] pry(main)> (a or b) && c
=> false

メソッドとして定義されている演算子

  • |, ^, &, <=>, ==, ===, =~, > >=, <, <=, <<, >>, +@, -@, +, -, *, /, %
  • **, ~, [], []=, `, !, !=, !~
  • メソッドとして定義されている演算子は独自に定義することができる
メソッドとして定義されている演算子
### 1 + 1は以下のように書ける
[1] pry(main)> 1.+(1)
=> 2

### 二項演算子メソッドの==を独自に定義。同じクラスのオブジェクトであれば真
[1] pry(main)> class MyObject
[1] pry(main)*   def ==(other)
[1] pry(main)*     self.class == other.class
[1] pry(main)*   end
[1] pry(main)* end
=> :==
[2] pry(main)> MyObject.new == MyObject.new
=> true

### +や*は整数と文字列では異なる振る舞いとして定義されている
[1] pry(main)> 1 + 1
=> 2
[2] pry(main)> 'a' + 'b' + 'c'
=> "abc"
[3] pry(main)> 2 * 2
=> 4
[4] pry(main)> 'abc' * 3
=> "abcabcabc"
  • 比較演算子「===」はクラスによって異なる振る舞いをする
    • クラスが異なるオブジェクトを比較するとき、どちらをレシーバにするかによって変わる
      • メソッドオブジェクト : 動作
      • Range : 引数が自身の範囲内に含まれているなら真
      • Regexp : 引数の文字列がマッチしたら真
      • Proc : 右辺を引数にして手続きオブジェクトを実行した結果を返す
      • Module、 Class : 引数が自身または自身のサブクラスのインスタンスなら真

オブジェクトの同一性と同値性

  • 同一性
    • 同じインスタンスである
    • equal?メソッドで確認
    • アイデンティティ(ID)
      • オブジェクトの同一性を表すもの
      • 全てのオブジェクトに対して一意
  • 同値性
    • 値が同じである
      • ==演算子(メソッド)で確認
      • eql?メソッドでは整数と浮動小数点の違いも確認できる
同一性と同値性
[1] pry(main)> a = 'Alice'
=> "Alice"
[2] pry(main)> b = 'Alice'
=> "Alice"

### 値は同じ
[3] pry(main)> a == b
=> true

### アイデンティティは異なる事をobject_idメソッドで確認
[4] pry(main)> a.object_id
=> 70000642358620
[5] pry(main)> b.object_id
=> 70000642384720

### __id__メソッドでも確認できる。object_idメソッドの別名でライブラリはこちらを使うこと推奨。
[6] pry(main)> a.__id__
=> 70000642358620
[7] pry(main)> b.__id__
=> 70000642384720


### cはaと同じオブジェクト
[8] pry(main)> c = a
=> "Alice"
[9] pry(main)> c.object_id
=> 70000642358620

### equal?メソッドで確認
[10] pry(main)> a.equal?(b)
=> false
[11] pry(main)> a.equal?(c)
=> true

### 同値性の確認で==は、整数と浮動小数の型変換は自動的に行われる
[12] pry(main)> 12 == 12.0
=> true

### 整数と浮動小数の違いも見る場合はeql?メソッドを使う
[13] pry(main)> 12.eql?(12)
=> true
[14] pry(main)> 12.eql?(12.0)
=> false

クラス

  • Rubyはクラスベースのオブジェクト指向プログラミングをサポートする
  • クラスはオブジェクトの振る舞い(メソッド)を決める
    • 振る舞いの例
      • 数値クラスのオブジェクト : 足し算や引き算ができる
      • 文字列クラスのオブジェクト : 別の文字列とつなぎ合わせる
    • このようなメソッドは、各クラスで定義されている
  • Rubyは全ての値がオブジェクトとして扱われる
    • 全てのオブジェクトは何らかのクラスのインスタンス
    • JavaScriptなどはクラスに属さないオブジェクトも有る
    • Rubyではオブジェクトとインスタンスはほぼ同じ意味
      • あるオブジェクトがあるクラスに属している事を強調する意味でインスタンが使われる

クラスの定義式

  • 新たにクラスを定義にはclassキーワードを用いる
  • クラス名は大文字で始まる必要がある
  • クラスで定義したメソッドはそのクラスのインスタンスに対して呼び出せる

    • インスタンスメソッド。もしくは単にメソッドと言う。
  • インスタンスの生成

    • newメソッドを呼び出す
クラス1
### 書式
class クラス名
end

### クラスの例(MyClassの作成)
[1] pry(main)> class MyClass
[1] pry(main)*   def hello
[1] pry(main)*     puts 'Hello, My object!'
[1] pry(main)*   end
[1] pry(main)* end
=> :hello

### クラスからインスタンスを作る
[2] pry(main)> my_object = MyClass.new   ### インスタンスの作成
=> #<MyClass:0x007f9a83528d50>
[3] pry(main)> my_object.hello  ### 定義したMyClass#hello メソッドの呼び出し
Hello, My object!
=> nil

### 定義した定数を::演算子で参照する
[1] pry(main)> class MyClass
[1] pry(main)*   DEFAULT_VALUE = 1234
[1] pry(main)* end
=> 1234
[2] pry(main)> MyClass::DEFAULT_VALUE
=> 1234

インスタンス変数

  • インスタンスの中だけで参照できる変数をインスタンス変数と言う
    • インスタンス変数によってオブジェクトの状態を保持する事ができる
  • インスタンス変数は「@」から始まる名前で表記する
  • 外部からインスタンス変数にアクセスするためにはそのメソッドを定義する必要がある
    • 代入や参照を行うためのメソッドをアクセサと呼ぶ
  • attr_accessor
    • アクセサは自分で定義せずattr_accessorにインスタンス変数の名前を渡せば自動亭に定義される
    • 渡せる名前はインスタンス変数に用いることができるものに限られている
      • 疑問符はインスタンス変数名に使うことができないので使えない
    • ,で区切ると複数のインスタンス変数のためのアクセサを定義することができる
  • attr_reader
    • インスタンス変数を「参照するためのメソッド」だけを定義する
  • attr_writer
    • インスタンス変数に「代入するメソッド」だけを定義する
インスタンス変数
[1] pry(main)> class Ruler
[1] pry(main)*   def length=(val)   ### インスタンス変数に代入するためのメソッド。valはレシーバ
[1] pry(main)*     @length = val   
[1] pry(main)*   end
[1] pry(main)*
[1] pry(main)*   def length  ### インスタンス変数を参照するためのメソッド
[1] pry(main)*     @length
[1] pry(main)*   end
[1] pry(main)* end
=> :length
[2] pry(main)> ruler = Ruler.new
=> #<Ruler:0x007fbad73ccbb8>
[3] pry(main)> ruler.length = 30   ### インスタンス変数に値を代入するように記述できる。30はメソッド呼び出しの引数
=> 30
[4] pry(main)> ruler.length
=> 30

### 実際はインスタンス変数にアクセスするメソッドが欲しい場合はattr_accessorを用いる
[1] pry(main)> class Ruler
[1] pry(main)*   attr_accessor :length
[1] pry(main)* end
=> nil
# 自動的に「Ruler#length=」と「Ruler#length」が定義される

self

  • メソッドの中では、そのメソッドが属しているインスタンスをselfという名前の疑似変数で参照できる
    • 他言語の「this」にあたる
  • メソッドの中でレシーバを省略したメソッド呼び出しが行われたとき、暗黙的にselfがレシーバとなる
self
[1] pry(main)> class Ruler
[1] pry(main)*   attr_accessor :length  ### Ruler#lengthが定義されている
[1] pry(main)*   def display_length
[1] pry(main)*     puts length  ### Ruler#lengthの戻り値を出力。レシーバ(self)は略されている。
[1] pry(main)*   end
[1] pry(main)* end
=> :display_length
[2] pry(main)> ruler = Ruler.new
=> #<Ruler:0x007f183febbbe0>
[3] pry(main)> ruler.length = 30
=> 30
[4] pry(main)> ruler.display_length
30
=> nil

### selfを使う
[1] pry(main)> class Ruler
[1] pry(main)*   attr_accessor :length
[1] pry(main)*   def set_default_length
[1] pry(main)*     self.length = 30  ### Ruler#length= のようなメソッド呼び出しのselfは省略できない。
### length = 30 とすると、メソッドは呼び出されずローカル変数への代入と解釈されてしまう。
[1] pry(main)*   end
[1] pry(main)* end
=> :set_default_length
[2] pry(main)> ruler = Ruler.new
=> #<Ruler:0x007f7cb517b040>
[3] pry(main)> ruler.set_default_length
=> 30
[4] pry(main)> ruler.length
=> 30

初期化

  • インスタンスを初期化する
    • クラスのインスタンスを生成するときなど
  • initializeという名前のメソッドを定義する
    • 他言語ではコンストラクタと呼ばれるものに相当
initializ
[1] pry(main)> class Ruler
[1] pry(main)*   attr_accessor :length
[1] pry(main)*   def initialize(length)
[1] pry(main)*     @length = length  ### インスタンス変数 @lengthを設定
[1] pry(main)*   end
[1] pry(main)* end
=> :initialize
[2] pry(main)> ruler = Ruler.new(30)  ### 30はinitializeの仮引数として受け取ることができる
=> #<Ruler:0x007f8b1555ca88 @length=30>
[3] pry(main)> ruler.length
=> 30

インスタンスメソッド

  • 定義の仕方
    • クラス定義の中でメソッドを定義する
    • インスタンスメソッドは同じクラスの他のインスタンスメソッドを呼び出すことができる
    • selfはそのインスタンスメソッドのレシーバになる
      • 同じインスタンスのメソッドを呼び出す際はレシーバを省略できる
    • メソッドの末尾には!や?を使うことができる
    • 文法的には普通のメソッドと同じ
      • 慣習
        • ?
          • オブジェクトの状態を真偽値で返す術後メソッド
          • 他の言語ではisXXXのように書かれる
        • !
          • 同じ処理をする2つのメソッドがあるとき、プログラマに注意を促したいメソッドの名前につける
          • 破壊的なメソッドなど
          • 処理に失敗した際に偽を返すメソッドと例外を発生させるメソッドがある場合
          • 例外を発生させるほうのメソッドの名前につける
          • ActiveRecordはそのような意味で使用している
    • 特異メソッド
      • そのオブジェクト固有のメソッド
      • 定義方法
        • メソッドを定義したいオブジェクトをメソッド名の手前に指定する
    • クラス定義のネスト
      • クラス定義の中には他のクラス定義を記述できる
        • いくつでもネストすることができる
        • ネストしたクラスを参照するには::演算子を使う
        • ネストしたクラス同士には継承関係のような関係は無い
          • ただし定数の探索方法が若干異なる
インスタンスメソッド
### 末尾に?や!を使う
[1] pry(main)> class Brownie
[1] pry(main)*   def initialize
[1] pry(main)*     @baked = false
[1] pry(main)*   end
[1] pry(main)*   def bake!  ### Brownie#bake!
[1] pry(main)*     @baked = true
[1] pry(main)*     self
[1] pry(main)*   end
[1] pry(main)*   def baked? ### Brownie#baked?
[1] pry(main)*     @baked
[1] pry(main)*   end
[1] pry(main)* end
=> :baked?
[2] pry(main)> b = Brownie.new
=> #<Brownie:0x007fd988a7a5f8 @baked=false>

### オブジェクトの状態を確認
[3] pry(main)> b.baked?
=> false  ### 初期値はfalse

### 破壊的なメソッド
[4] pry(main)> b.bake!
=> #<Brownie:0x007fd988a7a5f8 @baked=true>

### オブジェクトの状態を確認
[5] pry(main)> b.baked?
=> true ### trueになった

### reverseの破壊的でないメソッドと破壊的なメソッド
[1] pry(main)> str = 'abc'
=> "abc"

### 破壊的でないメソッド
[2] pry(main)> str.reverse
=> "cba"
[3] pry(main)> str
=> "abc"

### 破壊的なメソッド
[4] pry(main)> str.reverse!
=> "cba"
[5] pry(main)> str
=> "cba"

### 特異メソッド
[1] pry(main)> class Foo
[1] pry(main)* end
=> nil
[2] pry(main)> foo = Foo.new
=> #<Foo:0x007fc5d02844a0>
[3] pry(main)> bar = Foo.new
=> #<Foo:0x007fc5d02957a0>

### barだけにメソッドを定義する
[4] pry(main)> def bar.singleton_method
[4] pry(main)*   puts 'Call'
[4] pry(main)* end
=> :singleton_method

### singleton_methodはbarに対してだけ呼べる
[5] pry(main)> bar.singleton_method
Call
=> nil
[6] pry(main)> foo.singleton_method
ArgumentError: wrong number of arguments (0 for 1)
from (pry):9:in `singleton_method'

### クラス定義のネスト
### ネストでクラスを定義
[1] pry(main)> class My
[1] pry(main)*   class SweetClass
[1] pry(main)*   end
[1] pry(main)* end
=> nil
[2] pry(main)> My.new
=> #<My:0x007f7df003c5d0>

### ネストしたクラスを参照
[3] pry(main)> My::SweetClass.new
=> #<My::SweetClass:0x007f7df00518b8>

### 既にMyクラスが定義されていれば以下のように書ける
[4] pry(main)> class My::GreatClass
[4] pry(main)* end
=> nil

### ネストした定数の参照
[1] pry(main)> VALUE = 'top'
=> "top"
[2] pry(main)> class Foo
[2] pry(main)*   VALUE = 'Foo value'
[2] pry(main)*   class Bar
[2] pry(main)*     def self.value
[2] pry(main)*       VALUE
[2] pry(main)*     end
[2] pry(main)*   end
[2] pry(main)* end
=> :value

### Foo::VALUEを参照する
[3] pry(main)> Foo::Bar.value
=> "Foo value"

### トップレベルのVALUEを参照する
[4] pry(main)> Foo::Bar::VALUE
(pry):11: warning: toplevel constant VALUE referenced by Foo::Bar::VALUE
=> "top"

クラスメソッド

  • クラスに対してもメソッド呼び出しができる
    • クラスもオブジェクトのひとつ
    • クラスに対して呼び出すことのできるメソッドをクラスメソッドという
      • Rulerのインスタンスを作成するRuler.newも組み込みのクラスメソッド
      • 他の言語で「staticメソッド」と呼ばれているものに似ている
  • クラスメソッドは、メソッド名の前に「self.」をつけて定義する
  • class << self ... endという記述によるクラスメソッドの定義
    • 特異クラス(Singleton class or Eigen class)と呼ばれる特殊なクラス定義
      • クラスメソッドとなる
      • 利点
        • クラスメソッドをまとめて書ける
        • ネストすることでインスタンスメソッドとの区別がしやすい
クラスメソッド
### Rulerのインスタンスを2つ作成し、配列として返すメソッドRuler.pair
[1] pry(main)> class Ruler
[1] pry(main)*   attr_accessor :length
[1] pry(main)*   def self.pair  ### selfはそのメソッドが属するクラスになる
[1] pry(main)*     [Ruler.new, Ruler.new]  ### [new, new]と略することもできる
[1] pry(main)*   end
[1] pry(main)* end
=> :pair
[2] pry(main)> Ruler.pair
=> [#<Ruler:0x007fa892261d70>, #<Ruler:0x007fa892261d48>]

### class << self ... endによるクラス定義
[1] pry(main)> class Ruler
[1] pry(main)*   attr_accessor :length
[1] pry(main)*   class << self
[1] pry(main)*     def pair
[1] pry(main)*       [new, new]
[1] pry(main)*     end
[1] pry(main)*     def trio
[1] pry(main)*       [new, new, new]
[1] pry(main)*     end
[1] pry(main)*   end
[1] pry(main)* end
=> :trio
[2] pry(main)> Ruler.pair
=> [#<Ruler:0x007ff4c8d32ad8>, #<Ruler:0x007ff4c8d32ab0>]
[3] pry(main)> Ruler.trio
=> [#<Ruler:0x007ff4c8d56a50>,
 #<Ruler:0x007ff4c8d56a28>,
 #<Ruler:0x007ff4c8d56a00>]

メソッドの呼び出し制限

  • 外部に公開したくないメソッド
    • 内部実装で使用するためのメソッド
  • メソッド呼び出し制限
    • public : 呼び出し制限なし
    • private : レシーバを省略するかたちでしか呼び出せない
      • 自動的にprivateになるメソッド
        • initializeという名前のインスタンスメソッド
        • トップレベルで定義したメソッド
    • protected : そのメソッドが定義されたのと同じクラス or サブクラスのインスタンスのみ呼び出せる
      • JavaやC++と違い、同じクラスに属しているインスタンスのメソッドの中であれば異なるインスタンスのprotectedなメソッドを呼び出すことができる
      • protectedのような呼び出し制限が必要となる場合は殆ど無い
      • 単にインスタンスの外から呼び出しを制限したい場合はprivateを使う
制限
[1] pry(main)> class Processor
[1] pry(main)*   def process
[1] pry(main)*     protected_process
[1] pry(main)*   end
[1] pry(main)*   def protected_process
[1] pry(main)*     private_process
[1] pry(main)*   end
[1] pry(main)*   protected :protected_process
[1] pry(main)*   def private_process
[1] pry(main)*     puts 'Done'
[1] pry(main)*   end
[1] pry(main)*   private :private_process
[1] pry(main)* end
=> Processor
[2] pry(main)> processor = Processor.new
=> #<Processor:0x007fd593747780>

### Processor#processで呼び出しているprotected_processメソッドは同じクラスのインスタンスから呼び出している -> OK
### Processor#protected_processは、private_processをレシーバを指定せず呼び出している -> OK
[3] pry(main)> processor.process
Done
=> nil

### privateなメソッド呼び出しではレシーバ(ここではドットの前の「processor」)は指定できない
[4] pry(main)> processor.private_process
NoMethodError: private method `private_process' called for #<Processor:0x007fd593747780>
from (pry):16:in `__pry__'

### protectedなメソッドは同じクラスかそのサブクラスのインスタンスからしか呼び出せない
### トップレベルで呼び出しているのでエラー
[5] pry(main)> processor.protected_process
NoMethodError: protected method `protected_process' called for #<Processor:0x007fd593747780>
from (pry):17:in `__pry__`

### privateなメソッドをまとめて複数記述する方法
[1] pry(main)> class Processor
[1] pry(main)*   private
[1] pry(main)*   def process_a
[1] pry(main)*   end
[1] pry(main)*   def process_b
[1] pry(main)*   end
[1] pry(main)* end
=> :process_b

クラス変数

  • クラスとそのインスタンスをスコープとした変数
    • 「@@」のように@が2つからはじまる名前で表記する
    • クラス定義の中やインスタンスメソッド、クラスメソッドから参照できる
      • トップレベルでクラス変数を定義するとグローバル変数のようにどこからでもアクセスできてしまう
クラス変数
[1] pry(main)> class MyClass
[1] pry(main)*   @@cvar = "Hello, My class variable!!"  ### クラス変数の作成
[1] pry(main)*   def cvar_in_method
[1] pry(main)*     puts @@cvar  ### インスタンスメソッドの定義
[1] pry(main)*   end
[1] pry(main)*   def self.cvar_in_class_method  ### クラスメソッドの定義
[1] pry(main)*     puts @@cvar
[1] pry(main)*   end
[1] pry(main)* end
=> :cvar_in_class_method
[2] pry(main)> my_object = MyClass.new
=> #<MyClass:0x007f311fa37d00>
[3] pry(main)> my_object.cvar_in_method  ### インスタンスメソッドから参照
Hello, My class variable!!
=> nil
[4] pry(main)> MyClass.cvar_in_class_method  ### クラスメソッドから参照
Hello, My class variable!!
=> nil

継承

  • Rubyは単一継承をサポートしている
    • スーパークラス
      • 継承された方
      • クラス作成時にスーパークラスを指定しなければ自動的にObjectクラスが継承される
    • サブクラス
      • 継承した方
      • スーバークラスのインスタンスメソッド、クラスメソッドを継承する
      • スーパークラスの定数を継承する
        • 同名の定数を定義した場合、サブクラスで定義した定数の値が参照される
      • インスタンス変数についての情報は継承しない
        • インスタンス変数はインスタンスメソッドの中で継承されるため
      • 継承したメソッドの中でインスタンス変数が定義されていれば、結果的にスーパークラスと同じインスタンス変数が定義される
  • メソッドのオーバーライド
    • スーパークラスで定義されているメソッドをサブクラスで再定義すること
      • 機能をそのサブクラスに適したものに変更したり拡張できる
    • super
      • メソッドの中で呼び出すとスーパークラスの同名のメソッドを呼び出すことができる
      • サブクラスのメソッドで受け取った引数が自動で渡される
        • 別の値を渡したい場合はsuperに引数を渡す
        • 引数を一切渡したくない場合はsuper()と括弧を記述して明示する
クラスの継承
class サブクラス名 < スーパークラス名
end
クラスを継承する例
[1] pry(main)> class Parent   ### スーパークラス
[1] pry(main)*   def hello
[1] pry(main)*     puts 'Hello, Parent class!'
[1] pry(main)*   end
[1] pry(main)* end
=> :hello
[2] pry(main)> class Child < Parent  ### Parentのサブクラス
[2] pry(main)*   def hi
[2] pry(main)*     puts 'Hello, Child class!'
[2] pry(main)*   end
[2] pry(main)* end
=> :hi
[3] pry(main)> child = Child.new
=> #<Child:0x007f51e56d29a8>
[4] pry(main)> child.hello
Hello, Parent class!  ### サブクラスのメソッド(Child#hi)を呼ぶ
=> nil
[5] pry(main)> child.hi
Hello, Child class!  ### スーパークラスのメソッド(Parent#hello)を呼ぶ
=> nil

### オーバーライドする
[6] pry(main)> class Child < Parent
[6] pry(main)*   def hello
[6] pry(main)*     super  ### Parent#helloが呼び出される
[6] pry(main)*     puts 'Hello, Child class!'  ### あらたに追加
[6] pry(main)*   end
[6] pry(main)* end
=> :hello
[7] pry(main)> child = Child.new
=> #<Child:0x007f51e5766978>
[8] pry(main)> child.hello
Hello, Parent class!  ### superで呼び出されたParent#hello
Hello, Child class!   ### 新たに追加したputsの実行結果
=> nil

### スーパークラスを指定しない場合はObjectクラスを継承したクラスが定義される
[1] pry(main)> class Whatever
[1] pry(main)* end
=> nil
[2] pry(main)> Whatever.superclass ### クラスメソッドでスーパークラスを確認
=> Object  ### スーパークラスはObjectクラス

モジュール

モジュールの定義式

  • クラスと異なる点
    • インスタンス化できない
    • 継承することはできない
    • moduleキーワードを用いて定義する
  • モジュールの用途
    • 名前空間を作る
    • モジュールのメソッドを、あるクラスのインスタンスメソッドとして取り込む
    • モジュールのメソッドを、あるオブジェクトの特異メソッド(クラスメソッド)として取り込む
    • モジュール関数を定義して使う
module定義
module モジュール名
end
  • モジュール定義の中には別のモジュールやクラスの定義を記述できる
    • 名前空間として利用可能
同じ名前のクラスを異なるモジュールの中に定義
[1] pry(main)> module Brainfsck
[1] pry(main)*   class Parser
[1] pry(main)*   end
[1] pry(main)* end
=> nil
[2] pry(main)> module Whitespace
[2] pry(main)*   class Parser ### 同じ名前のクラスを異なるモジュールに定義
[2] pry(main)*   end
[2] pry(main)* end
=> nil
[3] pry(main)> Brainfsck::Parser    ### 「::」で参照する
=> Brainfsck::Parser
[4] pry(main)> Whitespace::Parser
=> Whitespace::Parser

### 特異メソッドを定義して使う。インスタンス化しているわけではない。
[1] pry(main)> module Sweet
[1] pry(main)*   def self.lot  ### self.で特異メソッドを作成
[1] pry(main)*     %w(a b c d).sample  ### Array#sampleは要素をランダムに返す
[1] pry(main)*   end
[1] pry(main)* end
=> :lot
[2] pry(main)> Sweet.lot
=> "d"
[3] pry(main)> Sweet.lot
=> "a"

### ある名前空間のクラスやモジュールを参照する
[1] pry(main)> module Sweet
[1] pry(main)*   module Chocolate
[1] pry(main)*   end
[1] pry(main)*   class Brownie
[1] pry(main)*   end
[1] pry(main)* end
=> nil

### モジュールを参照
[2] pry(main)> Sweet::Chocolate
=> Sweet::Chocolate

### クラスを参照
[3] pry(main)> Sweet::Brownie
=> Sweet::Brownie
  • Mix-in
    • モジュールに定義されたメソッドをクラスのインスタンスメソッドとして取り込むこと
    • includeを使う
      • includeできるモジュールの数に制限は無い
        • クラスは継承できるのは一つだけ
      • 他のモジュールをモジュールにincludeすることはできる
    • 組み込みクラスにも用いられている
        • ArrayやHash,RangeにincludeされているEnumerableモジュール
        • eachメソッドの定義されているクラスにincludeするとeachによる繰り返し処理を用いたいくつかのメソッドが利用できるようになる
        • countメソッドやmapメソッドなど
  • extend
    • オブジェクトの特異メソッドとして取り込む
    • クラスもオブジェクトの一つなのでモジュールをextendすることができる
Mix-in
[1] pry(main)> module Greetable
[1] pry(main)*   def greet_to(name)
[1] pry(main)*     puts "Hello, #{name}. My name is #{self.class}."
[1] pry(main)*   end
[1] pry(main)* end
=> :greet_to

### includeすることでgreet_toメソッドを呼び出すことができるようになる
[2] pry(main)> class Alice
[2] pry(main)*   include Greetable
[2] pry(main)* end
=> Alice
[3] pry(main)> alice = Alice.new
=> #<Alice:0x007f9e82394108>
[4] pry(main)> alice.greet_to 'Bob'
Hello, Bob. My name is Alice.
=> nil

### extendでオブジェクトにモジュールのメソッドを取り込む
[1] pry(main)> module Greetable
[1] pry(main)*   def greet_to(name)
[1] pry(main)*     puts "Hello, #{name}. I'm a #{self.class}."
[1] pry(main)*   end
[1] pry(main)* end
=> :greet_to

### Objectクラスからインスタンスを作成
[2] pry(main)> o = Object.new
=> #<Object:0x007f33a0ac6658>

### extendで特異メソッドとして取り込む
[3] pry(main)> o.extend Greetable
=> #<Object:0x007f33a0ac6658>

### 特異メソッドを使う
[4] pry(main)> o.greet_to 'World'
Hello, World. I'm a Object.
=> nil
  • モジュール関数
    • サブルーチンとして利用されることを目的としたメソッド
    • privateなインスタンスメソッドであると同時にモジュールの特異メソッドでもある
    • モジュール関数としてメソッドを定義することで以下のように使える
      • モジュールから直接呼び出せる
      • includeして使う事ができる
    • モジュール関数化
      • module_functionを使う
      • 複数定義する場合は先頭にmodule_functionを記載する
モジュール関数
### モジュールから直接呼び出す。モジュールをレシーバにしている
[1] pry(main)> Math.sqrt(4)
=> 2.0

### モジュルールをincludeして使う
[2] pry(main)> include Math
=> Object
[3] pry(main)> sqrt(4)
=> 2.0

### モジュールに定義したメソッドをモジュール関数化する
[1] pry(main)> module MyFunctions
[1] pry(main)*   def my_module_function
[1] pry(main)*     puts 'ok'
[1] pry(main)*   end
[1] pry(main)*   module_function :my_module_function  ### モジュール関数化
[1] pry(main)* end

=> MyFunctions
[2] pry(main)> MyFunctions.my_module_function
ok
=> nil
[3] pry(main)> include MyFunctions
=> Object
[4] pry(main)> my_module_function
ok
=> nil
  • autoload
    • 毎回必ずロードするとは限らない外部ファイルの読み込みに使う
      • たくさんのライブラリを使ったプログラムを実行するとrequireにかかる時間やリソースの消費が無視できない場合がある
        • コマンドラインで頻繁に起動を繰り返すようなアプリケーションなど
          • 滅多に使わないのに毎回読み込むのは無駄
    • 引数に指定したクラスモジュール名が最初に参照された時、外部ファイルを自動的にrequireする
autoload
autoload :MySweets, 'my_library/my_sweets'
MySweets   ### ここでrequireされる

### ネストしたクラスやモジュールを読み込む場合
module MySweets
  autoload :Cake, 'my_library/my_sweets/cake'
end

MySweet::Cake ### ここでrequireされる

オブジェクト

  • Objectクラスに定義されているオブジェクトの基本的な振る舞いについて

    • ほとんどのクラスはObjectクラスから派生したサブクラス
    • 新しく定義されたクラスは必ず既存の継承ツリーのどこかに収まる
    • スーパークラスを指定しなければ自動的にObjectクラスを継承する
  • Objectクラス

    • そのオブジェクトの情報を返すメソッドや比較演算子などが実装されている
    • Objectクラスのメソッドはサブクラスで必要に応じてオーバーライドする必要がある
  • Object#==

    • 同一性を返す演算子メソッド
    • ほとんどのクラスでは同値性を返すようオーバーライドされている
Objectクラス
[1] pry(main)> o = Object.new
=> #<Object:0x007fa5b5ca7620>
[2] pry(main)> o.class   ### どのクラスのオブジェクトか?
=> Object
[3] pry(main)> o.is_a?(Object) ### Objectクラスのインスタンスか?
=> true
[4] pry(main)> o.object_id  ### オブジェクト固有のID
=> 70174848138000
[5] pry(main)> o.nil?  ### nilか?
=> false
[6] pry(main)> o.frozen? ### frozenされているか_
=> false
[7] pry(main)> o.tap {|v| puts v}   ### 自身をブロック引数にしてブロックを実行し、自身を返す
#<Object:0x007fa5b5ca7620>
=> #<Object:0x007fa5b5ca7620>
  • Object#freeze
    • レシーバへの破壊的な操作を禁止することができる
    • 変更しようとすると例外RuntimeErrorが発生する
    • 一度freezeされたオブジェクトは二度と変更することはできない
    • 定数など変更されることを想定しないオブジェクトを守ることができる
freeze
[1] pry(main)> DEFAULT_SETTINGS = {host: 'localhost', port: 8080}.freeze
=> {:host=>"localhost", :port=>8080}
[2] pry(main)> DEFAULT_SETTINGS.frozen?
=> true
  • オブジェクトをコピーする
  • 浅いコピー(shallow copy)
    • 自身のコピーを返すが、自身の参照している他のオブジェクトはコピーしない
      • 配列をコピーした時、配列の要素まではコピーされない
    • Object#dup
      • オブジェクトをコピーする
    • Object#clone
      • 自身がfreezeされているかや、特異メソッドの情報も含めてコピーする

``` ruby:shallow copu
[1] pry(main)> value = 'foo'
=> "foo"
[2] pry(main)> array = [value]
=> ["foo"]
[3] pry(main)> array_dup = array.dup
=> ["foo"]
[4] pry(main)> array_clone = array.clone
=> ["foo"]

全て同じオブジェクトID

[5] pry(main)> value.object_id
=> 70297012757380
[6] pry(main)> array_dup[0].object_id
=> 70297012757380
[7] pry(main)> array_clone[0].object_id
=> 70297012757380
```

  • 汚染されたオブジェクト
    • Rubyにはセーフレベルという外部からの入力によって危険な操作が行われる事を未然に防ぐ機能がある
    • 外部からの入力は汚染されたオブジェクトとして扱われる
      • 環境変数
      • コマンドライン引数
    • 汚染されているとみなされるオブジェクトと禁止される操作はセーフレベル毎に変わる
    • ほとんどの環境ではデフォルトはセーフレベル0
    • Object#tainted?
      • オブジェクトが汚染されているかの確認
    • Object#taint
      • 汚染されているオブジェクトに変更できる
    • 汚染は引き継がれる
      • 汚染されたオブジェクトを元に生成されたオブジェクトも汚染されている
    • セーフレベル
      • 0
        • デフォルト
        • IOや環境変数、コマンドライン引数から得られた文字列には汚染マークが付く
      • 1
        • 汚染マークが付く対象は0と同じ
        • 汚染されたオブジェクトを引数としたファイル操作、コマンドの実行、シグナルのトラップなどが禁止される
      • 2
        • 汚染マークが付く対象は0と同じ
        • 1の制限に加えプロセスに関する操作も禁止される
      • 3
        • オブジェクトには全て汚染マークが付く
        • 2までの制限に加え汚染を解除することが禁止される
      • 4
        • 汚染マークが付く対象は3と同じ
        • 3の制限に加えグローバル変数や汚染されていないオブジェクトの変更、メソッドの再定義が禁止される
    • $SAFE
      • 新しい値を代入することによりセーフレベルを変更できる
        • 上げることはできるが下げることはできない
      • 一時的に上げたい場合はProcオブジェクトの中だけでセーフレベルを上げる
セーフレベル
### デフォルトは0
[1] pry(main)> $SAFE
=> 0

### 外部からの入力ではないので汚染されていない
[2] pry(main)> Object.new.tainted?
=> false

### 外部からの入力は汚染されている
[3] pry(main)> ENV['HOME'].tainted?
=> true

### セーフレベル1では、汚染されたオブジェクトを使ってファイルの削除はできない
[1] pry(main)> $SAFE = 1
=> 1
[2] pry(main)> rcfile = '/home/vagrant/.bashrc'
/usr/local/rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/pry-0.10.1/lib/pry/history.rb:96:in `<<': Insecure operation - << (SecurityError)
...

### Procオブジェクトの中のみセーフレベルが上がっている
[1] pry(main)> $SAFE
=> 0
[2] pry(main)> level4 = Proc.new { $SAFE = 4 }
=> #<Proc:0x007f473c36bbb0@(pry):2>
[3] pry(main)> level4.call
ArgumentError: $SAFE=4 is obsolete
from (pry):2:in `block in __pry__'
[4] pry(main)> $SAFE
=> 0

組み込みクラス

組み込みクラスに移動

例外

  • プログラムは実行中にエラーが発生すると例外によってそれが通知される
    • 例外の例
      • 0除算
      • 存在しないファイルをオープンしようとした
      • 存在しない変数やメソッドを参照しようとした
  • 例外が発生すると後続処理は中断される
  • 発生した例外が捕捉されない場合、プログラムは例外メッセージを出力して終了する
例外
### 例外の例 (0除算)
[1] pry(main)> 1 / 0
ZeroDivisionError: divided by 0
from (pry):1:in `/`

### 例外処理の例
[1] pry(main)> begin
[1] pry(main)*   1 / 0
[1] pry(main)* rescue ZeroDivisionError
[1] pry(main)*   puts '1を0では割れません。'
[1] pry(main)* end
10では割れません。
=> nil
  • 例外発生と例外クラス

    • 例外が発生 -> コールスタックをさかのぼる -> トップレベルまで伝搬する
      • トップレベルでメソッドA呼び出し
      • メソッドAがメソッドBを呼び出し
      • メソッドBで例外発生
      • メソッドBで捕捉されず
      • メソッドAで捕捉されず
      • トップレベルで捕捉されず
      • 異常終了
        • -> メソッドA,B,トップレベルに例外処理を記述する事で適切な制御を行う事ができる
  • 例外オブジェクト

    • Exceptionまたはそのサブクラスのインスタンス
    • 構造
      • Exception
        • NoMemoryError
        • ScriptError
          • LoadError
          • NotlmplementedError
          • SyntaxError
        • SecutiryError
        • SignalException
          • Interrupt
        • StandardError
          • (色々)
        • SystemExit
        • SystemStackError
  • 例外の制御

    • raise (Kernel.#raise)
      • 例外を発生させる
    • 発生した例外に対して処理を行う場合
      • begin節に例外が発生する可能性のある処理を書く
      • rescue節に例外処理を記述する
        • 捕捉したい例外クラスを指定できる
          • 指定した例外クラスとそのサブクラスが対象
        • 何も指定しなければStandardErrorとそれを継承した例外が捕捉される
    • 例外の有無に限らず必ず実行したい処理はensure節に記述する
    • 例外が発生しなかった場合のみ実行する処理はelse節に記述する
    • 例外発生後に再実行する場合はrescue節の中でretryを呼び出す
raise
### 第一引数に文字列を指定した場合はそれを例外メッセージとしてRuntimeErrorを発生させる
[1] pry(main)> raise 'error!'
RuntimeError: error!
from (pry):1:in '__pry__'

### 第一引数に例外クラスを指定した場合、第二引数で例外メッセージを指定する
[2] pry(main)> raise StandardError, 'error!'
StandardError: error!
from (pry):2:in '__pry__'

### 例外を捕捉する。
[3] pry(main)> begin
[3] pry(main)*   raise 'error!'  ### errorの発生しそうな処理を書く
[3] pry(main)* rescue => e   ### 捕捉した例外オブジェクトが変数eに代入される
[3] pry(main)*   puts "Error occurred (#{e.class})"
[3] pry(main)* end
Error occurred (RuntimeError) ### 例外オブジェクトのクラスが分かる
=> nil

### 例外についての情報を得るためのいくつかのメソッドがある
[4] pry(main)> e.class
=> RuntimeError

### Exception#messageは例外についての人が読めるメッセージを返す
[5] pry(main)> e.message 
=> "error!"

### Exception#backtraceは例外が発生した時点のコールスタックを配列で返す
### ファイル名:行番号:in `メソッド名'
[6] pry(main)> e.backtrace 

=> ["(pry):4:in '__pry__'",
 "/usr/local/rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/pry-0.10.1/lib/pry/pry_instance.rb:355:in `eval'",
 "/usr/local/rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/pry-0.10.1/lib/pry/pry_instance.rb:355:in `evaluate_ruby'",
...
 "/usr/local/rbenv/versions/2.2.2/bin/pry:23:in `<main>'"]

### Exception以下すべての例外を捕捉する場合
[7] pry(main)> begin
[7] pry(main)*   do_process
[7] pry(main)* rescue Exception => e ### ここでExceptionを指定
[7] pry(main)*   puts "error"
[7] pry(main)* end
error
=> nil

### 1つのbegin...endに対して複数のrescue節を書ける。最初にマッチしたものが捕捉される
[8] pry(main)> begin
[8] pry(main)*   do_process
[8] pry(main)* rescue LoadError => e
[8] pry(main)*   puts "LoadError"
[8] pry(main)* rescue ArgumentError => e
[8] pry(main)*   puts "ArgumentError"
[8] pry(main)* end

### 1つのrescue節に複数の例外を書ける
[9] pry(main)> begin
[9] pry(main)*   do_process
[9] pry(main)* rescue ArgumentError, NameError => e
[9] pry(main)*   puts "error"
[9] pry(main)* end

### 例外をログに書き出し、その後同じ例外を再度発生させる
[10] pry(main)> begin
[10] pry(main)*   raise StandardError, 'hi'
[10] pry(main)* rescue => e
[10] pry(main)*   logger.error e.message
[10] pry(main)*   raise
[10] pry(main)* end

### 後置rescue。例外が発生したときに渡した式の値が戻り値となる
### StandardErrorとそのサブクラスにあたる例外クラス限定
[11] pry(main)> 1 / 0 rescue false
=> false

### ensure節の処理は必ず実行される
[12] pry(main)> begin
[12] pry(main)*   file = File.open('whatever.txt')
[12] pry(main)*   do_process file
[12] pry(main)* rescue
[12] pry(main)*   puts '何かおきた'
[12] pry(main)* ensure
[12] pry(main)*   file.close if file ### 必ずファイルを閉じる処理は実行
[12] pry(main)* end
何かおきた
=> nil

### else節を使うと例外が発生しなかった場合だけ行う処理を記述できる
[13] pry(main)> begin
[13] pry(main)*   file = File.open('whatever.txt')
[13] pry(main)* rescue SystemCallError
[13] pry(main)*   puts 'ファイルを開くとき何かあった'
[13] pry(main)* else
[13] pry(main)*   puts 'ファイル開けたよ'
[13] pry(main)* end
ファイルを開くとき何かあった
=> nil

### 例外が発生した場合5回リトライする
[14] pry(main)> begin
[14] pry(main)*   failed ||=0  ### 値が無ければ0が入る
[14] pry(main)*   puts 'trying...'
[14] pry(main)*   process!
[14] pry(main)* rescue
[14] pry(main)*   failed +=1
[14] pry(main)*   retry if failed < 5
[14] pry(main)* end
trying...
trying...
trying...
trying...
trying...
=> nil

外部ファイルの読み込み

  • ある程度プログラムが大きくなるとクラスやモジュール単位でファイルを分ける
    • 見通しが良くなる
    • 別のプログラムから再利用できる
  • Kernel.#require

    • 別ファイルに用意した既存ライブラリ、gemパッケージやプログラムを読み込む
    • 引数にファイル名をうけとり、そのライブラリを読み込んで実行する
    • $LOAD_PATH(or $:)
      • ここに含まれるディレクトリを順番に探し最初に見つかったものを読み込む
      • $LOAD_PATHにこのファイルのあるディレクトリを追加する例
        • $LOAD_PATH << File.dirname(_FILE_)
    • ./や../はRubyが実行されているディレクトリを起点としてファイルを探す
    • 指定できるファイル
      • Rubyスクリプト
      • 拡張ライブラリ(.so, .dll, .bundle)
        • 明示する必要がなければ拡張子は省略できる
    • $LOADED_FEATURES (or $")
      • 一度読み込んだファイルのPATHはここに追加され、同じPATHのファイルは一度しか読み込まれない
        • ただし、SymlinkなどでPATHが違っている場合は2回読み込まれてしまう
  • Kernel.#require_relative

    • 実行中のファイルから見た相対パスでrequireを行う
      • require_relativeを呼び出したファイルのディレクトリが基準となる
    • $LOAD_PATHは探索されない
  • Kernel.#load

    • 拡張子の自動的な補完は行わない
    • 同じパスのファイルを何度でも読み込むことができる
    • 設定ファイルのような役割のファイルを読み込むために用いられることが多い
    • 絶対パスを指定して使用される事が多い
    • このディレクトリのconfig.rbを読み込む例
      • load _dir_ + '/config.rb'
requireでのPATH指定
### 絶対PATHでlibrary.rbを読み込み
require '/path/to/library.rb'

### 相対PATHでlibrary.rbを読み込み。拡張子は省略できる。
require './library'

### 組み込み変数$LOAD_PATHから探す
require 'library'

添付ライブラリ

  • Rubyに付属しているライブラリ

テキストライブラリ

  • 文字列をIOオブジェクトのように取り扱うことができる

予約語

  • 予約語をローカル変数やトップレベルの定数名として使用することはできない
  • インスタンス変数、クラス変数、グローバル変数は予約語と同じ名前を指定できる
    • 紛らわしいのでやらないほうが良い
    • class, ensure, super, alias, yield, redo, rescure, retry, _LINE_, _FILE_, _ENCODING_, ...

組み込み変数/定数

擬似変数

  • 参照専用の特殊な変数
    • 代入を行うことはできない
    • self, true, false, nil
    • _LINE_ : 現在実行中の行番号
    • _FILE_ : 現在実行中のソースのファイル名
    • _ENCODING_ : 現在のソースでのスクリプトエンコーディング
  • 擬似変数ではないが、Ruby2.0以降では_dir_で_FILE_が配置されているディレクトリパスを取得できる
    • -> 変数ではなくKernelモジュールのメソッド

組み込み変数

  • グローバル変数でいくつか用意されているもの
    • $から始まる変数
  • どこからでも参照できるが、実際はもっとスコープが狭く参照する場所によって異なる値を参照することもある
    • $>
      • 標準出力。初期値はSTDOUT
      • $stdoutとも書ける
    • $!
      • 最後に発生した例外オブジェクト
    • $$
      • 実行中のRubyのプロセスID
    • $~
      • 最後に成功した正規表現マッチに関する情報
    • $*
      • Rubyスクリプトに与えられた引数の配列
    • $<
      • Rubyスクリプトに与えられた引数または標準入力で構成される仮想ファイル
    • $_
      • ARGFからKernel.#getsまたはKernel.#readlineで最後に読み込んだ行
    • その他いろいろ

組み込み定数

  • トップレベルには以下のような組み込み定数が定義されている
    • STDIN
      • 標準入力$stdinのデフォルト値
    • STDOUT
      • 標準出力$stdoutのデフォルト値
    • STDERR
      • 標準エラー出力$stderrのデフォルト値
    • ARGV
      • Rubyスクリプトに与えられた引数の配列。$*と同じ
    • ARGF
      • Rubyスクリプトに与えられた引数または標準入力で構成される仮想ファイル。$<と同じ
    • その他いろいろ
11
14
2

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
11
14