BOO入門 > 函數 (上一章:運算子 下一章:類別)


定義 定義 函數:用來執行特定任務的程式碼,包含了一個或多個程式區塊。

內建函數 編輯

前面幾個章節已經提過也使用過了部份函數:range()、print() 和 join()。這些都是 Boo 所內建的函數,這裡列出所有 Boo 提供的內建函數:

名稱 描述 範例
print 將物件輸出到標準輸出。這與 System.Console.WriteLine 相同。 print("hey")
gets 從標準輸入取得字串,與 System.Console.ReadLine() 相同。 input = gets()
prompt 詢問使用者特定問題並從標準輸入取得字串。 input = prompt("How are you? ")
join 遍訪 IEnumerable 物件並將所有元素放到一個字串裡。
join([1, 2, 3, 4, 5]) == "1 2 3 4 5"
map 對 IEnumerable 物件裡的全部元素執行同一函數,然後將結果放到一個新的 IEnumerable 物件之後回傳。
for i in map([1, 2, 3, 4, 5], { x | return x*x } ): print i
(譯註,例子與原文不同,這裡使用 closure (類似 C# 的 anonymous method),並加上迴圈印出結果。 )
array 用來建立一個空陣列或是將 IEnumerable 和 ICollection 物件轉換為陣列。 array(int, [1, 2, 3, 4, 5]) == (1, 2, 3, 4, 5)
matrix 建立多維陣列。請參考 Multidimensional Arrays 。(譯註:原文找不到此節。) matrix(int, 2, 2)
iterator 從物件取得一個 IEnumerable List(iterator('abcde')) == ['a', 'b', 'c', 'd', 'e']
shellp 啟始一個程序,並回傳 Process 物件。 process = shellp("MyProgram.exe", "")
shell 執行應用程式,並將應用程式輸出到標準輸出的結果放到字串裡面回傳。 input = shell("MyProgram.exe", "") // 譯註:原文為 "echo hi there" ,經測試,不管是在 Windows 或 Linux 都無法執行
shellm 在新的 AppDomain 裡執行特定 managed 應用程式,同樣,會把應用程式輸出到標準輸出的結果放到字串裡面回傳。 input = shellm("MyProgram.exe", (,))
enumerate 從 IEnumerator 物件建立新的 IEnumerator,但是會變成類似 (index, value) 的形式。 array( enumerate( range(5,8) ) ) == ((0, 5), (1, 6), (2, 7)) (譯註:原文用 List,這裡改用 array,因為用 List 會傳回 object,不容易看出結果。)
range 回傳包含整數串列的 IEnumerable 物件。 List(range(5)) == [0, 1, 2, 3, 4]
reversed 回傳 IEnumerable,但裡面成員的順序會變成相反。 List(reverse(range(5))) == [4, 3, 2, 1, 0]
zip 會回傳一個經過混搭多個 IEnumerables 物件的 IEnumerable 物件。 array(zip([1, 2, 3], [4, 5, 6])) == ((1, 4), (2, 5), (3, 6)) (譯註:原文為 List([1,2,3],[4,5,6]),應為 zip 才對,此處已修正) 或看這個例子:array(zip(['a','b','c'],[4,5,6],['aa','bb','cc']))==(('a', 4, 'aa'), ('b', 5, 'bb'), ('c', 6, 'cc'))
cat 串接多個 IEnumerable 物件。 List(cat(range(3), range(3, 6)) == [0, 1, 2, 3, 4, 5]

這些函數你都應該要知道,因為他們非常有用,能讓寫程式變得更簡單。

定義自己的函數 編輯

定義自己的函數非常簡單:

// 宣告函數
def Hello():
    return "Hello, World!"

print Hello()

輸出結果

Hello, World!

如果你不懂的話,下面會一步步地解說:

  1. def Hello():
    • def 表示你要開始宣告一個函數,def 表示 define。
    • Hello 是函數的名稱。你可以取任何你想要的名字,但這名字裡不能有空白,也不能以數字開頭。
    • () 表示函數要使用的引數。這裡不使用任何引數,所以裡面是空的。
  2. return "Hello, World!"
    • return 是個關鍵字,這讓函數知道該回傳些什麼給呼叫這個函數的地方。
    • "Hello, World!" 是個字串,也就是 return 要回傳的東西。
  3. print Hello()
    • print ,我們之前提過了,就是印出變數的內容。
    • Hello() 呼叫 Hello 函數,並且不帶引數。

如同變數一樣,函數回傳值的型別也會被自動推斷並決定。所以,

def Hello():
    return "Hello, World!"

Hello() 將永遠回傳字串,所以 Boo 將會推斷字串是他的回傳型別。也就是跟下面的程式碼一樣:

def Hello() as string:
    return "Hello, World!"
  建議 如果程式的語意不夠清楚的話,幫函數加上回傳型別的宣告。

如果 Boo 無法推斷回傳型別,它會假設為 object。如果沒有回傳值,那麼回傳型別會是 void,void 表示不回傳值。要定義一個無回傳值的函數,就不要在函數裡面使用 return,或就寫 return 就好。如果函數裡面有好幾個 return,而回傳值的型別又不同的時候,回傳型別將會是最接近的原型,通常是 object,但並非絕對。

譯註:"回傳型別將會是最接近的原型"的意思是,如果回傳的值剛好都繼承同一個父類別時,回傳型別將會被推斷為它們的父類別。但因為 .Net 裡面所有型別都兼容於 object,所以才說,"通常是 object"。另外,說兼容的原因是,像 int、bool, decimal 等等其實都是 struct,並不繼承 object。

引數 編輯

  定義 引數:允許相同順序的命令去操作不同的資料,而無須重新指定指令。

引數非常有用,這允許函數可以依據輸入來做出不同的事情。

// 引數範例
def Hello(name as string):
    return "Hello, ${name}!"

print Hello("Monkey")

輸出結果

Hello, Monkey!

這裡同樣提供步驟的說明:

  1. def Hello(name as string):
    • def 表示開始宣告函數。
    • Hello 是函數的名稱。你可以用任何你可以想到的名字,但是不能有空白,也不能以數字開頭。
    • (name as string) 這表示函數要使用的引數。這個函數將會使用一個引數:name。當呼叫此函數時,name 必須是個字串,否則你會得到一個編譯錯誤 - "The best overload for the method Hello is not compatible with the argument list '(The,Types, of, The, Parameters, Entered)'."
  2. return "Hello, ${name}!"
    • return 是個關鍵字,表示離開函數,並且可以傳回一個值給呼叫者(當然也可以不傳回值)。
    • "Hello, ${name}!" 使用了字串插值將 name 的值直接放到字串裡面去。
  3. print Hello("Monkey")
    • print 正如之前提到的,就是將物件印出的 macro。
    • Hello("Monkey") 將 ("Monkey") 當作引數傳入並呼叫 Hello 函數。

函數的多載 編輯

  定義 Overloading:同樣的名稱卻有不同的含意,這在語意上可以區別出來。舉例來說,有同樣名稱的兩個函數,編譯器可以根據函數引數的個數、型別或是回傳的型別來區別出你所要呼要的函數為何。

當函數被以不同引數宣告了數次時,函數多載就發生了。

// overloading example
def Hello():
    return "Hello, World!"

def Hello(name as string):
    return "Hello, ${name}!"

def Hello(num as int):
    return "Hello, Number ${num}!"

def Hello(name as string, other as string):
    return "Hello, ${name} and ${other}!"

print Hello()
print Hello("Monkey")
print Hello(2)
print Hello("Cat", "Dog")

輸出結果

Hello, World!
Hello, Monkey!
Hello, Number 2!
Hello, Cat and Dog!

不定個數的引數 編輯

要傳遞不定數目的引數時,可以這麼作:

// 不定引數的範例
def Test(*args as (object)):
    return args.Length

print Test("hey", "there")
print Test(1, 2, 3, 4, 5)
print Test("test")

a = (5, 8, 1)
print Test(*a)

輸出結果

2
5
1
3

在函數對引數的宣告加上 * 是讓函數知道引數的數目將不固定,如果要直接把容器變數當作引數傳遞時,得在呼叫函數時加上 *,以表示要把指定容器變數裡的元素當作引數傳遞到函數裡面去。以上面的例子來說,也就是把 a 裡面的三個元素當作引數傳遞到函數裡面去。 如果有一定需要(也就是必要)的引數的話,請宣告在不定引數的前面,可千萬別放到不定引數後面去了。

練習 編輯

  1. 寫個函數,讓它能在傳入值為偶數時,印些好的字眼﹔在傳入值為奇數時,印些不好的字眼。