網頁標題: 15 陣列 (2): 應用篇

Warning: fopen(/home/crazy/www/cmsb/bcj/has_read.php): failed to open stream: Permission denied in /home/crazy/www/compose/reading.php on line 2070

Warning: fputs() expects parameter 1 to be resource, bool given in /home/crazy/www/compose/reading.php on line 2072

Warning: fclose() expects parameter 1 to be resource, bool given in /home/crazy/www/compose/reading.php on line 2073
 
﹗﹗﹗觀看留言:此文章已經有2則留言 ﹗﹗﹗


 本篇簡介 AutoIt 標準函式庫的 Array.au3, 是一些經常用到的陣列處理步驟寫成使用者自定義函式,主要針對一維、二維陣列。這群函式大約可分成「輸出」、「單純資料搬移」、「排序與搜尋」、「增減元素」、「其他」等五大類。

 Array.au3 中,函式全部都以 _Array 開頭,參數中長度 n 的一維陣列被當成有 n 列 (row) 或 n 欄 (column), 大小 m*n 的二維陣列則是一個 m 列 n 欄的表格。許多函式的參數設計都將「列」的指定設為必要,「欄」的指定設為可選,以兼容一維與二維陣列。以下內容會將函式標上連結,讀者可從網頁裡獲取更多範例。

 註:本文以現行版本 3.3.14.2 為準。


 前面用過的 _ArrayDisplay 即屬於「輸出」類,另外還有 _ArrayToString_ArrayToClip, 簡單示範如下:

 這二個函式的參數設計完全一樣,或者可以說 _ArrayToClip 只是將 _ArrayToString 的結果放入剪貼簿。在 Array.au3 的函式裡,第一個參數一定都是陣列。

 第二到第四個參數分別指定「分隔字元」、「開始的列」、「結束的列」,分隔字元預設是 |, 範圍預設是所有的列,若呼叫 _ArrayToString($demo, "/", 1, 3) 就會產生 "2/3/4" 的答案。另外,指定範圍時可以填入 -1 表示保留預設值。

 第五到第七個參數用來指定二維陣列裡的「列之間的分隔字元」、「開始的欄」以及「結束的欄」。「列之間的分隔字元」預設為 @CRLF, 也就是一行洽容納一列,欄的範圍也預設所有的欄。如果只想指定列的分隔字元或欄範圍,不得不填上列的範圍時,可將開始、結束都寫 -1 表示全範圍。

 _ArrayToString 失敗時該直接檢查 @error, _ArrayToClip 除了所有 _ArrayToString 會有的錯誤外,加上 @error 會因為內部呼叫 ClipPut 內建函式將字串放到剪貼簿時失敗而設為 -1.

 順道一提,其實二維陣列的初始化寫法,可視為欄之間用逗號分隔,列之間用 "], [" 分隔的結果,前後加上 "[[" 與 "]]" 的字串。


 所謂「單純資料搬移」指的是改變資料位置,與資料之間的比較無關。在此我也將「資料搬移到一個新陣列而回傳」的函式歸為此類。首先,不會產生新陣列的是 _ArraySwap, _ArrayReverse_ArrayShuffle. 簡單示範如下:

 _ArraySwap 可以指定同一陣列的兩個位置,函式內將雙方的值交換。一維陣列只要三個參數,交換兩個位置的值。二維陣列有另外三個可選參數,可交換兩列或兩欄的部份或全部。第四個參數 True/False 表示要換兩列或兩欄,預設是兩欄。最後二個參數用來指定要交換列(欄)的範圍。

 _ArrayReverse 用來反轉陣列裡資料擺放的順序,只能用於一維陣列。它有二個可選參數,用來指定要被反轉的範圍,上例未指定則預設反轉整個陣列。

 _ArrayShuffle 就是「洗牌」,打亂陣列裡資料擺放的順序,遊戲設計常用。它的第二、第三個參數是可選參數,可指定只要打亂陣列的部分範圍,未指定則預設打亂整個陣列。對二維陣列而言,指定的範圍是參考到「列」,也就是以一橫排為單位來打亂,不過可用第四個可選參數指定成只打亂這些列的某一欄而已。再提供一個打亂二維陣列的範例:

 第一次 _ArrayDisplay 展示的結果可能是 [[1, 2], [3, 4]] 或 [[3, 4], [1, 2]], 就是兩列交換與否的結果,但第二次 _ArrayDisplay 顯示 [[1, 2], [3, 4]] 或 [[1, 4], [3, 2]], 就是只有第二欄被交換與否。注意參數的 1 是因為陣列索引從 0 算起,,所以第二欄的索引是 1. 這是隨機交換,不保證何時會有何結果,因此要看到所有結果就多執行幾次吧!

 想知道洗牌的內部實作,讀者可參考 Fisher-Yates shuffle, 看懂可用於其他沒有現成實作洗牌的程式語言來寫遊戲。


 「單純資料搬移」中被我歸到另一類的是 _ArrayTranspose, _ArrayCombinations_ArrayPermute. 它們都會牽涉到記憶體配置的改變,且都遵守某些既定原則來重新安排元素的位置,都帶點數學的味道,因此放在一起。

 _ArrayTranspose 用來「轉置」陣列,「轉置」即把它的列、欄對調,元使定義上只針對二維陣列。轉置前在 [$i][$j] 位置的資料,轉置後在 [$j][$i] 的位置,線性代數中「轉置」是對矩陣的一個重要運算。雖然上例未示範,但 _ArrayTranspose 會把一維陣列當成只有一列的二維陣列來轉置,再轉回去的話就真變成只有一列的二維陣列了。

 _ArrayCombinations 用來列出所有組合方法,上例將顯示四個元素的一維陣列 [3, "AB", "AC", "BC"]. 第一個元素 3 是計數,答案在後三個元素,但以字串表示。參數的 2 表示取出二個元素的組合方式,另有一個可選參數指定輸出組合方式之間區隔元素用的符號,比如指定 "|" 會把輸出的 "AB" 變成 "A|B".

 _ArrayPermute 用來列出所有排列方法,它也直接回傳另一個陣列,格式與 _ArrayCombinations 一致。上例將得到 [6, "ABC", "ACB", "BAC", "BCA", "CBA", "CAB"]. 它也有一個可選參數,指定輸出的排列方式之間區隔元素用的符號。這些排列的所有方式,就是洗牌後所有可能的結果。注意 _ArrayPermute 會改變輸入的陣列,變成轉置後的結果,想深入了解的讀者可閱讀 Calculating Permutations 這個網頁。


 「排序與搜尋」類有八個函式,但用途都很好理解,先以表格簡述其功能如下:

名稱功能
_ArrayMax搜尋陣列中最大的元素
_ArrayMaxIndex搜尋陣列中最大元素的位置
_ArrayMin搜尋陣列中最小的元素
_ArrayMinIndex搜尋陣列中最小元素的位置
_ArraySearch循序搜尋陣列中某個元素的(一個)出現位置
_ArrayFindAll循序搜尋陣列中某個元素的所有出現位置
_ArraySort排序陣列的內容
_ArrayBinarySearch二分搜尋陣列中某個元素的(一個)出現位置

 除了 _ArraySort 是「排序」用以外其他都是「搜尋」用途,之所以如此安排介紹的順序,原因是只有 _ArrayBinarySearch 要求陣列先行排序。以下示範搜尋最大、最小值位置的二個函式 _ArrayMaxIndex 跟 _ArrayMinIndex:

 這二個函式的參數設計完全相同。第二至第五個參數皆可選,第二個參數決定要用「字串」或者「數值」的角度來比較,當指定為預設的「字串」角度 (0) 時,依據字典順序來比較,「數值」角度 (1) 則將所有字串都轉數值後才比較。第一次 _ArrayDisplay 顯示字典順序挑出 8 開頭的字串最大,但第二次 _ArrayDisplay 顯示轉數值後 12 最大。

 第三、四個參數指定搜尋範圍,_ArrayMinIndex 第三個參數 2 即從位置 2 開始找起,本來整個陣列最小的數值是 4, 但從位置 2 開始找起最小的數值變成位置 5 的 5.

 如果輸入是二維陣列,可用第五個參數指定要找哪欄的最大元素位置,預設是索引 0 的欄,二維陣列就不在本篇示範。

 _ArrayMax, _ArrayMin 的參數和錯誤設置 @error 都跟 _ArrayMaxIndex, _ArrayMinIndex 一樣,只有回傳值變成找到的資料本身而非索引位置,故不另加示範。另外,當陣列內容混雜 0-9 字串與數值型態,建議先轉成相同型態才找最大、最小值,或者指定以數值角度來比較,否則結果可能不是預期。

 接著示範 _ArraySearch 及 _ArrayFindAll, 兩者都用來找出特定資料在陣列裡的位置,前者找出第一次或最後一次出現指定資料的位置,後者則一直呼叫 _ArraySearch 持續找出所有該資料出現的位置。

 上例有七次 MsgBox 印出 _ArraySearch 結果,及一次 _ArrayDisplay 印出 _ArrayFindAll 結果。前三次 MsgBox 針對 12 搜尋,後四次針對 AB, _ArrayFindAll 則只展示搜尋 12.

 _ArraySearch 共有九個參數,前兩個是輸入陣列即要找的特定資料,第三、四個可選參數指定搜尋範圍(預設整個陣列),第五到七個可選參數指定比較是否分大小寫、其他比較細節、搜尋的方向,最後二個可選參數與二維陣列有關,留給讀者摸索。

 第一次 MsgBox 印出 0 但那裡是「字串」的 12, 可見未指定範圍等其他比較細節時,_ArraySearch 會把資料轉成跟要找的值相同型態才比。第二次 MsgBox 因為指定範圍從 1 開始,越過 0 就會找到位置 3 的 12. 第三次 MsgBox 把第三到第五參數都填預設值 0, 只有第六參數指定 2, 意即同形態才會比較,因此搜尋整個陣列卻也得到答案是位置 3.

 第四次 MsgBox 答案 1 表示 "AB" 被無論大小寫比對,第五次 MsgBox 有要求第五參數為 1 就是也看大小寫,才會得答案 2. 第六次 MsgBox 把第六參數設為 1 表示「字串的部份搜尋」,找出有 B 的資料在位置 1, 但第七個 MsgBox 是第六個 MsgBox 的狀況多限定大小寫就找到 B 出現在位置 2.

 註:第七參數在範例中也未示範,留給讀者自己改數字玩玩看,預設 (1) 就是從頭搜尋,寫 0 變成從尾巴搜尋。

 _ArrayDisplay 顯示一維陣列內有 0, 3 兩個元素,也就是 12 出現在資料的這兩個位置。_ArrayFindAll 的參數跟 _ArraySearch 幾乎一樣,只是少了 _ArraySearch 第七個參數來指定搜尋方向,_ArrayFindAll 的第七、八參數就是 _ArraySearch 的第八、九參數。另一處差別就是 _ArrayFindAll 回傳陣列而非單一數字。

 最後是 _ArraySort 與 _ArrayBinarySearch. _ArrayBinarySearch 只能用於已排序的資料,若由小而大排序,發現中間的元素比要找的東西大時答案一定在左半邊,反之則是右半邊,每次都可以去除一半資料不必再比對,因而得名。以下先進行示範:

 _ArraySort 依照字典順序來排序四個名字,換言之就是 AutoIt 原本就定義的大於、小於關係。它的第二個是可選參數,為 1 則排序變成由大到小,第三、四個參數指定要排序的範圍,最後一個參數針對二維陣列指定參與排序用的欄位,二維陣列根據該欄決定資料順序,但資料位置交換時以整列為單位。

 註:被排序的陣列應該指定排序範圍內所有元素的資料型態藥一樣,否則 AutoIt 內部自動轉型比較的結果可能不是預期!

 第一個 Else 內,正確取得使用者輸入的名字後,就用 _ArrayBinarySearch 找出所在位置,不過因為此時陣列已排序了,所以搜尋出來的答案是排序之後的位置。它的第三、四個是可選參數,指定被搜尋的陣列範圍,最後一個參數針對二維陣列指定被搜尋的欄位。它跟 _ArraySort 一樣都用 AutoIt 定義的無大小寫比較關係,因此大小寫沒打對也一樣會找到人。

 以下示範二維陣列的排序與搜尋:

 這裡 $data 是一個表格,有標題列及四筆資料。排序時第三個參數指定 1 即範圍從第一列開始,以越過標題列,第四個參數指定 0 是它的預設值,範圍仍會延伸到最後,第五個參數 1 表示依據索引 1 的欄位排序。

 _ArrayDisplay 結果顯示排序完後,資料搬移是整列的,「編號」跟著人跑。但因為標題列未納入排序範圍,所以標題列仍在第一列。

 二分搜尋時,也用第三、四個參數指定跟排序時一樣的範圍,第五個參數也指定索引 1 的欄位。找到人的輸出結果會列出位置與編號供比對,表示可透過 $index 取出人的其他資料,讀者也可以改成取出年齡或性別。


 「增減元素」類的函式主要就是進行元素的「插入」與「刪除」。下面將此類八個函式的功能列表簡述:

