メソッド¶
同じメッセージの重複を避けるために、変数を使用する代わりに、メソッドを定義して複数回呼び出すことができます。
メソッドの定義は、キーワードdef
とそれに続くメソッド名で示されます。キーワードend
までのすべての式は、メソッド本体の一部です。
def say_hello
puts "Hello Penny!"
end
say_hello
say_hello
say_hello() # syntactically equivalent method call with parentheses
ヒント
メソッド呼び出しは、名前の後の括弧で明確に示されますが、省略することもできます。たとえば、say_hello
もローカル変数である場合など、あいまいさを解消するためだけに必要になります。
引数¶
異なる人に挨拶したいけれど、すべて同じようにしたい場合はどうすればよいでしょうか。個別のメッセージを記述する代わりに、パラメータを介してカスタマイズできるメソッドを定義できます。パラメータは、メソッド本体内のローカル変数のようになります。パラメータは、メソッド名の後の括弧内に宣言されます。メソッドを呼び出すときは、メソッドのパラメータの値としてマッピングされる引数を渡すことができます。
def say_hello(recipient)
puts "Hello #{recipient}!"
end
say_hello "World"
say_hello "Crystal"
ヒント
メソッド呼び出し時の引数は通常、括弧内に配置されますが、省略できることがよくあります。say_hello "World"
とsay_hello("World")
は構文的に同等です。
あいまいさを避けるために、一般的に括弧を使用することをお勧めします。ただし、式が自然言語のように読める場合は、省略されることがよくあります。
デフォルト引数¶
引数にはデフォルト値を割り当てることができます。引数がメソッド呼び出しで欠落している場合に使用されます。通常、引数は必須ですが、デフォルト値がある場合は省略できます。
def say_hello(recipient = "World")
puts "Hello #{recipient}!"
end
say_hello
say_hello "Crystal"
型制限¶
この例のメソッドは、recipient
がString
であることを期待しています。ただし、他の型も同様に機能します。たとえば、say_hello 6
を試してください。
これは、このメソッドにとっては必ずしも問題ではありません。他の型を使用しても有効なコードになります。ただし、意味的には、String
として名前を持つ人に挨拶したいと考えています。
型制限は、引数の許容型を制限します。引数名の後にコロンで区切られて指定します。
def say_hello(recipient : String)
puts "Hello #{recipient}!"
end
say_hello "World"
say_hello "Crystal"
# Now this expression doesn't compile:
# say_hello 6
これで、名前は数値やその他のデータ型にすることはできなくなりました。これは、名前として数値を持つ人に挨拶できないという意味ではありません。数値は文字列として表現される必要があります。たとえば、say_hello "6"
を試してください。
オーバーロード¶
引数の型を制限することは、位置によるオーバーロードに使用できます。メソッドがsay_hello(recipient)
のような制限のない引数を持つ場合、メソッドsay_hello
へのすべての呼び出しはそのメソッドに送信されます。ただし、オーバーロードを使用すると、異なる引数型の制限を持つ同じ名前の複数のメソッドが存在できます。各呼び出しは、最も適合するオーバーロードにルーティングされます。
# This methods greets *recipient*.
def say_hello(recipient : String)
puts "Hello #{recipient}!"
end
# This method greets *times* times.
def say_hello(times : Int32)
puts "Hello " * times
end
say_hello "World"
say_hello 3
オーバーロードは、型制限だけで定義されるわけではありません。名前付き引数と同様に、引数の数も関連する特性です。
値を返す¶
メソッドは値を返し、メソッド呼び出しの値になります。デフォルトでは、メソッドの最後の式の値です。
def adds_2(n : Int32)
n + 2
end
puts adds_2 40
メソッドは、return
ステートメントを使用して、その本体の任意の場所で返すことができます。return
に渡された引数が、メソッドの戻り値になります。引数がない場合は、nil
になります。
次の例は、明示的および暗黙的なreturn
の使用を示しています。
# This method returns:
# - the same number if it's even,
# - the number multiplied by 2 if it's odd.
def build_even_number(n : Int32)
return n if n.even?
n * 2
end
puts build_even_number 7
puts build_even_number 28
戻り型¶
Int32
値を返すことを期待するメソッドを定義し始めますが、誤ってString
を返します。
def life_universe_and_everything
"Fortytwo"
end
puts life_universe_and_everything + 1 # Error: no overload matches 'String#+' with type Int32
コンパイラにメソッドがInt32
を返すことを期待しているとは伝えていないため、コンパイラができる最善のことは、String#+
メソッドが引数としてInt32
値を取らないことを伝えることです(つまり、コンパイラは値を使用する瞬間を指していますが、バグの根本原因であるメソッドの戻り型を指していません)。
型情報を使用すると、エラーメッセージをより正確にすることができます。そのため、例をもう一度試してみますが、今回は型を指定します。
def life_universe_and_everything : Int32
"Fortytwo"
end
puts life_universe_and_everything + 1 # Error: method top-level life_universe_and_everything must return Int32 but it is returning String
これで、コンパイラは問題が発生した場所を正確に示すことができます。ご覧のとおり、型情報を提供することは、コンパイル時にエラーを見つけるのに非常に役立ちます。