網頁標題: 12 位元尺度的運算

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 2077

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

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



 本篇提及「位元」尺度的運算,從示範更完整的 MsgBox 操作談起,介紹旗標 (flag) 的概念。以下先從一個較不一樣的 MsgBox 範例開始,其實 MsgBox 是可以顯示多種按鈕組合的,下例讓它顯示「終止」、「重試」、「略過」三個按鈕,並且讓一開始焦點停留在第二個「「重試」紐:

 MsgBox 出現兩次,第一個參數都變成幾個常數的組合。$MB_ABORTRETRYIGNORE 從字面上拆解出 ABORT, RETRY, IGNORE 三個單字,就是顯示出來三個按鈕。$MB_DEFBUTTON2 則用來要求 MsgBox 一開始將焦點設在第二個按鈕。第二次 MsgBox 呼叫中的 $MB_ICONERROR 會讓它呈現錯誤的圖示,聽到的聲音提示也會不同。

 剛才的描述中含有 MsgBox 的三個概念:「出現的按鈕」、「預設焦點的位置」與「顯示的圖示」。MsgBox 的第一個參數不一定三種概念都被指定,但不同以往的是,當二種概念同時要被指定時必須透過內建函式 BitOR, 六種位元尺度運算之一。

 Switch...EndSwitch 中的關鍵字前面都介紹過,然而寫在 Case 的是以前也未見過的常數。MsgBox 當使用者按下按鈕回應時就結束,回傳值表示被使用者指定的按鈕。它們都是 $ID 開頭,而後面接自己的按鈕名稱,如 $IDABORT 就是「終止」紐。當然,之前指定 $MB_OK 時 MsgBox 只有「確定」紐,回傳的即 $IDOK, 但也因為只有單一可能性,就無須顧慮 MsgBox 的回傳值。

 上例既然有三個按鈕,應該也會回傳 $IDRETRY 即「重試」紐,但沒列入 Case 表示按了「重試」後 AutoIt 將跳到 WEnd 又跳回 While 開始處,再次顯示「發生錯誤」的訊息。按「終止」跟「略過」的差異在於前者會多跳一個視窗。

 MsgBox 的第一個參數有至少三種概念可供指定,當無指定時 MsgBox 就會執行「預設」的行為。下表列出這三個選項會用到的常數並敘述其功能。





$MB_OK確定(預設行為)
$MB_OKCANCEL確定、取消
$MB_ABORTRETRYIGNORE終止、重試、略過
$MB_YESNOCANCEL是、否、取消
$MB_YESNO是、否
$MB_RETRYCANCEL重試、取消
$MB_CANCELTRYCONTINUE取消、重試、繼續




$MB_ICONSTOP「停止」圖示
$MB_ICONERROR
$MB_ICONHAND
$MB_ICONQUESTION問號圖示,建議不要使用
$MB_ICONEXCLAMATION驚嘆號圖示
「警告」之意
$MB_ICONWARNING



