0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

ruby メソッドの引数をまとめてみた

Posted at

rubyの引数

メソッドは引数を取らないものもあれば、取るものもあります。
今回は、引数にはいくつかの種類があるのでまとめてみました。

位置引数

位置引数はおそらく引数の中でも最も一般的です。
以下のようにメソッドにnameやageを取ったりします。

def greet(name, age)
  puts "Hello, #{name}! You are #{age} years old."
end

greet("John", 25)

ただ、引数が1つだけだと分かりやすいのですが、これが複数個になった場合にはその順序に気をつけなければいけません。

例えば、
File.rename(file1, file2)というメソッドの場合は、変更元、変更先という順序で引数を取ります。基本的にはこの順序が多い気がしますが、逆の場合も当然あります。

例えば、alias_methodがそうです。
alias_methodの場合は、先に変更先を書いて、その後、変更元を書きます。

class MyClass
  def original_method
    puts "This is the original method."
  end

  alias_method :new_method, :original_method
end

obj = MyClass.new
obj.new_method

全部同じならいいのに...と思ったりもしますが、引数2個くらいならまだ許せます。
ただ、引数が増えれば増えるほど認知的負荷も高まったり、メモリ配置によって実行性能も低下したりするみたいです。

必須のキーワード引数

引数が増えたときに順序の問題を解決する方法として、必須のキーワード引数というものを使えます。

かなり適当なコードですが、以下のような感じです。
メソッドを呼び出すときに、引数に指定されたキーワードを使用することができます。

def greet(name:, email:, phone_number:)
  puts "Hello, #{name}!"
  puts "mail:  #{email}"
  puts "tel:   #{phone_number]"
end

greet(name: "Taro", email: "hoge@hoge.com", phone_number:"000-000")

しかし、これは以下の2点の大きな問題があります。

  • 別にキーワードは使用しなくても使える
  • 使うとしてもかなり面倒くさい

さらに以下のように書くこともできるみたいです。
しかも、これでArgumentError例外は発生しないようです。

greet(Yuji, bar@bar.com, 111-111,
      name: "Taro", email: "hoge@hoge.com", phone_number:"000-000")

したがって、かなり使いづらい印象です。
自分はあまり見たことないのですが、プロジェクトによっては使われるんですかね...

オプショナル位置引数

オプショナル位置引数は、とっても取らなくても良いオプショナルな引数のことです。そのまま。
例えば、以下のようにデフォルト値を設定しておくことで、引数を取らない場合は、デフォルト値が使用されます。

def greet(name = "Stranger")
  puts "Hello, #{name}!"
end

greet("John")     # "Hello, John!"
greet             # "Hello, Stranger!"

ただ、少しややこしいのが順序によって使える場合と使えない場合があるので注意が必要です。

以下は書籍の引用です。

「必須の位置引数でオプショナル位置引数を挟める」のに「オプショナル引数で必須の位置引数は挟めない」という挙動になっています。これには内部的な理由があります。

実はRubyの位置引数には 4 種類あり(可変長引数も位置引数の一種とみなす場合)、引数の種類 ごとに指定できる順序が決まっています。その種類と指定順は次のとおりです。

  1. 先導引数(leading argument)
  2. オプショナル引数(optional argument)
  3. 可変長引数(rest argument)
  4. 後続引数(post argument)

したがって、以下を意味します。

#これはOK
def a(x, y=2, z)
end

#これはNG
def a(x=1, y, z=1)
end

オプショナル引数で必須の位置引数を挟めない理由を説明すると、
まず、xがオプショナル引数なので、yは後続引数となります。

そして、=に来たところでエラーが発生します。その理由は後続引数がオプショナル引数のようにデフォルト値を設定できないからです。

可変長引数

可変長引数を配置できるのは、1つのメソッド定義につき1回だけです。
可変長引数に格納されるのは、メソッドが呼び出された時点で先ほど引用した

  • 先導引数
  • オプショナル引数
  • 後続引数

いづれにも処理されなかった残りの位置引数の全て となります。

この引数は何個でも渡すことができるので、いくつ渡すか不明な場合にも使用できます。
柔軟に使用できるのは利点ですね。

例えば、以下のように*を引数につけて、書くとnumbersは1つ以上の引数を取ることができるようになります。

def sum(*numbers)
  total = 0
  numbers.each { |num| total += num }
  total
end

puts sum(1, 2, 3)       # 6
puts sum(4, 5, 6, 7)    # 22

残余引数と似ていますが、残余引数は、メソッドの引数リストの中で、最後の引数にのみ指定することができる機能です。残余引数は配列として引数をまとめて受け取ります。

def greet(greeting, *names)
  names.each { |name| puts "#{greeting}, #{name}!" }
end

greet("Hello", "Alice", "Bob")    # Hello, Alice!  Hello, Bob!
greet("Hi")                       # Hi!

上記の例では、greetメソッドが最初の引数としてgreetingを受け取り、残りの引数を配列としてまとめてnamesに格納しています。このように、残余引数を使用することで、可変長引数よりも柔軟な引数の処理が可能です。

つまり、

  • 可変長引数
    メソッドの引数リスト全体で使用でき、任意の数の引数を受け取ることができる
  • 残余引数
    最後の引数にのみ指定できて、その引数を配列としてまとめて受け取る

となります。

オプショナル引数の余談

オプショナル引数はデフォルト引数と呼ばれることもあるみたいです。
また、オプション引数というものもあるので混同してしまいそうです。
オプション引数は引数の数、名前に関係なくハッシュで引数を渡すことができる仕組みのことです。

「*」を2つ付けるとオプション引数になります。
オプション引数はハッシュとして受け取られます。

class Foo
  def self.bar(**message)
    p message
  end
end
 
Foo.bar(message: 'hello')
Foo.bar(message: 'hello', name: 'John')
---------------------------------------
{:message=>"hello"}
{:message=>"hello", :name=>"John"}

ブロック引数

ブロック引数については、procの記事で出てくるので詳しくはこちらをご覧ください。
引数でhoge(&block)をとるやつです。ただ、省略できちゃいます。
https://qiita.com/F_Yoko/items/4bcc6d1c9cf6ad7be16c

0
0
0

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
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?