張智星 (Roger Jang) jang@mirlab.org http://mirlab.org/jang 台大資工系 多媒體檢索實驗室 MATLAB 程式設計與應用 結構陣列 張智星 (Roger Jang) jang@mirlab.org http://mirlab.org/jang 台大資工系 多媒體檢索實驗室
13-1結構陣列的建立 每一個結構陣列(Structure Array)可以包含多個元素,每一個元素可以看成是一筆資料。 因此每個元素(或每筆資料)可以包含數個欄位(Fields),而每個欄位可包含不同型態的資料。例如一個包含學生個人資料的結構陣列,可能含有的欄位是 name(學生姓名)、id(學號)、scores(小考成績)等。
結構陣列之範例一 要建立一個結構陣列,可在指令列直接輸入各個欄位的值。 範例13-1 : struct01.m clear student % 清除 student 變數 student.name = '洪鵬翔'; % 加入 name 欄位 student.id = 'mr871912'; % 加入 id 欄位 student.scores = [58, 75, 62]; % 加入 scores 欄位 student % 秀出結果 student = name: '洪鵬翔' id: 'mr871912' scores: '[58,75,62]‘ 此時 student 即代表一個結構陣列的第一個元素,或是第一筆資料。
結構陣列之範例二 範例13-2 : struct02.m clear student % 清除 student 變數 student.name = ‘洪鵬翔’; % 加入 name 欄位 student.id = ‘mr871912’; % 加入 id 欄位 student.scores = [58, 75, 62]; % 加入 scores 欄位 % 以下是新加入的第二筆資料 student(2).name = '邱中人'; student(2).id = 'mr872510'; student(2).scores = [25, 36, 92]; student % 秀出結果 student = 1x2 struct array with fields: Name Id scores dent =
結構陣列之範例二 此時 student 即代表一個 1×2 的結構陣列。由於此結構陣列已漸趨複雜,MATLAB 並不將所有欄位值印出。欲顯示某元素的特定欄位值,可輸入明確的敘述,例如 student(2).scores 等。 另一個建立結構陣列的方法,則是使用 struct 指令,其格式如下: structureArray = struct(field1, value1, field2, value2,….) 其中 field1、field2、…是欄位名稱,value1、value2、…則是欄位所包含的資料。如果 value1、value2、…為異質陣列(Cell Arrays,詳見第上一章),則 MATLAB 為依序將異質陣列的每個元素設定為每一個結構中相對應的欄位值,如以下範例。
結構陣列之範例三 範例13-3 : struct03.m student = struct('name', {'張庭碩', '張庭安'}, 'scores', {[50 60], [60 70]}); student(1) % 顯示 student(1) student(2) % 顯示 student(2) ans = name: '張庭碩‘ scores: [50 60] name: '張庭安' scores: [60 70] 在上述使用法中,{‘張庭碩’, ‘張庭安’} 和 {[50 60], [60 70]} 都是異質陣列,因此他們的每個元素會被依次設定到每個結構之中。但是如果其中有一個異值陣列的長度是1,那麼 MATLAB 會進行「純量展開」(Scalar Expansion)來自動補足,如以下範例。
結構陣列之範例四 範例13-4 : struct04.m student = struct('name', '張庭安‘,'scores', {[50 60], [90 100]}); student(1) % 顯示 student(1) student(2) % 顯示 student(2) ans = name: '張庭安' scores: [50 60] scores: [90 100] 在上述範例中,「張庭安」可視為異質陣列的一個元素,因此在設定至 student 結構陣列時,MATLAB 會進行純量展開,將「張庭安」分別設定到 student 的兩個元素的 name 欄位值。
結構陣列之範例五 結構陣列可以是巢狀式(Nested)的,也就是說,結構陣列的欄位可是另一個結構陣列,我們可以藉此產生複雜的資料結構 範例13-5 : struct05.m student = struct('name', {'張庭碩', '張庭安'}, 'scores', {[50 60], [60 70]}); student(2).course(1).title = 'Web Programming'; student(2).course(1).credits = 2; student(2).course(2).title = 'Numerical Method'; student(2).course(2).credits = 3; student(2).course ans = 1x2 struct array with fields: title credits
13-2 取用及改變結構陣列的資料 範例13-6 : buildStruct01.m 13-2 取用及改變結構陣列的資料 範例13-6 : buildStruct01.m clear student % 清除 student 變數 student(1) = struct('name', 'Banny', 'scores', [85,80,92,78]); student(2) = struct('name', 'Joey', 'scores', [80,85,90,88]); student(3) = struct('name', 'Betty', 'scores', [88,82,90,80]); 上述的 student 結構陣列,可圖示如下: student結構陣列 student(1) student(2) student(3) .name=‘banny’ .scores=[85,80,92,78] .name=‘joey’ .scores=[80,85,90,88] .name=‘batty’ .scores=[88,82,90,80]
struct2cell指令 欲取用結構陣列中所有元素內所有欄位的資料,可用 struct2cell 指令,例如: >> values = struct2cell(student) values(:,:,1) = 'Banny' [1x4 double] values(:,:,2) = 'Joey' values(:,:,3) = 'Betty' 請注意,傳回的 values 是一個異質陣列。一般而言,若輸入 struct2cell 指令的結構變數維度為 m×n,且包含 p 個欄位,則傳回異質陣列的維度為 p×m×n。(在上例中,p = 2,m = 1, n = 3。)
改變結構陣列內容 在結構陣列中,我們可以使用「句點」(”.”)來找出某一筆資料內的某一個特定欄位值,例如我們僅想看看第二個學生是誰,此時我們可以輸入: >> studentName = student(2).name studentName = Joey 在上例中,在此一學生結構陣列的第二陣列元素 student(2) 之後加上一點,再接上姓名欄位名稱 name,即可取用此學生的實際姓名資料 Joey,本例中進一步再將取出的學生姓名 Joey 儲存在使用者自設的變數 studentName 之後顯示於螢幕上。 類似取用結構陣列個別欄位資料內容的作法,我們可以改變結構陣列中個別欄位的資料內容,例如: >> student(2).name = 'Alex'; 在上例中,student(2) 的姓名已由原先的 Joey 改變為 Alex。
cat指令 MATLAB 提供了 cat 指令,以達到「並排欄位值」的目的,其語法為: A = cat(dim, structureField) 其中,dim 代表並排後所改變的維度。例如,欲將小考成績左右(水平)並排,可輸入: >> cat(2, student.scores) % 2 代表左右並排以改變直行的維度 ans = 85 80 92 78 80 85 90 88 88 82 90 80 欲將小考成績上下(垂直)並排,可輸入: >> cat(1, student.scores) % 1 代表上下並排以改變橫列的維度 85 80 92 78 80 85 90 88 88 82 90 80
計算平均 在進行「並排」時,必須確認被並排的欄位值有相同的行數(上下並排)或列數(左右並排),否則就會因為維度不和而產生錯誤訊息。 若要計算每次考試(共四次)的平均分數,可輸入: >> average1 = mean(cat(1, student.scores)) average1 = 84.3333 82.3333 90.6667 82.0000 若要計算每位學生(共三位)的平均分數,可輸入下列運算式,但注意運算式後面單引號的使用代表矩陣的轉置(Transpose),因為 mean 指令預設是對矩陣的每一直行進行平均值運算: >> average2 = mean(cat(1,student.scores)') average2 = 83.7500 85.7500 85.0000
並排運算 由於「並排運算」常被用到,MATLAB 又提供下列兩種方法 方括弧運算:可以左右合併結構陣列中相同欄位的數值矩陣,產生一個新的數值矩陣。 大括弧運算:可以左右合併結構陣列中相同欄位的資料,產生一個異值矩陣。 若要把 scores 欄位值進行左右合併,可以輸入如下:>> allScores = [student.scores] allScores = 85 80 92 78 80 85 90 88 88 82 90 80 若要把 name 欄位值抽取出來,形成由字串組成的異值陣列,可以輸入如下: >> allNames = {student.name} allNames = 'Banny' 'Alex' 'Betty'
結構陣列之範例七 範例13-7 : printStruct01.m student 1: 張庭碩 student 2: 鍾書蓉 clear student % 清除 student 變數 student(1) = struct('name', '張庭碩', 'scores', [85, 80]); student(2) = struct('name', '鍾書蓉', 'scores', [80, 85]); student(3) = struct('name', '黃念中', 'scores', [88, 82]); for i = 1:length(student) % 列印出每個學生的名字 fprintf ('student %g: %s\n', i, student(i).name); end student 1: 張庭碩 student 2: 鍾書蓉 student 3: 黃念中
取得及改變欄位資料 亦可用 getfield及setfield來取得及改變一個欄位的資料 其指令使用格式如下: fieldValues = getfield (structureArray, {arrayIndex}, field, {fieldIndex}) newStructure = setfield (structureArray, {arrayIndex}, field,{fieldIndex}) 輸入下列運算式即可取得第二位學生的第一次小考成績: >> score3 = getfield(student, {2}, 'scores', {1}) 上述 MATLAB 敘述可簡化為 >> score3 = student(2).scores(1); 若欲改變第二位學生的第三次小考成績,可輸入如下: >> student = setfield(student, {2}, 'scores', {1}, 75); 上述敘述亦可簡化為 >> student(2).scores(1)=75;
結構陣列之範例八 範例13-8:deal01.m myStruct = struct('name', {'Tim', 'Annie'}, 'age', {10, 13}); [myStruct.name] = deal('Roger', ‘Tom'); fprintf('myStruct(1).name = %s\n', myStruct(1).name); fprintf('myStruct(2).name = %s\n', myStruct(2).name); myStruct(1).name = Roger myStruct(2).name = Tom
13-3取用及改變結構陣列的欄位 使用 fieldnames 指令可傳回一個結構陣列的所有欄位名稱,例如: 範例13-9:fieldNames01.m student = struct('name', 'Roland', 'scores', [80, 90]); allFields = fieldnames(student) allfields = 'name' 'scores' 其傳回的結果是一個字串異質陣列(Cell Array of Strings),包含了 student 的所有欄位。 欲增加一個新的欄位,直接將此欄加入於任一陣列元素即可
結構陣列之範例十 範例13-10:addField01.m clear student % 清除 student 變數 student = struct('name', 'Roland', 'scores', [80, 90]); student(2).age = 20; % 加入新欄位 student(1) % 顯示 student(1) student(2) % 顯示 student(2) ans = name: 'Roland' scores: [80 90] age: [] name: [] scores: [] age: 20 由上述結果可看出,MATLAB 會將此新欄位加入其他元素,並設定其預設值為 [](空矩陣)。
結構陣列之範例十一 student = struct('name', 'Roland', 'scores', [80, 90]) 範例13-11:rmField01.m student = struct('name', 'Roland', 'scores', [80, 90]) student2 = rmfield(student, 'scores') % 刪除 scores 欄位 student = name: 'Roland' scores: [80 90] student2 =
13-4 其他相關指令 我們可用 isstruct 指令來測試某個變數是否為結構陣列,如下: 範例13-12 : isstruct01.m s = struct('name', {'Tim', 'Ann'}, 'scores', {[1 3 5 ],[2 4 6]}); isstruct(s) ans = 1 isfield 指令則可用於測試某結構陣列是否含一特定欄位,例如: 範例13-13 : isstruct02.m fprintf('isfield(s, ''name'') = %d\n', isfield(s, 'name')); fprintf('isfield(s, ''height'') = %d\n', isfield(s, 'height')); isfield(s, 'name') = 1 isfield(s, 'height') = 0 在上例中,因 s 並不包含“height”的欄位,故回傳數值 0。
結構陣列之範例十四 我們可用 cell2struct 指令來將異質陣列轉換成結構陣列,例如: 範例13-14 : cell2struct01.m fields = {'name', 'age'}; values = {'Tim', 9; 'Annie', 6}; s = cell2struct(values, fields, 2); s(1) % 印出第一筆資料 s(2) % 印出第二筆資料 ans = name: 'Tim' age: 9 name: 'Annie' age: 6 在上例中,運算式「s = cell2struct(values, fields, 2)」代表指令 cell2struct 將根據陣列變數 fields 的資料為欄位名稱,並以 values 的第二個維度來對應欄位名稱 fields,來產生一個結構陣列 s。
結構陣列之範例十五 如果以 values 的第一個維度來對應欄位名稱 fields,結果如下: 範例13-15 : cell2struct02.m fields = {'name', 'age'}; values = {'Tim', 9; 'Annie', 6}; s = cell2struct(values, fields, 1); s(1) % 印出第一筆資料 s(2) % 印出第二筆資料 ans = name: 'Tim' age: 'Annie' name: 9 age: 6
結構陣列之範例十六 dir 指令傳回一結構陣列,包含現在目錄(或資料夾)下各種資訊 範例13-16 : dir01.m dirinfo = dir(matlabroot) % 字串變數 matlabroot 代表 MATLAB 根目錄 dirinfo = 19x1 struct array with fields: name date bytes isdir 由上可知,dirinfo 為一結構陣列,內含在 MATLAB 根目錄下所含檔案或其他目錄的資訊,即其名稱(name),產生日期(date),大小(bytes),及是否為目錄(isdir)等。