$MB_DEFBUTTON1第一個按鈕(預設行為)
$MB_DEFBUTTON2第二個按鈕
$MB_DEFBUTTON3第三個按鈕
$MB_DEFBUTTON4第四個按鈕

 MsgBox 的第一個參數最多可三項都指定,如 BitOR($MB_CANCELTRYCONTINUE, $MB_ICONWARNING, $MB_DEFBUTTON2) 看起來就是一個警告視窗,也許剛才程式執行某些步驟有出現小錯誤但可以容許,程式詢問要取消、重試或繼續,預設放在「重試」暗示設計者較希望使用者檢查一下自己系統狀態後重試一次。

 上表明確把常數分成三群,每群中的常數最多只能挑一個指定,否則效果將不是預期,如 BitOR($MB_OKCANCEL, $MB_YESNO) 不會變成「確定」、「取消」、「是」、「否」四個按鈕一起出現。若無指定則會採取預設行為,比如只有指定圖示而沒寫另外兩個,就只顯示「確定」紐,焦點在第一個按鈕上,至於當圖示未被指定時就沒有圖示被顯示。

 按鈕方面另有一個獨立選項 $MB_HELP, 不限制跟以上何種按鈕組合共用,效果是多出一個「說明」紐,其使用不在本文範圍內。圖示也有兩個獨立選項,第一個可填 $MB_ICONINFORMATION 或 $MB_ICONASTERISK, 效果是多顯示一個小寫 i 圈起來的圖案,第二個「使用者圖示」選項可填 $MB_USERICON, 其使用也不列入本文。

 第一次見到 MsgBox 時我們這樣呼叫它 MsgBox(0, "第一個程式", "大家好!這是我寫的第一個 Windows 程式,請多指教。"), 當時只提 0 表示只有「確定」紐,但其實 0 意謂著所有選項都採用預設行為,所以只有「確定」紐,沒有圖示也沒有「說明」紐。

 因此以上總共談到六個「選項」,按鈕、圖示、預設焦點之外,「是否顯示說明紐」、「是否顯示 i 圈起來圖案」和「使用者圖示」。前三個是多選一的單選題,後三個是選或不選的複選題。以下就開始說明如何用位元尺度的運算達到單選、多選的效果。


 位元尺度運算共有 BitNOT, BitAND, BitOR, BitXOR, BitShift, 與 BitRotate 等六個內建函式支援。「位元尺度」意謂資料將以二進位 0/1 的角度來看待,不像過去資料的基本單位就是「型態」與「變數」。其實任意長度的二進位資料都可以做這些運算,然而較可惜的是 AutoIt 內建函式只處理 32 位元的整數。以下只簡介前五種運算,BitRotate 留給有興趣的讀者找資料自學了。

 整數與二進位間轉換請參考計算機概論相關書籍,這邊假設讀者已經學會。以下說明為方便閱讀,都以 4 位元整數舉例。

 BitNOT 只要一個輸入資料,會將資料裡每個位元的 0/1 變相反,如 0010 反向為 1101.

 BitAND, BitOR, BitXOR 三種運算都要二個位元數相等的輸入資料。BitAND 將雙方對應位置的位元都做「且」,BitOR 則做「或」。如 0011 與 0101 的 BitAND 結果是 0001, BitOR 結果是 0111. 「且」只要雙方有一個位元為 0 結果就是 0, 前三位元都至少一方帶有 0, 因此「且」的結果只有第四位元為 1. 「或」只要一方為 1 則結果就是 1, 因此「或」的結果僅第一位元 0, 後三個位元皆 1.

 BitXOR 則將對應位元做 XOR (exclusive-OR) 運算,中文稱「異或」或「互斥或」。「異或」的規則為,雙方位元相等答案為 0, 不童則答案為 1, 因此 0011 與 0101 的 BitXOR 結果是 0110, 中間兩個 1 代表那邊雙方提供的位元不一樣。

 BitShift 為「位移」,效果是將輸入資料的位元往右或往左移動,如 0001 左移一個位元變成 0010, 再左移一個位元變成 0100. 它的第一個參數是要運算的數值,第二個參數表示位移量,位移量正值表示向右移動,負值表示向左移動。其實剛才 0001 向左位移後第一個位元被丟棄,第四個位元右邊沒有資料來遞補,於是定義補 0, 但是向右位移較複雜,要遞補跟第一個位元一樣的 0/1, 也就是將 1111 右移的話仍是 1111 而非 0111.

 以下藉著「整數轉二進位」的自定義函式展現 BitShift 與 BitAND 的用法,並以此看看 BitOR, BitXOR 及 BitNOT 的效果。

 執行將發現輸出都很長串,不過 3 的後四位元是 0011, 5 的後四位元是 0101, 也就是之前解釋時所用的範例,因此讀者可以檢查後四位元是否與解釋中完全一致,而其他位元因為都是

0, 只要參考右邊數來第四位元雙方都 0 時得到的答案即可。

 IntToBase2 函式中初始化 $i = 1, 接著在迴圈裡 $i = BitShift($i, -1), 因為向左位移必在最後補 0, 所以一直向左移動到最後一定會變成所有位元都 0, 於是離開迴圈。

 1 的巧妙之處是它的二進位中只有最後一位是 1 其他皆為 0, 若一直向左位移,這單獨的 1 位元將依序出現在 $i 的最右到最左位元。BitAND 時,$i 提供 0 的地方無論 $n 對應位元為何,結果都是 0, 唯一不確定的只有 $i 提供 1 的地方,會依照 $n 對應的位元有所差異,更精準地說是靶 $n 對應位元的值「複製」過來當成答案。

 因此,如果 BitAND 結果為 0, 表示 $n 被指定的那個位元是 0, 反之則 $n 的該處是 1, 分支內即按照此結果添加字元到 $ans.

 下表簡列 IntToBase2(3) 的執行狀況。為了不撐開版面,以 ... 表示中間連續 27 個 0.

