LoginSignup
0

More than 5 years have passed since last update.

パーフェクトRuby勉強会 10/17 3章

Last updated at Posted at 2013-10-17

まえがき


3章 制御構造/メソッド/組み込み関数

3-1 演算子

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


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

演算子メソッドの独自定義

「+」演算子のメソッドを引き算として定義してみる
class MyNumber
  def initialize(val)
    @val = val
  end

  def +(other)
    @val - other
  end
end

val = MyNumber.new(3)
puts val + 3  # 0

3-2 基本的な制御構造

3-2-1 条件分岐

case文はJavaより柔軟性が高い

case文で正規表現を使ったパターン
stone = 'sardonyx'
case stone
  when /ruby/
    puts '7月'
  when /peridot|sardonyx/
    puts '8月'
  else
    puts 'よくわかりません'
end
case文は最後に評価した値を返す
detected =
    case stone
      when /ruby/
        '7月'
      when /peridot|sardonyx/
        '8月'
      else
        'よくわかりません'
    end
puts detected
case文はwhenの値をレシーバーとして「===」メソッドで比較している
class MyString
  def initialize(val)
    @val = val
  end

  def ===(other)
    @val.upcase == other.upcase
  end
end

a = MyString.new('A')
b = MyString.new('B')
c = MyString.new('C')
case 'a'
  when a
    puts 'Aでした'
  when b
    puts 'Bでした'
  when c
    puts 'Cでした'
  else
    puts 'どれでもありませんでした'
end

3-2-3 ジャンプ構文

next (Javaでいうcontinue)

ループの中でnextを呼ぶと次の繰り返しに移る
languages = %w(Prel Python Ruby Smalltalk JavaScript)

languages.each do |language|
  puts language
  next unless language == 'Ruby'
  puts 'I found Ruby!!!'
end

redo

ループの中でredoを呼ぶともう一度その処理をやり直す
languages = %w(Prel Python Ruby Smalltalk JavaScript)

languages.each do |language|
  puts language
  if language == 'Ruby'
    puts 'I found Ruby!!!'
    redo if rand(2) == 1  # 無限ループになるのをさける
  end
end

3-3 例外処理

3-3-2 例外を制御する

raiseで投げる、rescueで受け取る

raiseで投げる、rescueで受け取る
begin
  raise 'error!'
rescue => e
  puts e.class # RuntimeError
  puts e.message
  puts e.backtrace
end

投げる例外を指定する

投げる例外を指定する
begin
  raise StandardError, 'error!'
rescue => e
  puts e.class
  puts e.message
  puts e.backtrace
end

受け取る例外を指定する

受け取る例外を指定する
5.times do |n|
  begin
      if n%3 == 0
        raise LoadError
      elsif n%3 == 1
        raise NameError
      else
        raise ArgumentError
      end
  rescue LoadError => e
    puts 'rescue LoadError'
  rescue ArgumentError, NameError => e
    puts "rescue #{e.class}"
  end
end

後置rescue

後置rescue
result = (1 / 0 rescue false)
puts result # false

ensure (Javaでいうfinally)

例外発生に関わらず必ず実行したい処理はensure節に書く
begin
  file = File.open('test.txt')
  file.each_line do |l|
    puts l
  end
rescue => e
    puts e.class
    puts e.message
    puts e.backtrace
ensure
  file.close if file
end

else

例外発生しなかった時だけ実行する処理はelse節に書く
begin
  file = File.open('test.txt')
  file.each_line do |l|
    puts l
  end
rescue => e
    puts e.class
    puts e.message
    puts e.backtrace
else
  puts 'ファイルが開けました!!'
ensure
  file.close if file
end

retry

rescue句の中でretryを呼び出すとbegin節の処理をやり直す
begin
  failed ||= 0
  puts 'trying...'

  raise
rescue
  failed += 1
  retry if failed < 5
end

3-5 メソッド定義と呼び出し

3-5-1 メソッド呼び出しと括弧

メソッド呼び出しの括弧は省略可能

メソッド呼び出しの括弧は省略可能
puts 'hello'
puts('hello')

括弧を付けるかどうかの判断基準(の一例)

  • 戻り値を得るための式として記述する場合は括弧を付ける
  • 手続きを実行する場合は括弧を省略する

3-5-2 メソッド呼び出しとローカル変数

レシーバと括弧、引数のないメソッドと変数は変数が優先される

レシーバと括弧、引数のないメソッドと変数は変数が優先される
sweet = 'honey'
def sweet
  'salt'
end

puts sweet  # honey
puts sweet()  # salt

3-5-3 メソッドと定数

大文字で始まるメソッドをレシーバ・引数なしで呼び出そうとするとエラーになる

大文字で始まるメソッドをレシーバ・引数なしで呼び出そうとするとエラーになる
def Hello
  puts 'Hello, My method!!'
end

begin
  Hello
rescue => e
  puts "#{e.class}, #{e.message}" # NameError, uninitialized constant Hello
end

3-5-5 省略可能な仮引数

メソッドの引数には任意の式を与えることができる

メソッドの引数には任意の式を与えることができる
def greet(name, message = 'Hi')
  puts "#{message}, #{name}"
end

greet 'kamatama'
greet 'kamatama', 'Hello!!!'

3-5-6 可変長引数

引数の先頭に*を付けることで、任意の数の引数を配列として受け取ることができる

引数の先頭に*を付けることで、任意の数の引数を配列として受け取ることができる
def greet_messages(name, *messages)
  messages.each do |message|
    puts "#{message}, #{name}"
  end
end

greet_messages 'kamatama', 'Hi!!', 'Hello!!'

3-5-7 配列の展開

実引数の頭に*を付けると配列を複数の引数として渡すことができる

実引数の頭に*を付けると配列を複数の引数として渡すことができる
def greet_twice(name, first_message, second_message)
  puts "#{first_message}, #{name}"
  puts "#{second_message}, #{name}"
end

greetings = %w(Hello Hola)
greet_twice 'kamatama', *greetings

# 引数に指定しない数を渡すとArgumentErrorになる
begin
  greetings = %w(Hello Hola Hey)
  greet_twice 'kamatama', *greetings
rescue => e
  puts "#{e.class}, #{e.message}"
end

3-5-8 ブロック

メソッドはブロック(一連の処理を含んだオブジェクト)を受け取ることができる。

yieldをメソッドの中で呼び出すと、受け取ったブロックを実行する
def block_sample
  puts 'stand up'
  yield
  puts 'sit down'
end

block_sample do
  puts 'walk'
end

ブロックは最後に評価された式を返り値として返すことができる

ブロックは最後に評価された式を返り値として返すことができる
def display_value
  puts yield
end

display_value do
  4423
end

仮引数の先頭に&を付けることでメソッドにブロックを渡すことができる

yieldとの違いは仮引数はオブジェクト(Procクラス)なので、他のメソッドにさらに引数として渡すことができる

仮引数の先頭に&を付けることでメソッドにブロックを渡すことができる
def block_sample_with_block_arg(&block)
  puts 'stand up'

  block.call if block

  puts 'sit down'
end

block_sample_with_block_arg do
  puts 'walk'
end

ブロックの仕様用途

繰り返し処理
配列の要素を全部大文字にした新しい配列を作る
people = %w(Alice Bob Carol)

puts people.map{|person| person.upcase}.inspect # ["ALICE", "BOB", "CAROL"]
前後の処理を共通化できる場合
  • ファイルのオープン/クローズ
  • DBへの接続/切断
  • トランザクションの開始/終了
  • ロックと解放
  • ...など

課題

課題1

1-1. 以下の仕様を満たすModクラスを作成し、Modクラスとwhen句を使ってFizzBuzzを完成させる

  • 整数のインスタンス変数(valとする)を保持する
  • 引数をvalで割った余りが0の場合のみtrueを返す「===」メソッドを持つ

課題2

2-1. 以下の仕様を満たすtimeメソッドを作り、実行してみる

  • ブロックを引数に取り、ブロックの処理時間(秒)を出力する
  • 例外が発生した場合も処理時間は計測できる

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
0