close

遞迴函式:

(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,是介於1200013000之間。

(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函式執行後的回傳值。

 

 

 

上一篇:匿名函式

下一篇:類別的宣告

按此前往C#完整教學目錄

arrow
arrow
    創作者介紹
    創作者 埃伯 的頭像
    埃伯

    程式語言教學

    埃伯 發表在 痞客邦 留言(0) 人氣()