$i$nBitAND($n, $i)$ans
0...00010...00110...0001"1" & ""
0...00100...00110...0010"1" & "1"
0...01000...00110...0000"0" & "11"
0...10000...00110...0000"0" & "011"
1...00000...00110...0000"0" & "0000000000000000000000000000011"

 註 1: 數值上,左移數值變兩倍,右移數值減半,然而本文以位元運算為主,不強調二進位與十進位間的關係。

 註 2: 其實往左或往右位移概念雷同,但對於整數的「有號」 (signed) 與「無號」 (unsigned) 表示法而言,往右位移時最左邊的位元補 0 或 1 效果將大不同,因此又有「算數位移」和「邏輯位移」二種,分別只補 1 和補 0 的情形。


 「旗標」的概念就是將資料內某些位元賦予特殊意義,如同 MsgBox 範例中展現的,「旗標」可以單選也可複選。寫複選題的時候,若有五個選項 A 到 E, 10000 表示只選 A, 11000 表示複選 AB, 11111 表示全選,依此類推。當想知道 A 是否被選,即把作答與 10000 做 BitAND, 結果不是 00000 就是 10000, 可用結果是否為 0 判斷 A 有無被選到。

 把五個位元 XXXXX 解讀成五個選項 10000, 01000, 00100, 00010, 00001 的任意組合,就是旗標裡的複選題。剛才函式中不斷 BitAND 的行為,可想成 $n 是一個 32 位元的旗標複選題,$i 把每個選項都當過一遍,BitAND 就是測試 $n 有沒有包含該選項。

 當想要做出包含 AB 二選項的旗標,即可使用 BitOR, 因為「或」是只要一方提供 1 就會讓答案為 1, 所以 10000(A) 與 01000(B) 的BitOR 結果為 11000, 跟 A, B 認一方 BitAND 都不是 0, 但跟 00100(C) 的BitAND 結果就是 0.

 值得注意的是,其實旗標的值之間也可用 + 取代 BitOR 來組合出旗標。然而 + 的計算成本高於 BitOR, 對於工作量不大的 AutoIt 腳本而言兩者無顯著差別,但在其他應用中講求效能,就必須確實以「或」來執行。

 通常資料的表示,也不會總如剛才旗標複選題那樣單純,而是有欄位之分,每個欄位有自己的有效值,不同欄位間互相獨立,以 BitOR 串聯起來。其實旗標複選題是一個特例,每個欄位都只有一個位元,容納兩種有效值 0 跟 1. 當欄位的寬度為二位元以上,就變成旗標裡的單選題。

 以 MsgBox 的按鈕選擇為例,它利用整數 32 位元中最右邊三個位元記錄按鈕的選擇。三個位元可以表達八種 (2*2*2) 不同的值,不過不一定全部的可能值都會被使用,像按鈕的組合就只有七種。

 以下用一個例子示範取出整數的最右邊三個位元,其中引用範例 12-2 的 IntToBase2 函式。

 若輸入 15, 第一次 MsgBox 將顯示 0...01111, 除最後四位元 1 外其他皆為 0, 第二次 MsgBox 將顯示 0...0111, 變成只有最後三個位元是 1. 再測試一個 -3, 兩次 MsgBox 分別顯示 1...1101 與 0...0101.

 無論輸入何值,第二次 MsgBox 顯示的左邊 29 位元一定都是 0, 右邊三個位元的輸出會依據輸入數字的右邊三位元而定。只有最右邊三個位元會隨輸入而變,就是「取出」它的最右邊三個位元,取出來以後則可根據八種不同內容決定要做的事情。
 好奇的讀者可以測試看看,將程式碼裡的 7 改成 14, 就可以抓到右邊數來第二到四位元,改成 28 就是右邊數來第三到五位元。觀察 7 的二進位會發現它就是剛好只有最右邊三個位元是 1, 任何輸入與它 BitAND 時答案的左邊 29 位元都因為 7 提供了 0 而必然為 0, 只有右邊三個位元因為 7 提供 1 而必須再看 $n 提供的內容而定。

 為了取出輸入的某些位元而決定的常數稱為「遮罩」 (mask), 遮罩裡是 0 的地方表示那邊被「遮住」,只取出遮罩裡是 1 的地方,輸入對應處的位元。遮罩裡的 1 位置也不一定要連續,不過多數習慣上會安排連續位置。而「複選」的狀況如 $MB_HELP, 它只有一個位元表示要或不要,但也是「遮罩」的特例,只留下輸入的那一個位元,$MB_HELP 本身又當成這個複選題的選項值,換言之有寫 $MB_HELP 就是有選。

 一個經典的應用實例是色彩的表示,在一個 32 位元的像素 (pixel) 內,有 R (Red), G (Green), B (Blue) 三原色及不透明度 Alpha, 各自佔有八個位元。當使用 32 位元的整數來儲存一個像素時可以這樣規劃:從左邊數來前八個位元儲存 R, 之後八個位元儲存 G, 再過去八個位元儲存 B, 最右邊八個位元儲存不透明度。不透明度的遮罩是 255, 也就是右邊八位元是 1, B 的遮罩是 BitShift(255, -8), 意思是把這八個 1 放在右邊算來第九到十六位元處,G 跟 R 的遮罩則分別是 255 左移 16 與 24 位元。

 註:許多腳本像 AutoIt, AutoHotKey, 按鍵精靈等都很方便獲取視窗內的像素值,這種技巧可以用來製作遊戲外掛及圖形使用者介面的自動化測試,此技術不在本文範圍內。


 結語:

 我們可以把整數理的位元設計成單選與複選的欄位,用遮罩來定義要參考哪幾個位元的值。單選題通常會像 MsgBox 一樣又用常數表示每種選項,複選題的遮罩本身就是選項的值。

 使用時,把每個欄位的答案用 BitOR 組合成最後答案,函式內部將用遮罩透過 BitAND 取出指定欄位的位元,根據遮罩後的結果採取行動。


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


本文張貼者:Bo-Cheng Jhan〔張貼時間:民國105年6月5日(星期天)1點56分 | 更新次數 #2 | 最後更新:民國105年6月5日(星期天)16點57分〕

部落格首頁


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