Skip to content

Instantly share code, notes, and snippets.

@kyohei-shimada
Last active April 21, 2024 05:40
Show Gist options
  • Save kyohei-shimada/41863fd6eb6f3518f8cf to your computer and use it in GitHub Desktop.
Save kyohei-shimada/41863fd6eb6f3518f8cf to your computer and use it in GitHub Desktop.
rubyの引数のお話

引数

通常の引数

def hoge(a, b)
  [a, b]
end

hoge(1)         # ArgumentError: wrong number of arguments (1 for 2)
hoge(1, 2)      #=> [1, 2]
hoge(1, 2, 3)   # ArgumentError: wrong number of arguments (3 for 2)

# ちなみに
hoge 1, 2       #=> [1, 2]

デフォルト引数

# cがデフォルト引数
def hoge(a, b, c = 4)
  [a, b, c]
end

hoge(1)           # ArgumentError: wrong number of arguments (1 for 2..3)
hoge(1, 2)        #=> [1, 2, 4] # cにはデフォルト値が入る
hoge(1, 2, 3)     #=> [1, 2, 3] # デフォルト値が上書きされる
hoge(1, 2, 3, 4)  # ArgumentError: wrong number of arguments (4 for 2..3)

可変長引数

def hoge(a, b, **args)
  p a
  p b
  p args
end

hoge(1)           # ArgumentError: wrong number of arguments (1 for 2+)
hoge(1, 2)
#=> 1
# 2
# []
hoge(1, 2, 3)
#=> 1
# 2
# [3]
hoge(1, 2, 3, "文字列", {x: 1, y: 2})
# => 1
# 2
# [3, "文字列", {:x=>1, :y=>2}]

デフォルト引数と可変長引数

def hoge(a, b = 3, *args)
  #...
end

hoge(1)           # a = 1, b = 3, args = []
hoge(1, 2)        # a = 1, b = 2, args = []
hoge(1, 2, 3 , 4) # a = 1, b = 2, args = [3,4]

# これはできない(SymtaxError)
# 2引数目以降の引数をargsかbかどちらに入れればいいか判断がつかない
def hoge(a, *args, b = 3)
end

# でもこれはOK
def fuga(a, *args, b)
  p a
  p args
  p b
end

fuga(1) # ArgumentError: wrong number of arguments (1 for 2+)
fuga(1, 2)
#=> 1
# []
# 2
fuga(1, 2, 3)
#=> 1
# [2]
# 3

キーワード引数(基本)

def hoge(x: 1, y: 2)
  p x
  p y
end


hoge                 # x = 1,   y = 2
hoge(x: 100)         # x = 100, y = 2
hoge(y: 200)         # x = 1    y = 200
hoge(x: 100, y: 200) # x = 100, y = 200
# (ほぼ)ハッシュと等価なので順序の入れ替えは自由,指定されていないものにデフォルト値が入る
hoge(y: 200, x: 100) # x = 200, y = 100
# ただし指定していないキーワードは入れられない
hoge(x: 100, y: 200, z: 300) # ArgumentError: unknown keyword: z

キーワード引数(デフォルト値を記述しない場合)

def hoge(x: 1, y: 2, z:)
  #...
end

hoge(x: 10, y: 20, z: 30) # x = 10, y = 20, z = 30
# zを指定していないのでエラー
hoge(x: 10, y: 20)        # ArgumentError: missing keyword: z
# 逆に言うとzさえ指定しいればOK
hoge(z: 30)               # x = 1, y = 2, z = 30

オプション引数

def hoge(x: 1, y: 2, **opt)
  p x
  p y
  p opt
end

hoge
#=> 1
# 2
# {}

# キーワード引数で指定されたもの以外のkey, valueの組がhashでoptに入る
hoge(x: 10, z: 30, u: 40)
#=> 10
# 2
# {:z=>30, :u=>40}

ブロック引数

def hoge(&block)
  if block_given?
    block.call
  else
    "block is not given"
  end
end

hoge #=> "block is not given"
# ブロックを渡せる
hoge { "block!!" } #=> "block!!"

block = Proc.new { "block" }
hoge(&block) #=> "block"

# ちなみに&blockは省略可で省略した場合内部ではyieldで呼び出す
def hoge
  yield
end

hoge { "block" }

複雑な引数の取り方

def hoge(a, b, c = 3, *args, d, e:, f: "7", **opts, &block)
  p a
  p b
  p c
  p args
  p d
  p e
  p f
  p opts
  p block.call if block_given?
end

hoge # ArgumentError: missing keyword: e
hoge(
  1, # a
  2, # b
  3, # d
  e: "111" # e:
)
#=> 1
# 2
# 3
# []
# 3
# "111"
# "7"
# {}

hoge(10, 20, 30, 40, 50, 60, 70, a: "aaa", b: "bbb", c: "ccc", e: "eee") { "\(^o^)/" }
# a = 10
# b = 20
# c = 30
# args = [40, 50, 60]
# d = 70
# e = "eee"
# f = "7",
# opts = {
#   a: "aaa",
#   b: "bbb",
#   c: "ccc"
# }
# block.call => "\(^o^)/"

以下所感など

引数の順序

宣言は下記の順で書く(下記以外でも入れ替え可能なものもあるが,混乱を招くだけなので)

  1. 通常の引数1
  2. デフォルト引数
  3. 可変長引数(1つのみ)
  4. キーワード引数(デフォルト値の有無は順序には関係ない)
  5. オプション引数(1つのみ)
  6. ブロック引数(1つのみ)

どういう時にどの引数を使うか,どういうメソッドを期待するか

現実的には多くても3種類くらいにとどめておいたほうがいい. それ以上使う場合はクラス化やメソッドの細分化を検討したほうがいい(1メソッドにさせることは極力1roleに絞る)

  • 通常の引数
    • 必須パラメータで,メソッド名や引数名から何を渡せばよいか十分明確になっているもの.明確になっていないものはメソッドの見直しやキーワード引数の検討をしてみる
  • デフォルト引数
    • メソッドの実行に欠かせないパラメータではあるが,通常は広く認められたデフォルト値がありそれを使えば良いもの
  • 可変長引数
    • 呼び出し側で自然言語的に見やすくなるようなもの.(呼び出し側でArrayの1引数ではなく,あえて列挙したほうが視認性が上がるもの)
    • 下位のメソッドに配列としてデリゲートするもの
  • キーワード引数(デフォルト値つき)
    • 必須ではないが,指定することによってそのメソッドのパラメータをカスタマイズできるもの (link_to method: :putなど)
      • デフォルト引数よりかはよりカジュアルにオプショナルな設定を書いてよい
      • デフォルト引数の有無によってメソッド内の処理を大きく変えない
    • 呼び出し側でキーを指定したほうがわかりやすいと思われるものや自然言語として読みやすくなるもの
  • キーワード引数(デフォルト値なし)
    • 通常のパラメータよりも明示的に名称を指定したほうがわかりやすい場合
    • より自然言語っぽくなる場合
  • オプション引数  * 下位のメソッドにハッシュとしてデリゲートするもの
  • ブロック引数
    • blockを渡すときは明示的に書いています(blockという名前にとらわれず役割に適した変数名が付けられる && #callのメソッド名がより明示的なので)

自然言語的に美しくなることが善なのか?などはプロジェクトや周辺ライブラリの兼ね合いになるので, 一概には言えないが概ねこんな感じ. また下位にデリゲートしなければならないようなハッシュは巨大化している可能性があるので設計を見なおしたほうがよい場合が多いかもしれない

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment