tanish-kr's learning log

Learning output log

Ruby

メソッド定義と呼び出し

メソッド呼び出しと括弧

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

func "aaa"
func("aaa")

メソッド呼び出しとローカル変数

メソッド名と同じローカル変数がある場合、括弧をつけないと、ローカル変数へのアクセスと見なされます

sweet = "honey"

def sweet
  "salt"
end

# ローカル変数へのアクセス
sweet

# メソッド呼び出し
sweet()

メソッドと定数

::でのアクセスで先頭文字が大文字の場合は、定数と見なされます

Klass::Foo

戻り値

メソッドの中で最後に評価された式の値は、そのメソッドの戻り値となります。

def call
  "called"
end

call

省略可能な仮引数

メソッドの仮引数には、デフォルト値として任意の式を与えるとが出来ます。デフォルト値のある仮引数は「省略可能な仮引数」と呼ばれます。
メソッド呼び出し時に引数が省略された場合、デフォルト値がnilであってもデフォルト値は使用されません。

def great(name, message = "Hi")
  "#{message}, #{name}"
end

great "Ruby" # "Hi, Ruby"
great "Ruby", "Hello" # "Hello, Ruby"
great "Ruby", nil # ", Ruby"

可変長引数

仮引数の先頭に*をつけることで、任意の数の引数を配列として受け取ることが出来ます。これは「可変長引数」と呼ばれます。可変長引数は1つのメソッドに1つだけ指定することが出来ます。

def great(name, *message)
  messages.each do |message|
    puts "#{message}, #{name}"
  end
end

great "Ruby", "Hello", "こんにちは"   # Hello, Ruby \n こんにちは, Ruby

配列の展開

実引数の頭に*をつけると、配列を複数の引数として渡すことが出来ます。配列をメソッド呼び出しの引数リストとして渡したい場合に便利です。

def greet_twice(name, first_message, second_message)
  puts "#{first_message}, #{name}"
  puts "#{second_message}, #{name}"
end

greetings = %w[Hello Hi]
greet_twice "Ruby", *greetings  # "Hello, Ruby\nHi, Ruby"

ブロック

メソッドはブロックを受け取ることが出来ます。メソッドは受け取ったブロックを任意のタイミングで、任意の回数実行することができます。配列のeachメソッドはブロックを受け取り、要素の数だけブロックを実行します。

[1, 2, 3].each { |i| puts i }

yield

メソッドの中でyieldを呼び出すと、受け取ったブロックを実行します。

def block_sample
  puts "stand up"
  yield
  puts "sit down"
end

block_sample do
  puts "walk"
end
# "stand up\n" "walk\n" "sit down\n"

block_sample # LoacalJumpError: no block given (yield)

blok_given?メソッドを使用することで、メソッドに対してブロックが与えられたがどうかを知ることが出来ます

def block_sample
  puts "stand up"
  yield if block_given?
  puts "sit down"
end


block_sample # "stand up\n" "sit down\n"

ブロックの戻り値・引数

yieldはブロックの戻り値を返します。ブロックの戻り値は、最後に評価された式の値です。
ブロックの中でnextを呼び出すと、処理は「yieldの呼び出し元」に戻ります。nextに与えた値はブロックの戻り値となります。
ブロックの中でbreakを呼び出すと、処理は「メソッドの呼び出し元」に戻ります。

def add_num
  num = 10
  num += yield
  num += 1
end


add_num { 1 }
=> 12

add_num { next 1 }
=> 12

add_num { break 1 }
=> 1

仮引数としてブロックを受け取る

&block引数を渡すことで、yieldと同じ役割を担うことが出来ます

def block_sample(&block)
  puts "stand up"
  block.call if block
  puts "sit down"
end

block_sample do
  puts "walk"
end
# "stand up\n" "walk\n" "sit down\n"

オブジェクトとしてブロックを渡す

メソッドの呼び出しを行う際、実引数の先頭に&をつけると、Procオブジェクトをブロックとして渡すことが出来ます。

people = %w(Alicee Bob Charlie)
block = Proc.new { |name| puts name }
people.each &block
people.map(&:upcase)

繰り返し以外に用いられるブロック

ブロックはeachのようなループ処理だけではなく、準備 -> 本質的な処理 -> 後片付けというような前後の処理を共通化出来るパターンの処理にも向いています。応用できる用途には以下のようなものが考えられます。

  • ファイルのオープン/クローズ
  • DBへの接続/切断
  • トランザクションの開始/終了
  • ロックと解放

ブロックローカル変数

ブロック内の変数と同名のローカル変数が、ブロック外に存在しないとき、その変数はブロックのローカル変数と見なされます。スコープはブロックの内部のみになります。ブロック外に同名のローカル変数があったとしても、ブロック引数は常にブロックローカル変数と見なされます。

someone = "Dave"
people = []

# ブロック引数として渡した変数はブロック外の変数に影響しない
%w(Alice Bob Charlie).each do |someone|
  people << someone # ブロック外の変数を更新
end

puts someone # Dave
puts people  # ["Alice", "Bob Charlie"]

ブロック引数以外にもブロックローカル変数を使用したい場合には、ブロックの仮引数リストの後にセミコロンを置き、その後ろに変数名を記述します。

someone = "Dave"

%w(Alice Bob Charlie).each do |person; someone|
  someone = person
end

puts someone # Dave

キーワード引数

キーワード引数とは名前のついた引数です。実引数を渡す際、以下のように引数の名前を指定して渡すことが出来ます。
キーワード引数にはデフォルト値は必須です。nilでも可となります。

def set_cache(key: nil, val: nil)
  write(key, val)
end

キーワード引数を用いつつも、それとは別にHashを受け取りたい場合、**をつけた仮引数を用いることが出来ます。

def keywords_with_options(alice: nil, bob: nil, **others)
  { alice: action, bob: bob, others: others }
end

keywords_with_options alice: "Alice", bob: "Bob", charile: "Charlie"