方法¶
為了避免重複相同的訊息,我們可以定義一個方法並多次呼叫它,而不是使用變數。
方法定義以關鍵字 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
值作為引數(即編譯器指向我們使用該值的那一刻,而不是 bug 的根源:方法的返回值的類型)。
如果使用類型資訊,錯誤訊息可能會更準確,所以讓我們再次嘗試這個範例,但現在指定類型
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
現在編譯器可以準確地顯示問題的根源。正如我們所看到的,提供類型資訊對於在編譯時尋找錯誤非常有用。