遞迴函式:
(1) 遞迴函式的基本使用法:
遞迴函式就是在回傳變數時呼叫其本身。如下例:
public int get_q(int N_, int p_) //這就是我們要討論的遞迴函式
{
if (N_ % p_ != 0) { return get_q(N_ - 1, p_); } //遞迴函式在回傳變數時呼叫其本身
else {return N_/p_;}
}
public Form1()
{ InitializeComponent();}
private void Form1_Load(object sender, EventArgs e)
{
int q=get_q(60,7);
textBox1.Text = q.ToString();
}
※ 註一:上述的遞迴函式之寫法,其實用for迴圈就可以取代,算是「大才小用」。遞迴函式真正的用途應是用在函式之執行路徑遇到「分岔點」的時候。因無法預先知曉分岔點的數量、以及分岔過後的函式路徑,故也不知道到底要使用幾個for迴圈,就必須靠遞迴函式解決。函式之執行路徑的問題牽涉到演算法的撰寫,故不在此探討。
(2) 遞迴函式的次數限制、如何解除該限制:
(2-1) 在C#應用程式中,函式的遞迴數目有其限制。也就是說,若函式執行超過N次,C#會懷疑該函式可能有無限遞迴的情況而自動跳出。這個N之大小,與函式所使用到的記憶體大小、其運算量、作業系統設定…等等有關。如以下範例:
int N = 0;
int N_max = 12000; //此變數N_max為遞迴函式所能執行的最大次數
public int add(int a)
{
N++;
if (N >= N_max){ return a; }
else { return add(a + 1); }
}
public Form1()
{ InitializeComponent(); }
private void Form1_Load(object sender, EventArgs e)
{
textBox1.Text = add(1).ToString();
}
經筆者以自家電腦測試,該程式可以順利執行,執行之後textBox中顯示:12000,這表示該遞迴函式add確實順利地完成了12000次的遞迴。但若是把int N_max = 12000;改成int N_max = 13000;,程式執行時便會出錯,錯誤訊息提示為:請確定沒有無限迴圈或無限遞迴的情況。依程式碼來看,程式只是要求add函式遞迴13000次,怎麼可能會出現無限遞迴呢?此乃C#為此函式所配置的記憶空間不足,無法執行13000次,導致C#誤認為出現無限遞迴。而經上述測試可知,C#為add函式所配置的記憶空間所允許add函式的最大遞迴次數N,是介於12000和13000之間。
(2-2) 欲讓遞迴函式突破該記憶體空間的限制、而能執行更多次遞迴,須建立一個新的執行緒、一個自訂的類別,並為該執行緒配置更多的記憶空間、以該新類別儲存函式與參數與回傳值,然後用此執行緒來執行該遞迴函式。寫法改成:
(註:要使用Thread物件(執行緒),須在程式開頭寫下:using System.Threading; )
class add_function_environment //自訂的類別,也就是「函式環境」
{
// par set
public int a; //函式add所用的參數
// return value
public int result; //函式add的回傳變數
public int N = 0, N_max = 13000; //函式add所用的全域變數
public add_function_environment(int a_) { a = a_; }
public void add(object obj) //遞迴函式add,已成為函式環境中的成員函式
{
N++;
if (N >= N_max) { result = a; return; }
else { a++; add(obj); }
}
}
public Form1()
{ InitializeComponent();}
private void Form1_Load(object sender, EventArgs e)
{
add_function_environment add_fe = new add_function_environment(1);
Thread th_for_add = new Thread(new ParameterizedThreadStart(add_fe.add), 2000000);
th_for_add.Start();
th_for_add.Join();
textBox1.Text = add_fe.result.ToString();
}
具體說明一一如下:
※ 自訂的類別add_function_environment是遞迴函式add的函式環境。一個函式的環境中,包含了該函式、及所有該函式執行時所需要的全域變數、參數、回傳變數…等。若函式為遞迴函式、或函式須對某變數進行作用後回傳該變數、或函式有超過一個回傳變數…時,函式環境就可派上用場。而原本的遞迴函式add已成為函式環境的成員函式,要注意的是,add函式所要使用的參數不可在參數列中定義,而是定義成函式環境的成員函式,參數列必須寫為object 自訂名稱,且add函式的回傳值必須是void,這些都是為了讓自定的執行緒能夠執行該遞迴函式。
※ 執行緒(Thread)的宣告方式之一為:
Thread 執行緒名稱 = new Thread(new ParameterizedThreadStart(函式), 堆疊大小);
其中,函式即是該執行緒所要執行的函式,其回傳變數型態必須為void,其參數列必須為object 自訂名稱,否則會出錯。而堆疊大小為一整數,表示系統分配給該執行緒的總記憶空間大小,其單位為Bytes,預設值通常為1MB(約1000000 Bytes)。堆疊大小之設定,端看程式設計者欲使遞迴函式遞迴多少次而定。
※ 執行緒物件的使用方式:
執行緒物件.Start():開始執行該執行緒。
執行緒物件.Join():開始執行該執行緒後,讓該執行緒與執行緒一起執行,並且使在該執行緒在執行完畢之前不得執行下一道指令。
※ 函式環境物件的使用方式:
宣告一函式環境物件,就是在宣告一函式的參數。如:
add_function_environment add_fe = new add_function_environment(1);
就是在宣告add函式的參數為1。
要取回函式的回傳變數,只需從函式環境物件中呼叫即可,如:
add_fe.result 就是add函式執行後的回傳值。
留言列表