跳至內容

方法

為了避免重複相同的訊息,我們可以定義一個方法並多次呼叫它,而不是使用變數。

方法定義以關鍵字 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

現在編譯器可以準確地顯示問題的根源。正如我們所看到的,提供類型資訊對於在編譯時尋找錯誤非常有用。