ここまで、メソッド定義の基本的な形を解説しました。基本的な形では引数の数は固定でしたが、可変長引数を取ることもできます。可変長引数を用いた例を、method05.rbに示しましょう。
def sum(*args) puts args.class args.inject(:+) end p sum(1, 2, 3, 4, 5) p sum(1) p sum
$ ruby method05.rb Array 15 Array 1 Array nil
ここでは、与えられた任意の数の引数の合計値を求めるメソッドsumを定義して使っています。可変長引数を取る場合、引数の前に「*(アスタリスク)」を付けます。コードの2行目の実行結果が示しているように、メソッドの中では、アスタリスクを付けた引数が配列として扱われます。3行目は少しトリッキーな書き方ですが、配列の内容をたたみ込むinjectメソッドを使って、各要素に対して「+メソッド」を適用し、合計値を求めています。
引数の数が多くなってくると、その順番を覚えるのが大変です。そのため、メソッドを作る側としても使う側としても、メンテナンスのための手間が増えてしまいます。そこで、キーワード引数を使うことで、使いやすくメンテナンスしやすいメソッドを用意できます。
1.9では、ハッシュを使ってキーワード引数のようなことを実現していました。その後、本当のキーワード引数が2.0で登場しましたが、その時点では引数のデフォルト値が必須となっていました。2.1ではさらなる改良が施され、デフォルト値を設定しなくてもキーワード引数を取れるようになりました。
では、method06.rbに例を示しましょう。
def basic_form(a: "a", b: ["b", "b", "b"], c: :c) puts "a(#{a.class}): #{a}" puts "b(#{b.class}): #{b}" puts "c(#{c.class}): #{c}" end def complex_form(a, *b, c: "c", d:) puts "a(#{a.class}): #{a}" puts "b(#{b.class}): #{b}" puts "c(#{c.class}): #{c}" puts "d(#{d.class}): #{d}" end basic_form(a: "meow", b: "oink", c: :bowwow) basic_form(a: "meow") complex_form("oink", "bowwow", d: "meow", c: "quack") complex_form
$ ruby method06.rb a(String): meow b(String): oink c(Symbol): bowwow a(String): meow b(Array): ["b", "b", "b"] c(Symbol): c a(String): oink b(Array): ["bowwow"] c(String): quack d(String): meow method06.rb:17:in `<main>': missing keyword: d (ArgumentError)
basic_formがキーワード引数の基本形です。キーワードに続けて:(コロン)を書き、その後にデフォルト値を続けます。メソッドの中では、キーワードを変数として使えます。15行目のように一部の引数を省略しても、デフォルト値が使われていることが実行結果から確認できます。
complex_formは、少し複雑な形です。通常の引数と可変長引数、キーワード引数が混在しています。この場合、通常の引数、可変長引数、キーワード引数、という順番を守る必要があります。全ての引数を埋めて呼び出した16行目の実行は成功していますが、17行目ではArgumentErrorという例外が出て、プログラムが終了しています。
「d」のようにキーワード引数のデフォルト値を省略した場合、その引数は必須の引数となります。17行目の実行では、必須の引数である「d」を設定していないために、「ArgumentError」つまり「引数のエラー」が例外として出現します。
eachメソッドなど、Rubyでは「ブロック」という仕組みを活用して、メソッドに「処理そのものを引数として渡す」というパターンが頻出します。ここでは、何気なく使ってきたブロックについて詳しく解説します。これまで使うばかりであったブロックを、メソッドの引数として利用する方法についても学びます。
では、メソッドの引数としてブロックを与え、それを利用する基本形を見てみましょう。block01.rbに例を示します。
def repeat(n) for i in 1..n puts "#{i} =>" yield end end repeat(3) { puts "pyonpyon" }
1 => pyonpyon 2 => pyonpyon 3 => pyonpyon
この例では、引数に指定した回数だけブロックとして与えた処理を繰り返す「repeat」というメソッドを定義しています。
ここで、4行目に注目してください。「yield」という見慣れない予約語があります。実は、このyieldこそがキモで、メソッドに与えたブロックを実行するという働きがあります。for文によって1から「n」まで1ずつ整数を取り出しながら「i」に代入し、その回数だけyieldが実行されるので、結果的にn回ブロックが実行されることになります。
では、より高度なブロックの使い方を学ぶために、指定した数の等差数列を生成するジェネレーターを作ってみましょう。等差数列というのは、ある初項から始めて、一定の公差を加えることで生成される数列です。
例えば、初項2で公差3の場合は、「2、5、8、11、14、17……」という数列になります。サンプルコードをblock02.rbに示します。
def arithmetic_sequence(init: 1, diff: 1, count: 10) current = init if block_given? count.times do yield(current) current += diff end else Array.new.tap do |a| count.times do a << current current += diff end end end end arithmetic_sequence(init: 2, diff: 3, count: 5) do |n| puts n end p arithmetic_sequence(init: 2, diff: 3, count: 5)
$ ruby block02.rb 2 5 8 11 14 [2, 5, 8, 11, 14]
まず、3行目に注目してください。「block_given?」メソッドを利用して、メソッドにブロックが与えられているかどうかを判定して、処理を分けています。もしブロックが与えられていれば4〜7行目が実行され、そうでなければ、9〜14行目が実行されます。
この例では、5行目でyieldに引数を与えています。yieldに与えた引数は、9行目のメソッドに与えたブロックの引数として利用されます。つまり、「arithmetic_sequence」メソッドで生成した等差数列のそれぞれの値がブロック引数nに格納され、メソッドを使う側が自由に利用できるということになります。
また、ブロックが与えられなかった場合は、配列を用意して、生成した数列を配列に順に格納して呼び出し元に返しています。
Copyright © ITmedia, Inc. All Rights Reserved.