Ruby メソッド def

メソッドの () は省略できる

  • メソッドの「呼び出し時」に付与する () は省略できる
    • ただし、「曖昧でない・別の解釈ができない」場合に限定される
      • 曖昧な場合はエラーになるため、通常どおり () を付ける
  • 慣習的には省略できる場合は省略することが多い
def calc(n)
  n * n
end

p calc(5)
# 25

p calc 5
# 25
  • メソッドの「定義側」に付与する () も省略できる
    • 慣習的にはこの「定義側」では省略しないことの方が多い
  • ただし、引数の個数が 0 個の場合は省略することが多い
def calc n
  n * n
end

p calc 5
def calc()
  5 * 5
end

p calc
  • puts などのメソッドも実は
    • 呼び出し時に付与する () を省略して使っていた
puts 5
puts(5)
  • 下記のようなイメージ
def puts(x)
  画面に出力する処理
end

puts 5

メソッドを定義する def

  • 面積を求めるプログラムを例にする
p 5 * 5
  • メソッドは def で定義する
def area
  p 5 * 5
end

area # 25

メソッド名 method_name : スネークケース

  • すべて小文字

メソッドの戻り値

  • メソッドは最後の実行結果が戻り値となる
def area
  5 * 5
  2 * 2 # 最後の実行結果が戻り値になる
end

p area
# 4
  • 戻り値は変数にも代入できる
def area
  5 * 5
  2 * 2 # 最後の実行結果が戻り値になる
end

result = area
p result
# 4

複数の戻り値を返す

  • 複数の戻り値を返したいときは配列なども使える
def calc_nums(x, y)
  [x, y].map { |n| n * 2 }
end

p calc_nums(2, 5)
# [4, 10]

引数

def calc_nums(n)
  n * n
end

p calc_nums(5)
# 25
  • 引数は複数渡すことができる
    • 順番に注意
      • 下記の例の場合 x=3, y=2, z=4 がそれぞれ代入される
def calc_nums(x, y, z)
  x * y * z
end

p calc_nums(3, 2, 4)
# 24

複数の引数を渡す

  • 配列などのオブジェクトも引数として渡せる
def make_href_list(urls, titles)
  urls = urls.map {|url| "<li><a href=\"#{url}\">"}
  titles = titles.map {|title| "#{title}</a></li>"}
  urls.zip(titles).map(&:join)
end

urls = ["url1", "url2", "url3"]
titles = ["title1", "title2", "title3"]

lists = make_href_list(urls, titles)
lists.each {|list| p list}
# "<li><a href=\"url1\">title1</a></li>"
# "<li><a href=\"url2\">title2</a></li>"
# "<li><a href=\"url3\">title3</a></li>"

return

メソッドを途中で終わらせる

  • メソッド内で return 以降は実行されない
def greeting(judge)
  p "hello"
  unless judge
    return
  end
  p "goodbye"
end

greeting(true)
# "hello"
# "goodbye"

greeting(false)
# "hello"

戻り値を指定する

  • return には戻り値を指定する機能もある
    • return に続く内容が戻り値になる
  • return が実行されない場合は、通常どおりメソッドの最後の処理が戻り値になる
def greeting(judge)
  hello = "hello"
  unless judge
    return hello
  end
  hello + ", goodbye"
end

p greeting(true)
# "hello, goodbye"

p greeting(false)
# "hello"

デフォルト引数

def order(item = "coffee")
  p item
end

order("latte")
# "latte"
order("tea")
# "tea"
order
# "coffee"

キーワード引数

  • 引数の順番を変えることができる
  • メソッド定義
    • def メソッド名(引数:)
      • 引数名の後ろに :(コロン)を付ける
        • メソッド内の処理は : は不要
  • 呼び出し側
    • メソッド名(引数: 値)
      • ハッシュ形式の {} を除いた形で記載する
def order(item:, price:, size:)
  "#{item} -> #{size} -> #{price}"
end

p order(size: "L", item: "coffee", price: 500)
p order(size: "S", item: "tea", price: 350)

*順番を変えることができるメリットがある一方、呼び出し側の記述が冗長になるデメリットもある

  • 要素の数が増える場合は便利になるケースもあるが、シンプルなメソッドなら使わない方が簡略化できる
def order(item, price, size)
  "#{item} -> #{size} -> #{price}"
end

p order("coffee", 500, "L")
p order("tea", 350, "S")

デフォルト値つきキーワード引数

  • メソッドの引数にデフォルト値をセットできる
    • 引数: デフォルト値
      • = は不要なので注意
def order(item:, price:, size: "M")
  "#{item} -> #{size} -> #{price}"
end

p order(item: "coffee", price: 500)
p order(size: "S", item: "tea", price: 350)

ローカル変数とスコープ

ローカル変数

  • メソッド内の変数(ローカル変数)は、メソッド外からアクセスできない
def hello
  txt = "hello"
end

p hello
# "hello"
p txt
# undefined local variable or method `txt'
  • メソッド外の変数は、メソッド内からアクセスできない
txt = "hello"

def hello
  txt
end

p txt
# "hello"
p hello
# undefined local variable or method `txt'

スコープ

  • メソッド内のローカル変数 & それが指すオブジェクトは、メソッドが実行されると内容はそこで破棄される
def hello
  txt = "hello"
end

p hello # ← ここで txt = "hello" は破棄される
p txt
  1. p hello 実行
  2. txt = "hello" 破棄される
  3. p txt → 変数 txt は存在しないためエラー
  • 変数にアクセスできる範囲 & 寿命 → スコープ と呼ぶ

スコープ外の変数をメソッド内に取り込む

  • これはエラーになる
def greet
  txt
end

txt = "hello"
p greet
# undefined local variable or method `txt'
  • 単純にスコープ外の変数を、メソッドの引数にセットすればよい
def greet(txt) # スコープ外の変数をメソッド引数にセット
  txt
end

txt = "hello"
p greet(txt)
# "hello"

例文

例文 1

def price(item:, size:)
  case size
  when "ショート"
    n = 0
  when "トール"
    n = 50
  when "ベンティ"
    n = 100
  end

  case item
  when "コーヒー"
    300 + n
  when "カフェラテ"
    400 + n
  end
end

p price(item: "コーヒー", size: "ベンティ")
p price(item: "カフェラテ", size: "トール")
# 400
# 450

例文 2 return で処理を中断する

  • before
def dice
  n = (1..6).to_a.sample
  if n == 1
    puts "#{n} が出たのでもう一回"
    (1..6).to_a.sample
  else
    n
  end
end

puts dice
  • after
    • return 実行で処理が中断されるので if で条件分岐する必要がない
    • (1..6).to_a.sample の処理が重複しているのでメソッドにして呼び出す
def dice_core
  (1..6).to_a.sample  
end

def dice
  n = dice_core
  return n unless n == 1
  puts "#{n} が出たのでもう一回"
  dice_core
end

puts dice

例文 3 case を使わずハッシュ化する

  • before
def price(item:)
  case item
  when "コーヒー"
    300
  when "カフェラテ"
    400
  end
end

p price(item: "コーヒー")
p price(item: "カフェラテ")
  • after
    • case を使わず、ハッシュ化することで行数を少なく記述できる
def price(item:)
    items = {"コーヒー" => 300, "カフェラテ" => 400}
    items[item]
end

p price(item: "コーヒー")
p price(item: "カフェラテ")

例文 4 case の戻り値は変数に代入できる

  • before
def price(item:, size:)
  case size
  when "ショート"
    n = 0
  when "トール"
    n = 50
  when "ベンティ"
    n = 100
  end

  case item
  when "コーヒー"
    300 + n
  when "カフェラテ"
    400 + n
  end
end

p price(item: "コーヒー", size: "ベンティ") # 400
p price(item: "カフェラテ", size: "トール") # 450
  • after
    • case の戻り値は変数に代入できる
def price(item:, size:)
  total = case size
  when "ショート"
    n = 0
  when "トール"
    n = 50
  when "ベンティ"
    n = 100
  end

  total += case item
  when "コーヒー"
    300
  when "カフェラテ"
    400
  end
end

p price(item: "コーヒー", size: "ベンティ")
p price(item: "カフェラテ", size: "トール")
  • ハッシュを使うことで簡潔な記述もできる
def price(item:, size:)
  items = {"コーヒー" => 300, "カフェラテ" => 400}
  sizes = {"ショート" => 0, "トール" => 50, "ベンティ" => 100}
  total = items[item] + sizes[size]
end

p price(item: "コーヒー", size: "ベンティ")
p price(item: "カフェラテ", size: "トール")