跳轉到內容

Futurebasic/語言/參考/local fn

來自華夏公益教科書,開放的書籍,為了一個開放的世界

[CLEAR] LOCAL FN functionName [(arg1 [,arg2 ...])]    [statementBlock] END FN [= expr]

該語句標誌著 FB 區域性函式的開始。區域性函式的結束由 END FN 語句標記。區域性函式是語句的命名集合,可以透過引用函式的名稱來訪問和執行(見 FN <userFunction> 語句)。區域性函式中引用的所有變數和陣列(除了顯式宣告為“全域性”的變數和陣列)都對該函式是區域性的,這意味著它們在函式外部沒有任何影響;在函式外部出現的任何同名變數實際上是不同的變數,並在記憶體中佔用不同的位置,而不是函式的區域性變數。(當陣列被列為函式的正式引數時,此規則會出現例外;請參閱以下內容。)當您的程式“呼叫”(執行)區域性函式時,您可以透過引數列表(也稱為引數列表)將資料傳遞給函式,並且可以透過返回值接收來自函式的值。區域性函式允許您封裝複雜的程式設計任務;它們是一種基本且非常強大的程式設計結構。

除了標記函式的開始,LOCAL FN 語句還宣告函式的名稱、返回值的資料型別(如果有)以及輸入引數的數量和型別(如果有)。區域性函式可以放置在程式中的任何位置,除了另一個區域性函式內部;您也不應將區域性函式放置在“塊”結構中,例如 LONG IF...END IF 等。statementBlock 中的語句可以包含任何內容,但以下內容除外

  • 一個 LOCAL 語句;
  • 一個 ENTERPROC...EXITPROC 塊;
  • 另一個區域性函式。

新增 CLEAR 關鍵字會導致每次呼叫函式時,所有函式的區域性變數和陣列(除了引數變數 arg1arg2 等)都被初始化為零、空字串或空記錄。(您可以透過使用帶有 CLEAR 關鍵字的 LOCAL 語句來實現相同的效果;有關更多資訊,請參閱 LOCAL 語句。)

functionName 必須是唯一的;也就是說,它必須與程式中任何其他 LOCAL FNENTERPROC FNLONG FNDEF FN USINGDEF FN <expr> 語句中使用的名稱不同。如果函式要返回值,則應在 functionName 末尾包含適當的型別識別符號字尾來指定返回值的資料型別。例如,要返回字串值的區域性函式可以宣告如下

  LOCAL FN FullName$(idNum&)

函式返回值的預設資料型別為“長整數”;如果函式要返回長整數值,您可以包含或省略“&”型別識別符號字尾。如果函式不返回值,則不應將型別識別符號字尾追加到 functionName

為了執行 statementBlock 中的語句,您的程式必須使用 FN <userFunction> 語句“呼叫”該函式。FN <userFunction> 語句可以出現在程式中的任何位置,只要它呼叫的函式是在 FN <userFunction> 語句上方某個位置定義的(使用 LOCAL FN 語句)或原型化的(使用 DEF FN <prototype> 語句)。此限制是必要的,以便允許您的程式編譯;但是,程式語句的實際執行順序不受您放置 LOCAL FN 的位置影響。

函式引數 每個引數 arg1arg2 等可以具有以下任何形式

LOCAL FN 語句中的引數稱為函式的“形式引數”。它們不能是全域性變數。您不應在 DIM 語句中宣告形式引數變數;它們是由 LOCAL FN 語句隱式宣告的。