名稱功能
_ArrayAdd將指定的值加到一維或二維陣列最後面
_ArrayColDelete移除二維陣列中指定的欄
_ArrayColInsert新增欄至二維陣列中的指定位置
_ArrayConcatenate串接二個陣列的內容
_ArrayDelete從指定位置刪除陣列的元素
_ArrayInsert插入元素到陣列中指定位置
_ArrayPop回傳陣列最後面的元素並移除之
_ArrayPush把元素加到陣列最後面但移除最前面的元素,或反之

 下列範例用一維陣列展示幾個函式的行為:

 第一次 _ArrayDisplay 顯示的是 _ArrayDelete 完後的結果 [2, 3, 4, 5, 6, 7, 8]. 第二次 _ArrayDisplay 顯示的是 _ArrayPop 完後的結果 [2, 3, 4, 9, 5, 6, 7].

 _ArrayAdd 在陣列最後追加元素,這裡只展示前二個參數:陣列與要追加的值。因此 6 在最後面,它也可以一次追加多個元素,將陣列 $b 隹加上去則 7 跟 8 就依序被放在最後。

 _ArrayDelete 可從任意位置去除元素,第二個參數就是要去除元素的位置。這裡將它指定為 0, 則陣列 [0] 的元素就被去除了,後面的元素會往前遞補,換言之,刪除後 $a[0] 並非不存在,而是由原本在 $a[1] 的 2 取代,真正變成不存在的是 $a[7], 因為後面沒有元素來遞補它。

 _ArrayInsert 也是增加元素但可任選位置,第二個參數變成要加入元素的位置,第三個參數才是要被加入的元素。完成後 $a[3] 變成指定的 9, 其他的值就依序被往後推。這裡也可以插入多個元素,不過本文沒有多加示範。

 _ArrayPop 不但會固定去除陣列最後一個元素,還會把該元素回傳,因此特地接住其回傳值在 $top 並且將它放在 _ArrayDisplay 的視窗標題裡方便檢查。當 _ArrayInsert 完後 $a 最後一個元素是 8, 因此 _ArrayDisplay 視窗標題就寫 $top = 8, 可是清單堛滌}列內容只顯示到 7.

 以下範例展示二維陣列的操作。剛才示範的函式如果被修改陣列是二維,被增減的都是「列」的數目,但有時我們要調整「欄」的數目。這邊展示 _ArrayColInsert 及 _ArrayColDelete 感覺上就是替資料表格增減欄位,之前的函式則替表格增減資料本身的數量。

 第一次 _ArrayDisplay 顯示的是包含「名字」、「身高」、「體重」的表格,第二次 _ArrayDisplay 顯示的表格多了「血型」欄位,第三次 _ArrayDisplay 顯示的表格比上次少了「名字」欄位。

 _ArrayColInsert 可在表格裡新增空欄位,不過新增以後該欄的標題與資料內容必須在程式裡自行填上。第二個參數指定新增欄位處,指定為 1 表示以後 $data 陣列的第二個索引為 1 的位置都變成新創立的空變數,後面有資料就往後推移,猶如在 Excel 裡的插入欄功能。

 _ArrayColDelete 第二個參數就是要刪除的欄位置,此處 0 就是第一欄「名字」。它也如同 Excel 的刪除欄功能,當這欄清除後,後面的資料就往前推移地補。_ArrayColDelete 有第三個可選參數,預設為假,若設為真則當刪除欄後陣列剩下一欄時,自動把它變成一維陣列。

 想挑戰的讀者,可以自己追加一個 BMI 欄,然後替二人算看看 BMI 後填入。


回 · 我的 AutoIt 學習筆記 這一篇文章封面
回 · 我的 AutoIt 學習筆記 這一篇文章封面
回 · 我的 AutoIt 學習筆記 這一篇文章封面
回 · 我的 AutoIt 學習筆記 這一篇文章封面


本文張貼者:Bo-Cheng Jhan〔張貼時間:民國105年6月8日(星期三)0點29分 | 更新次數 #9 | 最後更新:民國105年8月1日(星期一)12點23分〕

部落格首頁


學習的故鄉首頁
本站公告:〔您越需要我們,我們就越有創意〕 本站說明書:〔發現故鄉還有改進的地方,請來信告訴原丁們〕
觀察應用學習點數 :〔咱的故鄉有您的參與,會使我們有更大的發揮空間,展現更豐富精彩的學習畫面〕 〔期待藉由無障礙網頁設計,能讓視障小朋友更愛看書、更愛寫作且更愛學習〕:盲用電腦「心得分享」
〔為了讓我們有乾淨的學習環境,請勿任意在本站散播商業廣告與不合法文件或聯結〕:本站宣示