使用變數(Using Variables)

所謂的變數(variables)就是資料命名以便隨後可以存取,在前面的教學中已經看過很多使用變數的範例了。Octave 中的變數名稱可以使用英文字母、數字或下底線組成,而開頭的第一個字元不能是數字,變數名稱的長度並沒有限制,以下這些都是正確的變數名稱:

  • x
  • x15
  • __foo_bar_baz__
  • fucnrdthsucngtagdjb

在 Octave 中以雙下底線開頭的變數名稱是專門用於 Octave 內部的變數,例如 __foo_bar_baz__,一般使用者在自行撰寫程式時,除非是要更改 Octave 內部的變數設定,否則應該要避免使用這樣的變數名稱,以免影響 Octave 內部的變數。

Octave 會將大寫與小寫的字母視為不同的變數名稱,例如 varVar 是兩個不同的變數。

單一個變數名稱就是一個算式,這樣會輸出此變數的值,變數的值可以透過指定運算子(assignment operators)或遞增運算子(increment operators)更改,請參考指定算式。

Octave 內建的變數 ans 是一個特別的變數,當算式的運算結果沒有被指定到一個變數中儲存時,Octave 就會自動將運算結果儲存至 ans 中,例如:

cos(pi)

這個算式的運算結果沒有被指定要儲存在哪一個變數,所以 Octave 會儲存在 ans 中,輸出為

ans = -1

若是有明確指定要儲存的變數名稱:

a = cos(pi)

輸出為

a = -1

這樣 Octave 會將運算結果儲存至變數 a,而 ans 的值不會受影響。

Octave 中的變數沒有限制必須保持一定的類型,也就是說一個變數可以一開始儲存一個數值,隨後將此變數指定為一個字串。變數在使用前一定要先指定變數的值,使用未經指定的變數名稱會造成錯誤。

ans

ans 變數會儲存最近一次未指定的儲存變數的運算結果,例如:

3^2 + 4^2

輸出為

ans =  25
isvarname (name)

isvarname(name) 函數會判斷 name 是否為正確的變數名稱,若正確則傳回 true,否則傳回 false。例如:

isvarname("test_var")

輸出為

ans =  1
varname = genvarname (str)
varname = genvarname (str, exclusions)

genvarname(str) 函數會根據字串 str 產生一個獨特的變數名稱,參數 exclusions 是指定排除的名稱。例如:

x = 3.141;
genvarname ("x", who ())

輸出為

ans = x1

str 指定為巢狀陣列,則會輸出一個包含獨特變數名稱的巢狀陣列,例如

genvarname ({"foo", "foo"})

輸出為

ans =

{
  [1,1] = foo
  [1,2] = foo1
}

genvarname() 函數所輸出的結果是字串或字串巢狀陣列,若是需要定義一個變數,可以使用 eval() 函數,例如:

name = genvarname ("x");
eval([name " = 42"]);

這會將產生的變數指定為 42,輸出為

x =  42

另外,genvarname() 也可以用在產生資料結構的欄位名稱,例如:

x = struct ();
for i = 1:3
  x.(genvarname ("a", fieldnames (x))) = i;
endfor
x

輸出為

x =
{
  a =  1
  a1 =  2
  a2 =  3
}

由於變數名稱只能包含英文字母、數字或下底線,genvarname() 函數會將不符合的字元以下底線代替,變數開頭若為數字,則會在開頭自動加入一個下底線,例如:

genvarname("123abc")

輸出為

ans = _123abc

雙下底線開頭的變數雖然是正確的變數名稱,但這樣的變數名稱是專門留給 Octave 內部的變數使用的,因此在一般的情況應該避免使用這類的名稱,genvarname() 函數也不會產生這樣的變數名稱。

genvarname() 函數所產生的變數名稱會自動避開 Octave 的關鍵字,例如 forif 等,但不會自動避開函數名稱,例如 sin,因此要避免產生與函數名稱相同的變數名稱,可以使用 exclusions 參數加以指定。

namelengthmax ()

namelengthmax() 函數會傳回與 Matlab 相容的最長變數名稱長度,在 Octave 中最長的變數名稱長度是 2^31-1,而在 Matlab 中若是變數名稱長度沒有那麼長,其最大長度可由此函數獲得,若要與 Matlab 相容,則所有的變數名稱長度都要小於這個值,若變數名稱長度超過這個值,在 Matlab 中會將變數名稱超過的部分自動刪除。

全域變數(Global Variables)

在宣告變數時可以使用 global 關鍵字將變數宣告為全域變數(Global Variables),例如:

global a
global a b
global c = 2
global d = 3 e f = 5

被宣告為全域的變數不需要以參數的形式傳入函數中,即可直接在函數中使用。

使用 global 宣告全域變數時,只能宣告一次,重複宣告不會有作用,例如:

global gvar = 1
global gvar = 2

此時 gvar 的值是 1,不是 2

若要在函數中使用全域變數必須在函數中使用 global 宣告變數,例如:

global x
function f ()
  x = 1;
endfunction
f ()

這樣執行 f() 函數並不會將全域變數 x 變數指定為 1,要將全域變數 x 指定為 1 必須在函數中使用 global 宣告變數:

function f ()
  global x;
  x = 1;
endfunction

將全域變數以參數的形式傳入函數中時,Octave 會將全域變數複製一份在函數中,不會影響到原來的全域變數,例如定義一個函數 f(x)

function f (x)
  x = 0
endfunction

另外再宣告一個全域變數:

global x = 13

將此全域變數傳入 f() 中:

f (x)

輸出為

x = 0

但最外層的全域變數 x 的值還是沒有改變,其值還是維持 13

isglobal (name)

isglobal(name) 函數會判斷變數 name 是否為可見的全域變數。例如:

global x
isglobal ("x")

輸出為

ans =  1

永久變數(Persistent Variables)

在函數中的永久變數(persistent variable)會將其儲存的值持續保持在記憶體中,即使離開函數後,其值依然存在,永久變數與全域變數的差異在於永久變數只存在於其宣告的區域,在其他的區域是不可見的。例如:

function count_calls ()
  persistent calls = 0;
  printf ("'count_calls' has been called %d times\n", ++calls);
endfunction

這裡自行定義了一個函數 count_calls(),此函數使用一個永久變數紀錄被呼叫的次數,使用迴圈呼叫此函數三次:

for i = 1:3
  count_calls ();
endfor

輸出為

'count_calls' has been called 1 times
'count_calls' has been called 2 times
'count_calls' has been called 3 times

永久變數可以使用 persistent 關鍵字宣告,例如:

persistent a
persistent a b
persistent c = 2
persistent d = 3 e f = 5

Octave 中的永久變數與 C 語言中的靜態變數(static variable)有相同的作用,Octave 中的關鍵字 staticpersistent 的作用是一樣的。

永久變數與全域變數一樣只能宣告一次,重複宣告不會有作用,例如:

persistent pvar = 1
persistent pvar = 2

此時 pvar 的值是 1,不是 2

若永久變數只有被宣告,但沒有指定其值,其預設會是一個空矩陣,因此可以依照其是否為空矩陣來判斷其是否已經被初始化,例如:

function count_calls2 ()
  persistent calls;
  if (isempty (calls))
    calls = ;
  endif
  printf ("'count_calls' has been called %d times\n", ++calls);
endfunction

這個 count_calls2() 函數與上面的 count_call() 函數的功能是一樣的。

宣告在函數中的永久變數會一直存在記憶體中,直到將整個函數刪除,永久變數的值才會從記憶體中刪除,要將函數刪除可以使用 clear 指令:

clear -f count_call

這樣會將函數 count_call() 與其中的永久變數從記憶體中刪除。

變數的狀態(Status of Variables)

在使用 Octave 時常常會需要查看目前存在的變數有哪些,這時候可以使用 who 等相關的指令,這些指令可以顯示目前在記憶體中儲存的變數,例如:

str = "A random string";
who

輸出為

Variables in the current scope:

ans  str
who
who pattern ...
who option pattern ...
C = who("pattern", ...)

who 指令可以列出目前有定義的變數,參數 pattern 可以指定所要顯示變數,指定方式與 clear 函數所使用的指定方式相同,若不指定則預設會顯示所有的區域變數。參數 option 可以指定為下列選項:

  • global:顯示全域變數。
  • -regexp:在 pattern 中使用常規表示法。
  • -file:將下一個參數視為檔案,顯示此檔案中所列的變數,以檔案指定變數時,必須指定變數的完整名稱,無法使用 pattern 的方式比對。

若以函數的方式呼叫,則會傳回包含變數名稱的巢狀陣列,例如:

c = who()

輸出為

c =

{
  [1,1] = ans
  [2,1] = c
  [3,1] = str
}
whos
whos pattern ...
whos option pattern ...
S = whos("pattern", ...)

whos 指令會輸出目前有定義的變數與其細部資訊。其參數的使用方式與 who 指令相同。whos 指令會顯示變數的下列資訊:

  • Attr:變數的屬性(attributes),可能的值有:
    • 空白:一般區域變數。
    • g:全域變數(global variable)。
    • p:永久變數(persistent variable)。
  • Name:變數名稱。
  • Size:變數的尺寸,例如純量是 1×1,向量是 Nx1 或 1xN,二維的矩陣是 MxN。
  • Bytes:變數所占的記憶體大小。
  • Class:變數的類別,例如:雙精度浮點數(double)、單精度浮點數(single)、字元(char)、十六位元無號整數(uint16)、巢狀陣列(cell)與資料結構(struct)等。

以上這些資訊可以使用 whos_line_format() 函數自行設定所要輸出的欄位。

若以函數的方式呼叫,則會傳回包含上述資訊的資料結構,例如:

s = whos()

輸出為

s =
{
  4x1 struct array containing the fields:

    name
    size
    bytes
    class
    global
    sparse
    complex
    nesting
    persistent
}
val = whos_line_format ()
old_val = whos_line_format (new_val)

whos_line_format() 函數可用來設定 whos 指令的輸出格式,完整的欄位格式為:

%[modifier]<command>[:width[:left-min[:balance]]];