當您的程式呼叫函式時,在 FN <userFunction> 語句中傳遞給它的引數稱為“實際引數”。它們在數量上必須與函式的形式引數匹配,並且它們必須是“相容型別”(有關更多資訊,請參閱 FN <userFunction>)。每次呼叫函式時,都會按以下方式為其形式引數賦值

  • 如果形式引數是 simpleVar,則實際引數的值將被複制到 simpleVar 中。
  • 如果形式引數的形式為 ptrVar AS POINTER [TO someType],則實際引數應為記錄變數或長整型表示式。在第一種情況下,記錄的地址將被複制到 ptrVar 中;在第二種情況下,長整型的值將被複制到 ptrVar 中。
  • 如果形式引數的形式為 @longVar&@ptrVar AS POINTER [TO someType],則實際引數必須是變數(可能是記錄變數),或者是由“=”字首的長整型表示式。在第一種情況下,變數的地址將被複制到 longVar&ptrVar 中。在第二種情況下,長整型表示式的值將被複制到 longVar&ptrVar 中。
  • 如果形式引數是 array(dim1 [,dim2 ...]),則實際引數必須是相同型別陣列的基元素,該陣列具有相同的維數。基元素是所有下標都設定為零的元素。然後,整個陣列對區域性函式是可訪問的,並且(重要的是!)對陣列元素在函式內進行的任何更改都將在函式退出後持續存在。

如果區域性函式沒有引數,則應省略 functionName 後的括號

.

傳遞未知大小的陣列 有時,編寫一個在引數列表中傳遞的陣列上執行的區域性函式很有用,而事先不知道傳遞陣列的大小。例如,假設您希望編寫一個對長整型陣列元素進行排序的函式,並且您希望它在傳遞陣列的宣告大小無關緊要的情況下都能工作。

當您將陣列宣告為形式引數時,FB 會忽略 LOCAL FN 語句中陣列第一個宣告維的值。例如,假設我們有一個這樣定義的函式

LOCAL FN SetElements(anArray&(1,7), max&)   '將陣列中的每個元素設定為 1492:   FOR i& = 0 TO max&     FOR j = 0 TO 7       anArray&(i&,j) = 1492     NEXT   NEXT END FN

當我們將長整型陣列傳遞給 FN SetElements 時,傳遞的陣列可以具有任何大小作為其第一個宣告的維,只要它具有宣告為 7 的第二個維即可。例如

DIM arrayOne&(1250,7), arrayTwo&(465,7) FN SetElements(arrayOne&(0,0), 1250) FN SetElements(arrayTwo&(0,0), 465)

在函式內部,我們可以安全地運算元組中的元素,只要我們使用的下標不超過傳遞的實際陣列的宣告維即可。因此,在 FN SetElements 中,我們可以將 anArray& 中的第一個下標設定為遠遠大於 1 的值,即使 anArray& 是用維數 (1,7) “宣告”的。

注意:您在引數陣列的第二個、第三個等維數方面沒有相同的自由度。如果陣列是多維的,則第二個和後續維數必須在“形式”陣列引數(在 LOCAL FN 語句中)和宣告實際傳遞陣列的外部 DIM 語句中宣告為相同的值。

返回值 如果您在 END FN 語句中指定了 expr,則函式將“返回”expr 的值。這可以是與 functionName 中出現的型別識別符號字尾(如果有)相容的任何表示式。當您的函式“返回值”時,這意味著您可以將函式(使用 FN <userFunction>)作為字串或數字表達式的一部分引用,並且函式的返回值將被替換為表示式。例如

   maxPuppets = 6 * FN storeCount%(x)

在這裡,如果 FN storeCount%(x) 返回值為 7,則值 42 將被分配給 maxPuppets

區域性變數的壽命 函式的區域性變數的記憶體空間在呼叫函式時被保留。此記憶體將在執行 END FN 語句後釋放。因此,您不應該在函式執行完畢後引用區域性變數的地址;特別是,您不應該將區域性變數的地址傳遞迴呼叫該函式的例程。例如

'不要這樣做! LOCAL FN myFunction&(x,y,z)   DIM r#   r# = SQR(x*x + y*y + z*z) END FN = @r#

   rAddr& = FN myFunction&(x,y,z)

