鄭士康 國立台灣大學 電機工程學系/電信工程研究所/ 資訊網路與多媒體研究所 函式 鄭士康 國立台灣大學 電機工程學系/電信工程研究所/ 資訊網路與多媒體研究所
綱要 函式呼叫 傳值函式 傳址函式 out參數 陣列作為函式參數與傳回值 井字遊戲Tic-tac-toe函式版
函式 集中重複或類似的程式碼片段為一個函式 縮短簡化原程式,使程式較容易閱讀,也比較不會出錯 各函式的資料相互獨立,讓程式開發團隊成員可以同時各自撰寫偵錯不同函式,方便合作 下一章再介紹較複雜程式的規畫與函式導向的程式設計方法
綱要 函式呼叫 傳值函式 傳址函式 out參數 陣列作為函式參數與傳回值 井字遊戲Tic-tac-toe函式版
呼叫函式一例 Random rand = new Random(); int k = rand.Next() % 30 + 1; for (int i = 0; i < k; ++i) { for (int j = 0; j < 50; ++j) Console.Write("*"); } Console.WriteLine();
函式呼叫流程 class Program { static void Main(string[] args) int k = rand.Next() % 30 + 1; } public class Random { . . . public int Next() } }
CallingFunctions.Program.Main static void Main(string[] args) { Random rand = new Random(); int k = rand.Next() % 30 + 1; for (int i = 0; i < k; ++i) PrintALineOfStars(); }
CallingFunctions.Program. PrintALineOfStars static void PrintALineOfStars() { for (int j = 0; j < 50; ++j) Console.Write("*"); } Console.WriteLine();
函式呼叫流程 class Program { static void Main(string[] args) A(); . . . } static void A() B(); static void B()
偵錯器的進一步應用 “逐步執行”與”不進入函式”的差別 目前類別與函式顯示 監看式的應用 呼叫堆疊的應用 即時運算視窗的應用
有回傳值的函式宣告與呼叫 static int ARandomNumber() { Random rand = new Random(); int result = rand.Next(); return result; } . . . . . . int k = ARandomNumber() % 30 + 1;
產生多個亂數時的問題 int k1 = ARandomNumber(); int k2 = ARandomNumber();
函式名稱的商榷 動詞片語 名詞片語 宣告為void , 沒有回傳值 內容為執行一些動作 呼叫時可以呈現出一個祈使句的形式 例:PrintALineOfStars 名詞片語 描述傳回值的內涵 例:ARandomNumber
綱要 函式呼叫 傳值函式 傳址函式 out參數 陣列作為函式參數與傳回值 井字遊戲Tic-tac-toe函式版
數學函數 函數與自變數 sin(x), x = p/6, x = p/2 真實參數, 形式參數, 傳回值
數學函式呼叫流程 double y = Math.Sin( Math.PI/6 ); class Math { static double Sin( double x ) . . . return result; }
傳值呼叫及以值回傳 形式參數 呼叫函式記憶區 區域變數 真實參數 結果變數 傳回值 形式參數 被呼叫函式記憶區 區域變數 傳回值
水平拋體計算 老鷹抓著石頭水平飛行 老鷹放開石頭攻擊地面目標 在主控台螢幕上看到石頭落下 水平方向的移動距離為 x = v0 t 垂直落下距離為 y = g t2/2
虛擬碼 1 Screen[0,0]~Screen[NY-1,NX-1]設為空白 2 for(t=0; t<NT; ++t) { 2.1 Screen[y, x] = ' ' 2.2 x = HorizontalDistance(v0, t) 2.3 y = VerticalDistance(g, t) 2.4 Screen[y, x] = 'o' 2.5 重新顯示Screen // 動畫效果 }
函式虛擬碼 HorizontalDistance( v0, t ) 1 x = v0*t 2 return x
函式虛擬碼 VerticalDistance( g, t ) 1 y = 0.5*g*t*t 2 return y
PlottingTrajectory.Program.Main 片段 for (t = 0; t < NT; ++t) { Screen[y, x] = ' '; x = HorizontalDistance(v0, t); y = VerticalDistance(g, t); if (x >= NX || y >= NY) break; Screen[y, x] = 'o'; Console.Clear(); for (i = 0; i < NY; ++i) { for (j = 0; j < NX; ++j) { Console.Write(Screen[i, j]); } Console.WriteLine();
PlottingTrajectory.Program. HorizontalDistance static int HorizontalDistance(double v0, int t) { double x = v0 * t; return (int) x; }
PlottingTrajectory.Program. VerticalDistance static int VerticalDistance(double g, int t) { double y = 0.5 * g * t * t; return (int) y; }
綱要 函式呼叫 傳值函式 傳址函式 out參數 陣列作為函式參數與傳回值 井字遊戲Tic-tac-toe函式版
SwappingIntegers.Program 片段 int x = 3; int y = 5; Swap(x, y); Debug.Assert(x == 5 && y == 3); . . . static void Swap(int x, int y) { int temp = x; x = y; y = temp; }
正確版本片段 int x = 3; int y = 5; Swap(ref x, ref y); Debug.Assert(x == 5 && y == 3); . . . static void Swap(ref int x, ref int y) { int temp = x; x = y; y = temp; }
Euclid.Program.Main static void Main(string[] args) { Debug.Assert(GCD( 1, 5 ) == 1); Debug.Assert(GCD( 12, 18 ) == 6); Debug.Assert(GCD(17, 13) == 1); Console.Write("輸入第一個正整數: "); int x = Convert.ToInt32(Console.ReadLine()); Console.Write("輸入第二個正整數: "); int y = Convert.ToInt32(Console.ReadLine()); Console.WriteLine("兩數的最大公因數為{0}", GCD(x, y)); }
Euclid.Program.GCD static int GCD(int x, int y) { if (x < y) Swap(ref x, ref y); // x 較小時, 需交換x 與 y int remainder; while (y != 0) { remainder = x % y; x = y; y = remainder; } return x;
Euclid.Program.Swap static void Swap(ref int x, ref int y) { int temp = x; x = y; y = temp; }
綱要 函式呼叫 傳值函式 傳址函式 out參數 陣列作為函式參數與傳回值 井字遊戲Tic-tac-toe函式版
應用傳址參數傳回多個變數值 同時以函式的多個計算結果來改變真實參數內容 閱讀程式時,不易區分使用傳址參數取得函式計算結果與利用傳址參數改變真實參數的兩種情況,不利了解與偵錯 需設定真實變數初值 改用out參數較佳 不必設定真實變數初值
UsingOutArguments.Program.Main static void Main(string[] args) { int a = 5; int perimeter1 = 0; int area1 = 0; GetAreaAndPerimeterOfASquare( a, ref area1, ref perimeter1); Debug.Assert(area1 == 25 && perimeter1 == 20); int perimeter2; int area2; GetAreaAndPerimeterOfASquareUsingOut( a, out area2, out perimeter2); Debug.Assert(area2 == 25 && perimeter2 == 20); }
UsingOutArguments.Program. GetAreaAndPerimeterOfASquare static void GetAreaAndPerimeterOfASquare( int a, ref int area, ref int perimeter) { area = a * a; perimeter = 4 * a; }
UsingOutArguments.Program. GetAreaAndPerimeterOfASquareUsingOut static void GetAreaAndPerimeterOfASquareUsingOut( int a, out int area, out int perimeter) { area = a * a; perimeter = 4 * a; }
綱要 函式呼叫 傳值函式 傳址函式 out參數 陣列作為函式參數與傳回值 井字遊戲Tic-tac-toe函式版
一維陣列記憶配置 p[0] p p[1] p[2] p[3] p[4]
PassingArray.Program.Main片段 int[] p = { 1, 2, 3, 4, 5 }; int[] pCopy = p; PassArrayElementByValue(p[2]); PassArrayElementByReference(ref p[2]); PassArrayElementAsOutParameter(out p[2]); PassArrayByValue(p); Debug.Assert(p == pCopy); PassArrayByReference(ref p); Debug.Assert(p != pCopy); PassArrayAsOutParameter(out p);
陣列p和pCopy的參考指向相同位置 p pCopy
PassingArray.Program. PassArrayElementByValue static void PassArrayElementByValue(int x) { x *= 2; }
PassingArray.Program. PassArrayElementByReference static void PassArrayElementByReference(ref int x) { x *= 2; }
PassingArray.Program. PassArrayElementAsOutParameter static void PassArrayElementAsOutParameter(out int x) { x = 3; }
PassingArray.Program. PassArrayByValue static void PassArrayByValue(int[] a) { for (int i = 0; i < a.Length; ++i) a[i] *= 2; } a = new int[] { 7, 8, 9 };
PassingArray.Program. PassArrayByReference static void PassArrayByReference(ref int[] a) { for (int i = 0; i < a.Length; ++i) a[i] *= 2; } a = new int[] { 7, 8, 9 };
PassingArray.Program. PassArrayAsOutParameter static void PassArrayAsOutParameter(out int[] a) { a = new int[] { 1, 2, 3, 4, 5 }; }
使用重構修改Array2D 擷取方法 AverageOfEachRow 重新命名 table
ReturningArray.Program.Main片段 double[] individualAverage = AverageOfEachRow(score); double[] subjectAverage = AverageOfEachColumn(score); for (int i = 0; i < N_STUDENTS; ++i) { Console.WriteLine("{0}: 兩科平均成績 {1}", registerNumber[i], individualAverage[i]); } for (int j = 0; j < N_SUBJECTS; ++j) { Console.WriteLine("{0}: 全班平均成績 {1}", subject[j], subjectAverage[j]);
ReturningArray.Program. AverageOfEachRow static double[] AverageOfEachRow(int[,] table) { int nRows = table.GetUpperBound(0)+1; double[] average = new double[nRows]; int nColumns = table.GetUpperBound(1)+1; for (int i = 0; i < nRows ; i++) { int sum = 0; for (int j = 0; j < nColumns; j++) { sum += table[i, j]; } average[i] = (double)sum / nColumns; return average;
以Random物件為參數 static int ARandomNumber( Random rand ) { int result = rand.Next(); return result; } . . . . . . Random rand = new Random(); int k1 = ARandomNumber( rand ); int k2 = ARandomNumber( rand );
綱要 函式呼叫 傳值函式 傳址函式 out參數 陣列作為函式參數與傳回值 井字遊戲Tic-tac-toe函式版
判斷使用者或電腦獲勝虛擬碼 判斷使用者或電腦獲勝 Won( i0, j0, board ) 1 設使用者或電腦最新位置在i0, j0 2 檢查是否成列 if( board[i0, 0] == board[i0, 1] == board[i0, 2] ) return true 3 檢查是否成行 if( board[0, j0] == board[1, j0] == board[2, j0] ) return true 4 檢查主對角線是否均為'x' 或 'o' if( board[0, 0] == board[1, 1] == board[2, 2] == board[i0, j0] ) return true 5 檢查次對角線是否均為'x' 或 'o' if( board[0, 2] == board[1, 1] == board[2, 0] == board[i0, j0] ) 6 return false
亂數產生‘o’位置虛擬碼 虛擬碼: 亂數產生'o'位置 TakeAPosition( board, io, jo ) 0 | 1 | 2 ---+---+--- 3 | 4 | 5 6 | 7 | 8 1 do { 1.1 產生一個0到8的亂數k 1.2 io = k / 3 1.3 jo = k % 3 } while( board[io,jo] 不是空白 ) 2 board[io, jo] = 'o'
TicTacToe2.Program.Main片段 do { Display(board); Console.Write("輸入x位置的座標, 以逗點分隔: "); string[] input = new string[2]; input = Console.ReadLine().Split(','); int ix = Convert.ToInt16(input[0]); int jx = Convert.ToInt16(input[1]); board[ix, jx] = 'x'; userWin = Won(ix, jx, board); hasVacancies = HasVacancies(board); if (userWin || !hasVacancies) break;
TicTacToe2.Program.Main片段 TakeAPosition(rand, board, out io, out jo); computerWin = Won(io, jo, board); if (computerWin) break; hasVacancies = HasVacancies(board); } while (hasVacancies); Display(board); if (userWin) { Console.WriteLine("使用者獲勝"); } else if (computerWin) { Console.WriteLine("電腦獲勝"); } else { Console.WriteLine("平手");
TicTacToe2.Program.Won static bool Won(int i0, int j0, char[,] board) { if ((board[i0, 0] == board[i0, 1]) && (board[i0, 1] == board[i0, 2])) return true; if ((board[0, j0] == board[1, j0]) && (board[1, j0] == board[2, j0])) return true; if ((board[0, 0] == board[i0, j0]) && (board[1, 1] == board[i0, j0]) && (board[2, 2] == board[i0, j0])) return true; if ((board[0, 2] == board[i0, j0]) && (board[2, 0] == board[i0, j0])) return true; return false; }
TicTacToe2.Program.TakeAPosition static void TakeAPosition(Random rand, char[,] board, out int io, out int jo) { do { int k = rand.Next() % 9; io = k / 3; jo = k % 3; } while (board[io, jo] != ' '); board[io, jo] = 'o'; }
TicTacToe2.Program.Display static void Display(char[,] board) { Console.Clear(); Console.WriteLine(" 0 1 2 "); Console.WriteLine(" "); Console.WriteLine("0 {0} | {1} | {2} ", board[0, 0], board[0, 1], board[0, 2]); Console.WriteLine(" ---+---+---"); Console.WriteLine("1 {0} | {1} | {2} ", board[1, 0], board[1, 1], board[1, 2]); Console.WriteLine("2 {0} | {1} | {2} ", board[2, 0], board[2, 1], board[2, 2]); }
TicTacToe2.Program.HasVacancies static bool HasVacancies(char[,] board) { bool hasVacancies = false; for (int i = 0; i < 3; ++i) { for (int j = 0; j < 3; ++j) { if (board[i, j] == ' ') { hasVacancies = true; break; } return hasVacancies;