Rubyの関数の引数の渡し方を比較してみた
Rubyの関数の引数の、1.普通に並べて渡す(?)、2.キーワード引数で渡す、3.ハッシュで渡すの3通りの渡し方を比較してみました。
動作環境
❯ sw_vers
ProductName: Mac OS X
ProductVersion: 10.15.7
BuildVersion: 19H2
❯ ruby -v
ruby 2.7.3p183 (2021-04-05 revision 6847ee089d) [x86_64-darwin19]
概要
まず、3通りの渡し方の特徴です。
方法 | メリット | デメリット |
---|---|---|
普通に並べて渡す | 渡す側が簡単 過不足があるときにエラーが発生する |
順番に依存する |
キーワード引数で渡す | 順番に依存しない 過不足があるときにエラーが発生する |
引数が多い場合、面倒 |
ハッシュで渡す | 受け取る側が楽 順番に依存しない |
過不足があるときにエラーが発生しない |
1. 普通に並べて渡す場合
minitestでエラーを検証する方法は、Minitestで例外クラスとエラーメッセージを検証する方法を参考にしました。
普通に並べて渡す場合は、引数の個数に過不足があると例外が発生します。また、引数にデフォルト値を設定することも出来ます。
require 'minitest/autorun'
def sum_normal(a, b)
a + b
end
def sum_normal_with_default(a, b = 100)
a + b
end
class NormalArgumentsTest < Minitest::Test
def test_デフォルト値がない場合
# 正常系
assert_equal 30, sum_normal(10, 20)
# 引数が足りない場合エラーになる
e = assert_raises ArgumentError do
sum_normal(10)
end
assert_equal 'wrong number of arguments (given 1, expected 2)', e.message
# 引数が多い場合エラーになる
e = assert_raises ArgumentError do
sum_normal(10, 20, 30)
end
assert_equal 'wrong number of arguments (given 3, expected 2)', e.message
end
def test_デフォルト値がある場合
# 渡さなかった場合はデフォルト値が使われる
assert_equal 110, sum_normal_with_default(10)
end
end
この書き方のメリットは、簡単に書けることと、過不足があるときに例外が発生することです。デメリットは、引数が複数ある場合に、渡す順番に気をつけないといけないことです。
2. キーワード引数で渡す場合
キーワード引数の場合も、普通の渡し方と同様に過不足があると例外が発生したり、デフォルト値を設定することが出来ます。
require 'minitest/autorun'
def sum_keyword(a:, b:)
a + b
end
def sum_keyword_with_default(a:, b: 100)
a + b
end
class KeywordArgumentsTest < Minitest::Test
def test_デフォルト値がない場合
# 正常系
assert_equal 30, sum_keyword(a: 10, b: 20)
# 順番が逆でもOK
assert_equal 30, sum_keyword(b: 20, a: 10)
# 引数が足りない場合エラーになる
e = assert_raises ArgumentError do
sum_keyword(a: 10)
end
assert_equal 'missing keyword: :b', e.message
# 余分な引数が入った場合エラーになる
e = assert_raises ArgumentError do
sum_keyword(a: 10, b: 20, c: 30)
end
assert_equal 'unknown keyword: :c', e.message
end
def test_デフォルト値がある場合
# 渡さなかった場合はデフォルト値が使われる
assert_equal 110, sum_keyword_with_default(a: 10)
end
end
キーワード引数を使うことのメリットは、渡す側が順番に気をつけなくても良いことです。デメリットは、引数の個数が多かったり、名前が長かったりするときに、渡す側がちょっと面倒になることです。
3. ハッシュで渡す場合
ハッシュで渡す場合は、引数に過不足があっても例外は発生しません。デフォルト値を設定したい場合は、デフォルトのハッシュを作って、それとマージしてあげれば良いです。
require 'minitest/autorun'
def sum_hash(hash)
hash[:a] + hash[:b]
end
def sum_hash_with_default(hash)
default_hash = { b: 100 }
hash = default_hash.merge(hash)
hash[:a] + hash[:b]
end
class HashArgumentsTest < Minitest::Test
def test_デフォルト値がない場合
# 正常系
assert_equal 30, sum_hash({ a: 10, b: 20 })
# 順番は気にしなくてOK
assert_equal 30, sum_hash({ b: 20, a: 10 })
# 余分な値が入ってもエラーにならない
assert_equal 30, sum_hash({ a: 10, b: 20, c: 30 })
# 不足していても(渡したときは)エラーにならない
e = assert_raises TypeError do
assert_equal 10, sum_hash({ a: 10 })
end
assert_equal 'nil can\'t be coerced into Integer', e.message
end
def test_デフォルト値がある場合
# 渡さなかった場合はデフォルト値が使われる
assert_equal 110, sum_hash_with_default({ a: 10 })
end
end
ハッシュで渡すことのメリットは、キーワード引数と同様に順番を気にしなくて良いことです。また、受け取る側は引数にハッシュを1個書くだけなので楽です。デメリットは、渡す側が何を渡せばいいのか分かりにくかったり、受け取る側がちゃんと値が渡されてくるかどうか分からないことです。
この弱点は、以下のようにしてハッシュを検証するとカバー出来そうです。
def func(hash)
validate(hash)
# 処理
end
def validate(hash)
raise ArgumentError if hash[:key1].nil?
raise ArgumentError if hash[:key2].nil?
end
まとめ
基本的にはキーワード引数を使えばいいのではないかと思います。長くなってしまう場合は、ハッシュで渡して検証する方法を使うことも考えようと思います。
状況 | どれを使うか |
---|---|
大体の状況 | キーワード引数 |
引数の個数が1個か2個で、増えなさそうな場合 | 普通の渡し方 |
引数がたくさんある場合 | ハッシュで渡して、検証メソッドを実装する |
可変長引数(*args)や最後のハッシュ(**hash)というのもあるようなので、頭の片隅に置いておきたいと思います。
参考
-
Rubyのメソッドの引数受け渡しまとめ
- 全体像を把握するのに役に立ちました。
-
Rubyのパラメータと引数の対応付けを理解する(前編)
- 色々な種類を組みあわせると複雑になるみたいです(そっと目を背けました。)
-
サンプルコードでわかる!Ruby 2.7の主な新機能と変更点 Part 2 - キーワード引数に関する仕様変更
- Ruby3から、ハッシュとキーワード引数の相互変換がされなくなったようです。
Discussion