複雜的 Octave 程式可以經由定義函數的方式讓其架構更精簡,函數可以直接在命令列中定義或是寫在外部的檔案中,而其使用起來就像內建的函數一樣。

定義函數(Defining Functions)

要定義一個函數最簡單的方式就是

function name
  body
endfunction

這樣會定義一個名稱為 name 的函數。函數的命名規則與一般的變數相同,可以使用英文字母、數字或下底線,但開頭不可以是數字,函數的命名空間與變數是共用的。body 部份包含了一個或多個敘述,其指定了此函數所要執行的程式碼,這是定義函數時最重要的部份。例如:

function hello
  printf("Hello.\n");
endfunction

這樣會定義一個名稱為 hello 函數,呼叫此函數會使用 printf() 函數輸出字串 "Hello.\n"(關於 printf() 函數可以參考輸入與輸出)。當定義完一個函數之後,可以直接使用函數的名稱來呼叫此函數,例如:

hello

輸出為

Hello.

若要將變數傳入函數中,可以使用參數,使用方法為:

function name (arg-list)
  body
endfunction

其中 arg-list 是一個包含傳入參數的逗點分個序列,當函數被呼叫時,這些參數的名稱就會用來當作變數名稱,儲存函數呼叫時所傳入的值。arg-list 可以是空的,這個情況就跟上面沒有使用小括號的定義方式相同。

將上面的例子加入傳入參數:

function hello(msg)
  printf("Hello, %s.\n", msg);
endfunction

呼叫此函數

hello("world")

輸出為

Hello, world.

若要將函數中的運算結果傳回,則可使用指定運算子加入函數的傳回值:

function ret-var = name (arg-list)
  body
endfunction

ret-var 是一個變數名稱,其儲存的值會作為整個函數的傳回值,這個變數必須在函數結束之前定義,以便可以讓函數作為傳回值。

在函數中所使用的變數都是此函數的區域變數,包含 arg-listret-var 也都是區域變數,若要在函數中存取全域變數,可以參考全域變數。

下面的例子會計算一個向量中每個元素的平均值:

function retval = avg (v)
  retval = sum (v) / length (v);
endfunction

執行此函數:

avg([1, 3, 5, 7])

輸出為

ans =  4

若將上面的 avg() 函數改寫成這樣

function retval = avg (v)
  if (isvector (v))
    retval = sum (v) / length (v);
  endif
endfunction

此時若是呼叫此函數而傳入矩陣

val = avg([1, 2; 4, 6])

則會出現警告訊息:

warning: avg: some elements in list of return values are undefined
val = [](0x0)

這是因為當傳入矩陣時,if 判斷式中的程式碼將不會執行,因此導致 retval 沒有被定義,為了避免出現這種含糊不清的訊息,建議在定義函數時,將每一種可能發生的情況都考慮進去,並且確定傳回值都有被定義,在必要時也能輸出較詳細的錯誤訊息,例如這個 avg() 函數就可以改寫成這樣:

function retval = avg (v)
  retval = 0;
  if (isvector (v))
    retval = sum (v) / length (v);
  else
    error ("avg: expecting vector argument");
  endif
endfunction

這樣可以解決傳入參數型態不符合的問題,但是若在呼叫此函數時沒有傳入任何參數,則還是會出現錯誤。若沒有更詳細的錯誤檢查,Octave 所輸出的錯誤訊息常常會讓程式設計者很難追蹤錯誤所發生的位置,為了處理這類的問題,Octave 提供了一個特殊的內建變數 nargin,當函數被呼叫時,這個變數會自動被初始化,其會紀錄此函數在呼叫時所傳入的參數個數。上面的 avg() 函數可以更改成這樣:

function retval = avg (v)
  retval = 0;
  if (nargin != 1)
    usage ("avg (vector)");
  endif
  if (isvector (v))
    retval = sum (v) / length (v);
  else
    error ("avg: expecting vector argument");
  endif
endfunction

在 Octave 中若是在呼叫函數時傳入的參數個數超過函數所定義的個數,預設是不會產生錯誤的,但是會出現這樣的情況代表在程式中的某個地方一定有錯誤;若是傳入的變數比函數所宣告的個數少,在函數中使用那些沒有傳入的變數將會導致錯誤。為了避免以上兩種錯誤,建議在函數中加入對於這兩種情況的檢查,並針對各種錯誤輸出詳細的錯誤訊息。

nargin ()
nargin (fcn_name)

若在函數中呼叫 nargin() 函數,則會傳回呼叫此函數時所傳入的參數個數;若在最上層呼叫 nargin() 則會傳回執行 Octave 時所傳入的命令列參數個數。nargin(fcn_name) 函數可以查詢函數 fcn_name 可以接受的最大參數個數,若傳回 -1 則表示此函數可以接受各種不同的參數個數。

inputname (n)

在一個函數中呼叫 inputname(n) 函數會傳回呼叫此函數時所傳入的第 n 個參數。

val = silent_functions ()
old_val = silent_functions (new_val)

silent_functions() 函數可以查詢或設定是否要顯示函數中程式碼的執行輸出,若這個選項設定為 false,則 Octave 會顯示函數中沒有使用分號結尾的指令執行結果,預設是設為 false。例如:

function test_output
  x = 1
endfunction

執行 test_output() 函數

test_output

輸出為

x =  1

若是將 silent_functions() 設為 true

silent_functions(true)
test_output

則執行 test_output 時就不會輸出。