使用指令字串(Using Evaluation)

一般來說要執行 Octave 的指令可以由命令列視窗直接輸入,或讓 Octave 直接執行儲存在檔案中的程式,然而有時候會需要直行儲存在字串變數中的指令,這時候就可以使用 eval() 函數。

eval (try, catch)

eval(try, catch) 函數會將字串 try 的內容視為 Octave 的指令來執行,若執行失敗,則會再執行字串 catch 的內容。try 的內容是在目前的變數環境(Context)中執行的,因此所有的結果都會保留在目前的變數環境中。例如:

eval("a = acos(-1);");

這樣會在目前的變數環境中建立一個變數 a ,其值為 3.1416

若有錯誤發生時,就會執行 catch 的內容,例如:

eval ('error ("This is a bad example");', 'printf ("This error occurred:\n%s\n", lasterr ());');

輸出為

This error occurred:
This is a bad example

由函數名稱呼叫(Calling a Function by its Name)

feval() 函數會以包含函數名稱的字串呼叫指定的函數,這個特性可以讓使用者指定函數,此函數會呼叫第一個參數所指定的函數,並將第二個以後的參數傳入此函數。

下面這個範例是使用牛頓法尋找指定函數的解:

function result = newtroot (fname, x)

# usage: newtroot (fname, x)
#
#   fname : a string naming a function f(x).
#   x     : initial guess

  delta = tol = sqrt (eps);
  maxit = 200;
  fx = feval (fname, x);
  for i = 1:maxit
    if (abs (fx) < tol)
      result = x;
      return;
    else
      fx_new = feval (fname, x + delta);
      deriv = (fx_new - fx) / delta;
      x = x - fx / deriv;
      fx = fx_new;
    endif
  endfor

  result = x;

endfunction

這個範例主要的目的只是說明如何使用 feval() 函數而已,實際上的的函數應該還要加入一些安全檢查的機制,例如判斷 fname 所指定的函數是否真的存在等。

feval (name, ...)

feval(name, ...) 函數會執行名稱為 name 的函數,從第二個之後的參數都會被傳入此函數中,例如:

feval ("acos", -1)

這樣會執行 acos(-1),輸出為

ans =  3.1416

由於 Octave 中沒有類似像 C 語言的函數指標功能,因此要傳遞函數唯一的方式就是使用函數名稱,再配合 feval() 函數來執行。

另外有一個類似功能的函數 run(),此函數可以執行使用者的指令稿。

run (f)
run f
run(f) 函數會執行目前目錄的指令稿 f,若 f 包含其路徑,則 Octave 會先變換目前的目錄至該路徑再執行,執行完成後再返回原目錄。

在不同的變數環境中執行(Evaluation in a Different Context)

使用指令字串時,在指令中的變數值都是由儲存在 Octave 的符號表(symbol table)中,而在呼叫函數時 Octave 會將目前的符號表暫時儲存起來,另外產生一個新的提供呼叫的函數來使用,這個新的符號表會包含呼叫函數時所傳入的變數,另外再加上一些內建的變數,例如 nargin 等,任何在函數中的算式都是使用新的符號表。 有時候使用者會需要在呼叫函數時,在函數中取得或變更目前符號表中的變數,也就是類似 C 語言中的指標(pointer),這個時候可以使用 evalin()assignin() 函數。

evalin (context, try, catch)

evalin(context, try, catch) 函數與 eval() 函數類似,但可以透過 context 參數指定變數環境,此參數可以使用的選項有:

  • "caller":上一層的變數環境,也就是呼叫此函數的呼叫者。
  • "base":位於最上層的命令列變數環境。

下面我們以範例說明 evalin() 函數的使用方式,首先定義兩個函數 func1()func2(),並在最上層與 func1() 中都定義一個 test_var 變數:

test_var = "base";

function func1
 test_var = "from func1";
 func2();
endfunction

function func2
 evalin("base", "test_var");
 evalin("caller", "test_var");
endfunction

這時候若執行

func1()

輸出為

test_var = from base
test_var = from func1

func1() 呼叫 func2() 時,第一個 evalin() 函數的 context 參數是指定為 "base",因此會取得最上層的 test_var,而第二個 evalin() 函數的 context 參數則是設為 "caller",所以其取得的是函數呼叫者 func1() 函數之中的 test_var

assignin (context, varname, value)

assignin(context, varname, value) 函數可以將變數環境 context 中的變數 varname 指定為 value,context 可用的選項與 evalin() 函數相同。例如在自行定義的 func3() 函數中以 assignin() 函數在最上層的命令列變數環境中新增一個變數 new_var,並指定其值:

function func3
    assignin("base", "new_var", "new value");
endfunction

執行 func3() 函數之後,就會多了一個名為 new_var 的變數:

func3();
new_var

輸出為

new_var = new value