command 的部分有下列選項可以使用:

  • a:變數的屬性(attributes)。
  • b:變數所占的記憶體大小(bytes)。
  • c:變數的類別(class)。
  • e:變數所儲存的元素(elements)。
  • n:變數的名稱(name)。
  • s:變數的維度(size)。
  • t:變數的類型(type)。

modifier 的部分可使用的選項有:

  • l:靠左對齊。
  • r:靠右對齊。
  • c:置中對齊。

width 是一個正整數,指定輸出欄位時使用的最小寬度。當欄位的內容較長時,欄位的寬度會依照需要自動增加。

left-minbalance 只有在配合 modifier 輸出變數的維度時才會用到。

預設的格式是 " %a:4; %ln:6; %cs:16:6:1; %rb:12; %lc:-1;\n"

要檢查一個變數是否存在,除了上述的 who 指令外,也可以使用 exist() 函數。

exist (name, type)

exist(name) 函數會檢查 name 是否存在,若 name 是一個變數,則傳回 1;若為包含絕對路徑的檔案名稱、Octave 路徑中的檔案或函數(副檔名為 .m),則傳回 2;若為 Octave 路徑中的 .oct.mex 檔,則傳回 3;若為內建函數,則傳回 5;若為目錄,則傳回 7;若為使用者輸入的函數,則傳回 103;除此之外則傳回 0

exist(name) 函數在 name 為一般檔案時會傳回 2,若是需要獲取更多關於檔案的資訊,可以使用 file_in_path()stat() 函數。

參數 type 可以指定要檢查的類型,若有指定 type 參數,則在檢查時使會針對指定的類型做檢查,可用的選項有:

  • "var":只檢查變數。
  • "builtin":只檢查內建函數。
  • "file":只檢查檔案。
  • "dir":只檢查目錄。

一般來說 Octave 會自己管理記憶體的配置,但有些時候使用者會需要自行將某些變數從記憶體中刪除,例如在處理大量的資料時,會需要將沒有用到的變數刪除,釋放記憶體給其他變數使用。在 Octave 中要刪除變數可以使用 clear() 函數。

clear [options] pattern ...

clear 指令可以刪除由參數 pattern 所指定的變數,pattern 可以包含下列特殊字元:

  • ?:比對任意一個字元。
  • *:比對零個以上的任意字元。
  • [list]:比對中括號中所列出的字元,若括號中的第一個字元是 !^,則會比對這些字元以外的任意字元,例如 [a-zA-Z] 會比對所有大寫與小寫的英文字母。

例如:

clear foo b*r

將會把 foo 與所有 b 開頭 r 結尾的變數刪除。

若呼叫 clear 而不加任何參數,則會刪除所有使用者定義的變數(包含全域變數與區域變數);若加入至少一個參數,則只會刪除比對成功的可見變數,例如我們定義一個函數 foo(),然後再定義一個名稱為 foo 的變數,此時 foo() 函數會被隱藏,這個時候若是執行一次 clear foo,會將變數 foo 刪除,而 foo() 函數變成可見,若再執行一次 clear -f foo 則會將 foo() 函數刪除。

clear 可用的選項有:

  • -all-a:刪除所有使用者定義的區域變數、全域變數與函數。
  • -exclusive-x:刪除所有不符合 pattern 的變數。
  • -functions-f:刪除函數。
  • -global-g:刪除全域變數。
  • -variables-v:刪除區域變數。
  • -classes-c:刪除類別資料結構表與所有變數。
  • -regexp-r:將 pattern 是為常規表示法來比對。

除了 exclusive 之外,其餘的長選項都可以將開頭的連字號 - 省略。

關於函數與變數的細部資訊,例如函數所在的檔案位置等,可以經由下面的指令獲得,但這些資訊通常只有在開發程式時才會用到。

type options funcname ...

type 指令會顯示函數 funcname 的定義,另外也會顯示此函數是使用者定義的(user-definded )還是內建的(built-in ),若不要顯示可加入參數 -q

若有指定輸出參數,則會傳回一個包含函數定義的字串巢狀陣列,例如:

plotdef = type("dec2bin")

which 指令可以尋找 name 所在的位置:

which name ...

例如:

which dec2bin

輸出為

`dec2bin' is a function from the file C:\Octave\3.2.4_gcc-4.4.0\share\octave\3.2.4\m\strings\dec2bin.m

what 指令會列出目錄中所有的 Octave 檔案:

what
what dir
w = what (dir)

例如:

what 'C:\Octave\3.2.4_gcc-4.4.0\share\octave\3.2.4\m\strings'

輸出為

base2dec.m          isletter.m          strjust.m
   bin2dec.m           isstrprop.m         strmatch.m
   blanks.m            mat2str.m           strncmpi.m
   cstrcat.m           regexptranslate.m   strrep.m
   deblank.m           rindex.m            strsplit.m
   dec2base.m          str2double.m        strtok.m
   dec2bin.m           str2num.m           strtrim.m
   dec2hex.m           strcat.m            strtrunc.m
   findstr.m           strchr.m            substr.m
   hex2dec.m           strcmpi.m           validatestring.m
   index.m

若有指定輸出參數,則會傳回一個資料結構。