執行完上述操作後,rAddr& 指向不再保留的記憶體區域(r# 的舊地址),並且不應使用。

另一方面,將區域性變數的地址傳遞到另一個區域性函式是可以的。這是因為第一個區域性函式在呼叫第二個區域性函式時尚未完成執行。因此,在第二個函式執行時,儲存第一個函式區域性變數的記憶體空間仍然完好無損地保留著。

'這是可以的: LOCAL FN FirstFn   DIM 255 myString$   '將區域性變數的地址傳遞到另一個 FN:   FN SecondFN(@myString$) END FN   : LOCAL FN SecondFn(strAddr&)   BLOCKMOVE @gString$, strAddr&, LEN(gString$)+1 END FN

遞迴函式 您可以同時執行多個函式,從某種意義上說,一個函式可以呼叫第二個函式,第二個函式可以呼叫第三個函式,依此類推。如果您設計函式呼叫,使得一個函式可以呼叫正在執行的函式,那麼您就有一個“遞迴函式”。遞迴函式最明顯的(但不是唯一的)示例是任何呼叫自身的函式。發生這種情況時,我們說函式的兩個(或更多)“例項”同時執行。

在 FB 中,每個正在執行的本地函式“例項”都維護著自己的私有區域性變數集,它們不會干擾該函式的任何其他正在執行的例項的區域性變數。遞迴呼叫函式非常類似於呼叫一個“不同的”函式,該函式恰好包含完全相同的程式行。

雖然遞迴函式乍一看可能是一個奇怪的概念,但它們完全可以接受,並且經常非常有用。例如,以下是一個簡短的程式,它列印給定輸入字串中包含的所有字元的排列;請注意,FNpermute_r 自身呼叫。在不使用遞迴函式的情況下編寫這樣的程式將非常困難。

'函式原型:DEF FN Permute(aString$) DEF FN permute_r(prefix$, suffix$)

INPUT "Enter a word: "; theWord$ FN Permute(theWord$) END

LOCAL FN Permute(aString$)    '列印 aString$ 中所有字母的排列    FN permute_r("", aString$) END FN

LOCAL FN permute_r(prefix$, suffix$)   '列印 prefix$+suffix$ 的所有排列   '這些排列以 prefix$ 開頭   LONG IF suffix$ = ""     PRINT prefix$   XELSE     FOR i = 1 to LEN(suffix$)       '將 suffix$ 的第 i 個字母移到 newprefix$:       newprefix$ = prefix$ + MID$(suffix$, i, 1)       newsuffix$ = LEFT$(suffix$,i-1) + MID$(suffix$,i+1)       '現在列印所有以       'newprefix$ 開頭的排列       FN permute_r(newprefix$, newsuffix$)     NEXT   END IF END FN

返回多個值 END FN 語句只能返回單個數字或字串表示式。但很多時候,讓一個本地函式能夠返回多個值非常有用。實現此目的的方法是透過函式的引數列表。如果您為函式提供了對某個外部變數或陣列地址的訪問許可權,那麼該函式就可以更改該地址處的內容,從而有效地修改該變數或陣列的值。

有三種方法可以將地址傳遞給您的函式

  • 如果您傳遞整個陣列(在正式引數列表中使用 array(dim1 [,dim2 ...] 語法),那麼您的函式隱式地可以訪問所傳遞陣列的地址。您在函式內部對陣列元素所做的任何更改實際上都是對外部陣列進行的,因此這些更改在函式退出後仍然存在。
  • 如果您在函式的正式引數列表中使用 @var 語法,並在呼叫函式時指定一個變數,那麼變數的地址將被複制到 var 中。然後,您的函式可以修改該地址處的內容。
  • 您可以顯式地將任何地址傳遞到長整數或 POINTER 正式引數中。

示例: CD Example: LOCALFN.BAS

另請參見

[編輯 | 編輯原始碼]

FN <userFunction>; LOCAL; @FN; DEF FN <prototype>

華夏公益教科書