2008年10月10日 星期五

寫了2個抽籤相關的小程式

主要因為無聊,就寫了二支小程式,當然是用AMS寫的。
一支是可以抽號碼的程式,用於上課可以抽學生→有興趣的下載
另一支是重排學生的座位用的,讓他們覺得公平→有興趣的下載

2008年7月12日 星期六

FTP工作時的二種連接模式

FTP中的兩種工作方式 :

防火牆有很多種,其中有一些會禁止那些不是從內部網路IP發出的連接請求。而ftp協議是個很老的東東,沒有考慮這個問題。可以這樣簡單描述一般ftp的工作原理:
client對server發出請求:「嗨,我的xxPort正等著你傳送資料呢,sever聽到這個請求,就會建立一個連接,指向這個Port,並且開始傳送資料。這就回到了我們的問題,如果發出請求的client在防火牆的後面,而這個防火牆禁止外部IP建立連接,那麼當然server(在外部)不能建立與client的連接。
所以,過了幾年,人們修改了ftp協議以應付這種情況。這就是passive mode,工作原理如下:client向server發出passive命令,server回答說:「資料在xxPort等你,自己來拿。」 而這回則由client建立指向server的連接,從而取得資料。這就避免了防火牆的限制。

  FTP是一種文件傳輸協議,它支持兩種模式,一種方式叫做Standard (也就是 Active,主動方式),一種是 Passive (也就是PASV,被
動方式)。 Standard模式 FTP的客戶端發送 PORT 命令到FTP server。Passive模式FTP的客戶端發送 PASV命令到 FTP Server。

接著下面,介紹一個ftp,這兩種方式的工作原理:
  Standard模式FTP 客戶端首先和FTP Server的TCP 21Port建立連接,通過這個通道發送命令,客戶端需要接收資料的時候在這
個通道上發送PORT命令。 PORT命令包含了客戶端用什麼Port接收資料。在傳送資料的時候,服務器端通過自己的TCP 20Port發
送資料。 FTP server必須和客戶端建立一個新的連接用來傳送資料。
  Passive模式在建立控制通道的時候和Standard模式類似,當客戶端通過這個通道發送PASV 命令的時候,FTP server開啟一個
位於1024和5000之間的隨機Port並且通知客戶端在這個Port上傳送資料的請求,然後FTP server 將通過這個Port進行資料的傳送,
這個時候FTP server不再需要建立一個新的和客戶端之間的連接。
  現在的FTP軟體裡面包括在IE5以上的版本裡面也已經支持這兩種模式了。一般一些FTP客戶端的軟體就比較好設置了,一
般都有一個PASV的選項,比如CuteFTP,傳輸的方式都有Standard和PASV的選項,可以自己進行選擇;另外在IE裡面如果要設
置成PASV模式的話可以選中工具-Internet選項-進階-為FTP站點啟用資料夾視圖,否則就採用Standard模式。
  很多防火牆在設置的時候都是不允許接受外部發起的連接的,所以FTP的Standard模式在許多時候在內部網路的機器通過防
火牆出去的時候受到了限制,因為從服務器的TCP 20無法和內部網路的客戶端建立一個新的連接,造成無法工作。當然也可以
設置成功,首先要建立一條規則就是允許內部的IP連接外部的IP的21Port;第二條就是禁止外部IP的TCP 20Port連接內部IP的
<1024的Port,這條是為了防止外部連接內部的一般Port;第三條驗證ACK是否等於1,這個的原理就參見TCP建立連接的三次握
手吧。所以如果安全的配置的話非常困難,這個時候就想起來了PASV模式,因為不用建立新的連接,所以也就不會涉及到後
面的問題了。但是管理員可能不想使用PASV模式,因為這個時候FTP Server會開放一個隨機的高Port,儘管在IIS4和IIS5里面Port
的範圍是1024-5000,但是許多FTP Server的Port範圍達到了1024-65535,這個時候在這個主動開放的隨機Port上是有完全的訪
問權限的,如果IIS也要設置成開放的Port為1024-65535,具體方法如下:
1. regedt32
2. 找到HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters
3. 編輯-新增-數值

Value Name: MaxUserPort Data Type: REG_DWORD Value: 65534
  所以如果遇到了有防火牆的話或者怕配置麻煩的話還是採用PASV模式比較好些,但是如果真的對安全的需求很高的話建
議採用Standard模式。

轉字串為table的函數

在程式中,你可以呼叫這個函數來將字串轉為table,當然您的字串是規格化的,比如您有一個字串→str=" coffee,spring,worm,tea"(是用逗號來分隔的字串內容),那麼你可以在程式中寫:tb_back = DelimitedStringToTable(str, ",");
你可以得到一個table名為tb_back,而其內容就是四個字串。
function DelimitedStringToTable(DelimitedString, Delimiter)
tbReturn = {};
local strWorking;
local nPos = nil;
local strData;
local nTableIndex = 1;
local nDelimiterLength = String.Length(Delimiter);

if(nDelimiterLength < 1)then
tbReturn[nTableIndex] = DelimitedString;
return tbReturn;
end

strWorking = DelimitedString;
nPos = String.Find(strWorking,Delimiter);
while(nPos ~= -1)do
strData = String.Left(strWorking,nPos-1);
tbReturn[nTableIndex] = strData;
nTableIndex = nTableIndex + 1;
local nLength = String.Length(strWorking);
strWorking = String.Right(strWorking,nLength - (nPos + (nDelimiterLength-1)));
nPos = String.Find(strWorking,Delimiter);
end
if(strWorking ~= "")then
tbReturn[nTableIndex] = strWorking;
end

return tbReturn;
end

Script-Tip011其他內建涵數

*dofile
AMS的程式引擎完全支援LUA程式程言,您可以將程式寫在外面存成*.lua(純文字檔),在您的

AMS程式中再以dofile將外部程式檔引入及執行。
語法:

dofile(lua檔路徑);

您可以用這種方式來幫助您整理及分類龐大的程式碼,每執行一次上述的語法,路徑中的lua

檔便會重新讀入並執行一次。

*require
與dofile一樣是引入外部的lua檔並執行,不同的地方在於,程式啟動後,以require引入的lua檔

只會執行一次,並存於記憶體中,就算您在某個event中再require一次,程式引擎也不會重讀

路徑中的lua檔。
語法:

require(lua檔路徑);

※require很適合您將變數的初始化或專案會用到的涵數寫在lua然後引入,這樣可避免大量的

變數式及functions寫在global script中,程式碼多到您自己眼花。

※只要您將您要引入的外部lua檔,放入專案的Modules目錄中,您在寫路徑的時候就不必寫完

整路徑,只要寫檔名就行了,這適用於dofile及require
例如:您寫了一個外部檔test.lua並放入專案的Modules目錄中,那您引入的語法:

dofile("test.lua");

require("test.lua");

*type
這個內建涵數是讓您用來取得一個變數所帶的資料型別的,語法:

my_var = type(變數);

依資料的不同,它會傳回六種字串名:"nil","number","string","boolean","table","function"
例如:
a = 898;
my_var = type(a); --my_var會得到"number"這個字串值

b = "coffeeworm";
two_var = type(b); --two_var會得到"string"這個字串值

※在程式實務中,有時您必須確認一下變數的型別是不是您所想要的,以避免出錯。
範例:


code:--------------------------------------------------------------------------------
function Max(n1, n2)
if (type(n1)~= "number") or (type(n2)~= "number") then --利用這行來判斷是否二個參數都是數字
Dialog.Message("錯誤","二個都必須輸入數字");
return nil;
else
if n1 > n2 then
return n1;
else
return n2;
end
end
end

Script-Tip012程式的除錯

*寫程式出現錯誤是常發生的事,當您的程式越寫越多,就愈容易犯邏輯錯誤,譬如重覆定義了變數,或符號打錯了,有時為了找一個小錯誤會花上您一天的時間。
AMS提供了很完善的除錯機制,讓您在程式執行錯誤時可以很快地找到錯誤的地方。所有在AMS中的Script都使用同一個除錯技術。
不過有一點是您必須了解的,在有些function,modules或script並不必然能除錯的,尤其是一些外界開發者所寫的用於AMS的程式,可能有它們自己的除錯方式,您必須要自行去讀取這些作者的文件,以了解如何發現錯誤。
※您所寫的程式可能千變萬化(程式語言彈性本來就很大),程式引擎通常只能捕捉明顯的寫法錯誤,其他您就不要太苛求了!

在AMS中,當您執行程式時,程式引擎所能捕捉的錯誤有二類: 語法錯誤和功能性錯誤

*語法錯誤
這類型的錯誤,通常是您的程式寫法有缺損或錯誤,在您預覽或建立(build)您的程式時會被捕捉到。例如:

foo =

上列的程式碼算是很明顯的式子不完整,定義了變數卻沒有指定值給它。
語法錯誤還有一種是您使用內建涵數,但卻沒有傳入規定的參數個數,不過這種錯誤在預覽及建立程式時,不會被捕捉到,直到執行期才會出現錯誤。
例如:

Dialog.Message("大家好");

如上式,使用訊息對話盒的涵數,至少要傳入二個參數(可參閱各action說明),但只傳入一個,在預覽或建立時都不會捕捉錯誤,因為程式引擎檢查了涵數名稱沒有錯,括號也沒少,也有";"當結尾,所以就不認為有錯,不過這行程式在執行時便會出現錯誤訊息說明少了參數。
※如果您參考一下使用手冊中,Dialog.Message的涵數模型如下:


code:--------------------------------------------------------------------------------
number Dialog.Message ( string Title,
string Text,
number Type = MB_OK,
number Icon = MB_ICONINFORMATION,
number DefaultButton = MB_DEFBUTTON1 )
--------------------------------------------------------------------------------

上述的涵數模式是述說Dialog.Message這個內建涵數需要5個參數傳入,前二個要字串,後三個要數字,不過後三個有預設值,所以您不寫的話就用預設值,前二個就不能省了。

*功能性錯誤
有時候您使用內建涵數去執行某些動作,但給的參數錯了(如打錯字),就會出現這類的錯誤,在建立程式時不會捕捉到錯誤(語法沒錯),而且在執行期,AMS預設不會跳出任何錯誤訊息,例如:

filecontents = TextFile.ReadToString("geskdf''' exist.txt");

要讀入某文字檔,但檔名打錯了,在執行期雖出錯,但程式不會有任何動靜,又如下:

Folder.Delete("c:\\temp");

這行程式是要刪掉c:\temp這個目錄,如果這個目錄不存在,那就發生錯誤,但這類錯誤也不會顯現出任何動靜。
想要捕捉這類的錯誤,您可以靠自己寫程式(利用內建的Debug Action)來捕捉,再自行決定要不要出現錯誤警告,或利用程式來避免。

*Debug Action
使用Application.GetLastError這個內建涵數來了解最近的一個Action的執行結果,用以判斷某一行程式的執行是否成功。
它的寫法公式:
last_error_code = Application.GetLastError();
範例:

code:--------------------------------------------------------------------------------
File.Copy("c:\\temp\\test.dat", "c:\\temp\\text2.dat");
error_code = Application.GetLastError(); --這行可以捕捉上一行的copy的結果的傳回代碼
if (error_code ~= 0) then
Dialog.Message("錯誤", "複製檔案錯誤!");
Application.Exit(); --離開程式(內建action)
end
--------------------------------------------------------------------------------

上面的程式很直覺,AMS的程式引擎規定,任一action的執行如果成功就會傳回 0,若出錯就會傳回某特定的數字代碼(像是1182之類的),所以上面的程式去判斷error_code 這個變數如果不是0,那顯然是copy那行出錯了,便跳出個錯誤訊息給使用者看,接著就退出程式。
※如果您想了解程式引擎所捕捉到的程式錯誤說明,可如下:

code:--------------------------------------------------------------------------------
File.Copy("c:\\temp\\test.dat", "c:\\temp\\text2.dat");
error_code = Application.GetLastError(); --這行可以捕捉上一行的copy的結果的傳回代碼
if (error_code ~= 0) then
Dialog.Message("錯誤", "複製檔案錯誤!說明:" .._tblErrorMessage[error_code]);
Application.Exit(); --離開程式(內建action)
end
--------------------------------------------------------------------------------

上面的式子中_tblErrorMessage[錯誤代碼],這是內建讓您查錯誤說明的Tables,這裡所介紹的是很實用的作法,通常都是用Application.GetLastError()搭配Dialog.Message可以讓您在寫程式時,自覺比較容易出錯的地方,在設計期寫進去幫自己除錯,沒問題了再移掉(放著也行)。
※如果您連續執行了幾個action其中一個出錯,但您的Application.GetLastError()寫在一個對的action後面,那便捕捉不到錯誤了,這點應注意!

*使用Debug.ShowWindow
AMS內建了一系統以Debug開頭的Action,可以讓您在程式執行時出現一個報告執行過程的視窗,這也是一個除錯的利器!
使用法:
Debug.ShowWindow(true);
執行這一行後便會跳出一個視窗,接下來用Debug.Print輔助,便可顯示之後所有程式運行的過程,幫自己檢查運用情形。
執行下一行, Debug視窗就會關掉。
Debug.ShowWindow(false);

若您使用Debug.SetTraceMode(true);這個action的話,便會出現詳細的程式運行情形,利用Debug.GetEventContext()這個內建涵數,您可以取得出錯點是在哪個Event,否則物件一多,涵數一多,您可能會找不到。

Script-Tip010字串的操作

*連接字串:您可以利用 .. 這個運算子來連結二個字串,或者連結字串和數字,數字會轉為

字串。


code:--------------------------------------------------------------------------------
Fullname = "Coffee".." worm";
--這時Fullname的值為"Coffee worm"
d = 365;
text = "一年有"..d.."天";
--這時text的值為"一年有365天"
--------------------------------------------------------------------------------

※為了程式閱讀方便,.. 並不一定要緊靠著字串或變數
foo = "test"..var;
寫成
foo = "test" .. var;也可以

*字串的比較
在寫程式的時候,經常會需要比對二個字串是否一樣,AMS內定使用 == 這個符號作為比對

符號,中文的話沒什麼問題,比對英文時預設是大小寫視為不同的(二個英文字串必須大小寫

都一樣,字串才會相等),比對的結果為布林值。
範例:

code:--------------------------------------------------------------------------------
str_one = "CoffeeWorm";
str_two = "CoffeeWorm";
if str_one == str_two then
Dialog.Message("比對結果", "二個字串相同");
else
Dialog.Message("比對結果", "二個字串不同");
end
--------------------------------------------------------------------------------

上例會顯示二個字串相同

※如果您希望比對時不要管字母大小寫,您可以將欲比對的字串先套用內建涵數Strng.Upper(

轉為大寫)或String.Lower(轉為小寫)再進行比對。
範例:

code:--------------------------------------------------------------------------------
str_one = "coffeeworm";
str_two = "CoffeeWorm";
if String.Lower(str_one) == String.Lower(str_two) then
Dialog.Message("比對結果", "二個字串相同");
else
Dialog.Message("比對結果", "二個字串不同");
end
--------------------------------------------------------------------------------

上例會顯示二個字串相同

*計算字串長度(含有多少個字元)
只要使用內建涵數String.Length就可以傳回字串的長度了

code:--------------------------------------------------------------------------------
str_text = "我是 Coffeeworm";
num_chars = String.Length(str_text);
Dialog.Message("字元數", num_chars.."個字");
--------------------------------------------------------------------------------

※特別注意,中文字是雙位元碼,所以一個中文字會被算二個字元,字串中的空白鍵也會照

算,所以這個例子會回應 「15個字」

*搜尋字串
有時候您會需要在一串長字串中搜尋一下是否含有指定的字串,這只要使用內建涵數

String.Find就行了

code:--------------------------------------------------------------------------------
str_source_text = "Isn't it a wonderful day outside?";
str_search = "wonder";
found_pos = String.Find(str_source_text , str_search);
--如果有找到,會傳回找到字串的最左邊字元的位置,若找不到會傳回nil
if found_pos ~= nil then
Dialog.Message("找到了", "在第"..found_pos.."個字");
else
Dialog.Message("找不到", "不包含這個字串");
end
--------------------------------------------------------------------------------

※中文字也是一樣可以找的,AMS是完全相容於雙位元碼的

*置換指定字串
想在長字串中將所有的某個字換掉可以使用內建涵數String.Replace

code:--------------------------------------------------------------------------------
str_target = "There can be only one. Only one is 合格的!";
str_search = "合";
str_place = "正";
str_new = String.Replace(str_target, str_search, str_place)
Dialog.Message("新字串", str_new);
--------------------------------------------------------------------------------

上例中,str_new便是得出的新字串,原字串中的「格」已被換成「正」了

*分解字串
有時候,我們想取得長字串的某些位置的字元,有三個內建的涵數可以用
String.Left 從字串的左側取得指定數目的字元
String.Right 從字串的右側取得指定數目的字元
String.Mid 指定從字串的第幾字元開始取,取幾個字元
範例:

code:--------------------------------------------------------------------------------
source_text = "It really is good to see you again.";
strleft = String.Left(source_text, 13);
strright = String.Right(source_text, 18);
strmid = String.Mid(source_text, 14, 4);
--------------------------------------------------------------------------------

上例中:
strleft 會是"It really is ",strright 會是" to see you again.",而strmid 會是"good"

*將字串型的數字轉成數字型
當您擁有一個字串型的數字時,您必須利用內建涵數String.ToNumber將它變成數字型態,然

後用於判斷式時才不會出錯。
例如:

code:--------------------------------------------------------------------------------
age = "18";
if age < 31 then
Dialog.Message("test", "比31歲小")
end
--------------------------------------------------------------------------------

上述的程式執行到if age < 31 then這一行時,就會出現錯誤了,因為程式引擎會認為字串和數

值是不能比大小的!
※在實務中,何時會有得到字串型數字的情形呢?譬如,您放了一個InputBox讓使用者輸入數

字,然後您去取得使用者輸入的數字,因InputBox預設取回的是「字串」,所以您便得到了一

個字串型數字。
要解決這個問題,如下:

code:--------------------------------------------------------------------------------
age = "18";
if String.ToNumber(age) < 31 then
Dialog.Message("test", "比31歲小")
end
--------------------------------------------------------------------------------


※還有一個方法:AMS內定,當字串型數字做運算時,就會自動轉換型別了,所以您可以讓

您所握有的字串進行一個數學運算

code:--------------------------------------------------------------------------------
age = "18";
age = age + 0
if age < 31 then
Dialog.Message("test", "比31歲小")
end

Script-Tip009關於functions的使用

涵數是AMS中很有用的功能,分為內建涵數及自訂涵數,像Dialog.Message即為內建涵數,您只要提供所需的參數給它,它便為您呈現對話盒,AMS的內建涵數非常的多,只要熟悉他們的用法,您就可以做大部份的事了,當然您也可以自行定義您所需要的涵數,稱為自訂涵數,您需要按照下列的程式模型來定義:

function 涵數名(參數)
欲執行的程式
return 值;
end

以function關鍵字為首,以end結尾,參數是可有可無的,視您的需要,之中包著您的程式碼,若使用return關鍵字,則可讓涵數的執行有「傳回值」。
下面是一個簡單的自訂涵數範例:


code:--------------------------------------------------------------------------------
function Helloworld()
Dialog.Message("Welcome" , "Hello World !!!");
end
--------------------------------------------------------------------------------

上面是一個沒有參數,沒有傳回值的涵數,名為Helloworld(),您可以在任何地方呼叫它,只要在程式上打入下列這一行:

Helloworld();

如此就會呼叫這個涵數並執行其程式碼,會出現一個對話盒(標題為Welcome,文字為Hello World !!!)
※有一件要特別注意的是:在您呼叫某個涵數之前,必須確定它已經在記憶體中被初始化(即涵數已被執行過),所以我們通常會將自訂涵數集中寫在Globals Functions中,如此當程式啟動時便可確保這些涵數都已初始化了。

*涵數的傳入值(參數)
以下範例:

code:--------------------------------------------------------------------------------
--定義一個含有一個參數的涵數
function HelloWorld(a1)
Dialog.Message("title", a1);
end
--在其他地方呼叫它
HelloWorld("大家好!");
--------------------------------------------------------------------------------

如同上面的範例,執行的結果會出現一個對話盒,標題為title,文字列為 大家好!
當我們在定義涵數時,指明了這個涵數必須含有一個參數(傳入值),所以呼叫的時候就必須寫入您要傳入的字串或數字才不會出錯。上例在呼叫涵數時傳入了「大家好!」這個字串,意思便是以「大家好!」這個字串代表a1這個變數在涵數中的程式碼使用。
當然傳入值理論上可以定義無限多個,就看您的需要了,如下例:

code:--------------------------------------------------------------------------------
--定義一個含有2個參數的涵數
function HelloWorld(a1,a2)
Dialog.Message(a1, a2);
end
--在其他地方呼叫它
HelloWorld("大家好!","世界和平!");
--------------------------------------------------------------------------------

上面的程式碼定義了涵數有二個參數,所以呼叫時便傳入二個字串為參數(以逗點隔開),參數會依次序引入,用「大家好!」代表a1,用「世界和平!」代表a2

*涵數的傳回值
有時候我們寫一些涵數來做特定的事,並希望它將執行的結果(稱為傳回值)傳回給呼叫它的程式,以便繼續運用它執行的結果,如下例:

code:--------------------------------------------------------------------------------
--定義一個涵數,有2個參數,且有一個傳回值
function BMI(x,y)
bmi = y/(x*x);
return bmi;
end
--呼叫
my_var = BMI(1.65, 60);
--------------------------------------------------------------------------------

上面的程式中,特別注意的是傳入二個參數後,涵數內部會用這二個參數做運算,並用return關鍵字傳回計算的結果給呼叫它的式子,所以my_var的值便是計算後的結果。

※多重傳回值

code:--------------------------------------------------------------------------------
--定義一個涵數,有2個參數,且有2個傳回值
function sortNumbers(number1,number2)
if number1 <= number2 then
return number1, number2;
else
return number2, number1;
end
end
--呼叫
first_num, second_num = sortNumbers(number1,number2);
--------------------------------------------------------------------------------

注意上面程式中的呼叫語法,因為有二個傳回值,所以定義二個變數來接收

※重新定義涵數內含的方法很簡單,只要您在程式的其他地方用同一個名稱來定義涵數,那原本同名的涵數就會被覆蓋。

※將涵數放入Tables中
如果您想寫一個大型的程式,用到很多自訂的涵數,您可以考慮將涵數一個個放入Tables中,以便統一管理。
範例:

code:--------------------------------------------------------------------------------
--涵數一
function sun()
Dialog.Message("title", "我是太陽!");
end
--涵數二
function moon()
Dialog.Message("title", "我是月亮!");
end
--建一個空Tables
Hello = {};
--將涵數放入Tables中
Hello.s = sun;
Hello.m = moon;
--呼叫的方法
Hello.s();
Hello.m();

Script-Tip007結構化程式控制

AMS的程式引擎接受的程式控制為 if , while , repeat , for
*if 的語法:

if 條件式 then
--欲執行的程式
end

if , then , end是語法中的關鍵字,一定要使用的,如果條件式成立(即true)那麼if和end之間的程

式碼才會被執行
例如:


code:--------------------------------------------------------------------------------
x = 50;
if x > 10 then
Dialog.Message("title","文字對話盒");
end
--------------------------------------------------------------------------------

上面的例子中,對話盒那行程式是會被執行的,因為條件式「x>10」是成立的。
條件式也可以是複雜的,如下:

code:--------------------------------------------------------------------------------
y = 3;
if (y+7)*10 == 100 then
Dialog.Message("title","文字對話盒");
end
--------------------------------------------------------------------------------

在 if 控制式中也予許加入 else 和 elseif 等關鍵字來增加分支判斷式,如

code:--------------------------------------------------------------------------------
x = 100;
if x < x =" 5;" x ="="" x="="" x="="8" a =" 1;" a =" a+1;" a =" a+1;這一行程式會被執行9次,所以最後a的值為10" count =" 1;" count =" count+1;" count ="="" count =" 1;" count =" count" count ="="">100
--------------------------------------------------------------------------------

*For 迴圈
當您很確定所要執行的迴圈次數,使用for便是一個好選擇,其程式模型如下:

for 變數 = 開始數, 結束數, 步進值 do
程式區塊
end

以for關鍵字開始,以end作為結尾,其中「變數」可以隨意取名,如a,i,x,y......,它只是用來計

數用的,「開始數」代表「變數」的初始值,「步進值」是一個數字,它的意思是每迴圈一

次,變數目前的值便加上「步進值」,再與「結束數」比對,直到條件不成立(變數值大於或

小於結束數),「步進值」不寫的話預設是 1
在程式區塊中一樣可使用break關鍵字跳出迴圈。
以下的範例都是常見的:

code:--------------------------------------------------------------------------------
--demo 1
for x = 1, 10 do
Dialog.Message("test", x)
end

--demo 2
for x = 10, 1, -1 do
Dialog.Message("test", x)
end

--demo3
for x = 1, 100 do
Dialog.Message("test", x)
if x == 50 then
break;
end
end
--------------------------------------------------------------------------------

Script-Tip008關於Tables(Arrays)

在AMS中Tables是一個特別且非常有用的資料型別,您可用它來存放各類型的資料值,包含function或其他Tables
*建立Tables
有二種方法可以在程式中建立Tables第一種方法是用{ }將元素值包含在裡面,如下:
my_tables = {"元素一","元素二","元素三"};

my_tables = {name = "coffee", tel = "06-2841700"};

※Tables本身有二種陣列(Arrays)類型,像上面第一種是「數列式」,第二種是「關聯式」的。
第二種建立Tables的方法,是先建一個空的Tables再加入元素,如下:

my_tables = {};
my_tables[1] = "元素一";
my_tables[2] = "元素二";
my_tables[3] = "元素三";

my_tables = {};
my_tables.name = "coffee";
my_tables.tel = "06-241700";

上面的說明中第一種寫法是針對數列式的Tables,第二種是針對關聯式的Tables

* 存取Tables中的元素(存在Tables中的資料,每一筆以 , 分開)
儲存在Tables中的資料稱為「元素」,每個元素都含有一個key(鍵),這個key就關聯著它所代表的值,而這個key就是我們所謂的「索引(index)」。在AMS中Tables包含二種列陣,一為數列型陣列,它的index是數列組成,由1開始,另一類是關聯型的陣列,它的index可以是字串、數字、涵數。
※數列型陣列
--這個Tables中含有四個元素,值是e1,e2,e3,e4,它們的index是1~4
n_table = {"e1","e2","e3","e4"};
--存取其中元素的方法→ Tables名[第幾個元素],如n_table[2]代表第二個元素的值
Dialog.Message("table test", n_table[2]);
--這會顯示對話盒,其中的文字為 e2

※關聯型的陣列
--這個Tables中含有三個元素,值是one, two, three ,index分別為first, second, third
a_table = {first = "one", second = "two", third = "three"};
--存取這類陣列要利用「點運算子」,a_table.second 代表第二個元素的值
t = a_table.second
--這表示指定"two"給 t 這個變數
下面是一個較複雜的Tables的範例


code:--------------------------------------------------------------------------------
a = {}; --定義一個空的陣列
--定義二個關聯式陣列,分別儲存二個人的資料
b1 = {name = "王小明", sex = "男 ", age = "10"};
b2 = {name = "陳小美", sex = "女 ", age = "9"};
--再分別將二個陣列存到a陣列裡
a[1] = b1;
a[2] = b2;
--想要取得陳小美的年齡
Dialog.Message("title", a[2].age);
--------------------------------------------------------------------------------


*用For 迴圈列舉Tables中所有的元素
有一個很特別的迴圈可以用來將Tables中的所有元素一個一個提出來,其程式模型如下:
for index,value in table do
--運用元素的程式碼
end

for in do是關鍵字,迴圈以end作為結束,index及value是取二個變數名稱來代表目前迴圈所到達的元素的索引值及字串值,table是您想讀的Table名稱
範例:

code:--------------------------------------------------------------------------------
my_t = {"one", "two", "three"};
for j , k in my_t do
Dialog.Message("table test", j.."="..k);
end
--------------------------------------------------------------------------------

上面的程式碼會跳出三次的對話盒,中間顯示的字分別為「1=one」,「2=two」,「3=three」

※另一個範例

code:--------------------------------------------------------------------------------
my_t = {one=1, two=2, three=3};
for k in my_t do
Dialog.Message("table test", k);
end
--------------------------------------------------------------------------------

上面的程式定義了一個關聯式的陣列,利用迴圈列舉,但只是想取得每個元素的索引,上面的程式執行後會跳出三次的對話盒,中間顯示的字分別為「one」,「three」,「two」,這裡有個問題便是為什麼顯示的順序不是按照tables中元素的排列順序,原因是因為使用關聯式的陣列的時候,是以「雜湊表」來儲存資料,所以順序會依某些規則重新排列,若您使用數列型的陣列,就不會有這種情形了。

*複製Tables
之前曾提過:您不能以 = 的方式來複製Tables,因為這樣並不能讓Tables成為二個複本,只是讓二個變數指向同一個Tables而已

code:--------------------------------------------------------------------------------
t_one = {one = "spring", two = "summer"};
t_two = t_one;
t_two.one = "winter";
Dialog.Message("table one" , t_one.one);
Dialog.Message("table two" , t_two.one);
--------------------------------------------------------------------------------

上面的程式會跳出二個對話盒,不過裡頭的文字都是 winter,這証明Tables只有一份,並沒有被複製出另一份

※若想真正複製出另一個Tables,寫法應如下:

code:--------------------------------------------------------------------------------
t_one = {one = "spring", two = "summer"};
t_two = {};
for i , v in t_one do
t_two[i] = v;
end
--------------------------------------------------------------------------------

如此t_two便成為一個獨立的Table且內含的元素和t_one一樣

*AMS中還有很多用於處理Tables的內建涵數

Script-Tip006程式語法與運算子

所謂的程式語法即是變數與值的評定方法,可能是單純的指定一個數值給變數,也有可能是

複合的運算式子的結果指定給變數,您也可使用括號來改變式子的運算順序,不然就如同我

們所學的數學一樣,電腦會依「先乘除後加減」的順序來運算一個式子。
以下都是可行的寫法:


code:--------------------------------------------------------------------------------
a = 10;
a = (5+1)*2;
a = 100/10;
a = 100/(5*2);
--------------------------------------------------------------------------------

除以上的例子外還有很多,自己想像吧!
*算術運算子
算術運算子專司數字的處理
+ 加法運算子
- 減法運算子
* 乘法運算子
/ 除法運算子
unary - 負數符號
※其實只要您學過數學方程式,這個部份就如同數學一般只是 = 號在電腦中不是「等於」的

意思,而是「將右側的運算結果指定給左側的意思」,這一點是要常常注意的。
*比較運算子
這種運算子提供您比較二側的變數或值的功能,其結果是以布林值來表示(true / false)
> 大於
< 小於 <= 小於等於 >= 大於等於
~= 比較二側是否不相等
== 比較二側是否相等
以下範例:

code:--------------------------------------------------------------------------------
10 > 20; --結果為false

a = 10;
a > 5; --結果為true
"Coffee" ~= "Spring" --結果為true
--------------------------------------------------------------------------------

這兒特別提到「字串」的比較,在AMS中字串是分大小寫的,所以二側的字串要一模一樣才

會相等。

code:--------------------------------------------------------------------------------
"coffee" == "coffee"; --結果為true
"Coffee" == "cOffee"; --結果為false
"Yes" ~= "yes"; --結果為true
--------------------------------------------------------------------------------


*邏輯運算子
這種運算子主要用於布林值的結合判斷。常常結合 if ....then 判斷式
and 二側的式子結果都為「真」才回復「真」
or 只要一側的式子為「真」便回復「真」
not 回復與式子結果相反的

code:--------------------------------------------------------------------------------
a = 8;
b = 3;
a+b > 10 and a-b <> 10; --這會回復「真」

if a > 5 or b > 5 then
--這裡寫的程式會執行
end
--------------------------------------------------------------------------------

*連接運算子
在AMS中規定以 ( .. ) 這個符號作為字串連接子,您可用它來連結二個或多個字串值成為單一

字串(string)

code:--------------------------------------------------------------------------------
a = "coffee";
b = a.."worm"; --b的值便是"coffeeworm"
c = b.."有"..10.."元"; --c的值便是"coffeeworm有10元 "
--------------------------------------------------------------------------------

*運算子間的優先順序
由低到高排列如下:
and or
< > <= >= ~= ==
..
+ -
* /
not -(unary)
^

Script-Tip005資料型別

*Number(數值)
在AMS中,Number代表所有的數字性的值,不像其他開發程式還將數值分為整數、單精數、

倍精數、.....,只有一類:就是數字,可容許的數值表示如下:
4 4. .4 0.4 4.57e-3 0.3e12

*String(字串)
所謂的String(字串)是指一連串的字元的集合,例如:"我的測試" 這個字串即是一個4個中文字

元的字串,若您的字串是英文,一個英文字母也是一個字元,這說明了AMS是支持雙位元編

碼的,所以您可以放心的使用中文字。
字串可以含有非常長的字元(甚至長至整篇文章),其中也可以包含空白字元,也可以包含合

法的8-bit ASCII字元或null(\0),您必須將欲表示的字串置於成對的 " " 或 ' '中,通常是使用

前者,在程式引擊方面,AMS會自動分配記憶體的使用,不需您操心。
例如以下的表示法:


code:--------------------------------------------------------------------------------
name = "Joe blow";
Lastname = 'worm';
Dialog.Message("Hello","我是咖啡虫!");
--------------------------------------------------------------------------------


有時候,您想在字串中顯示單括號(' ')或雙括號(" ")您可以如下的寫法:

code:--------------------------------------------------------------------------------
title = "小朋友,你們知道 '神木' 在哪裡嗎?" --這個字串可顯示 '神木'
title2 = '小朋友,你們知道 "神木" 在哪裡嗎?' --這個字串可顯示 "神木"
--------------------------------------------------------------------------------

當然我們還可以使用字元跳脫的方法:為了能順利地顯示一些系統內定有特殊意義的字元,

AMS規定以 \ 反斜線當作跳脫字元的定義符號。
以下是有特殊意義的跳脫符號:
\a 鈴聲
\b 退一格
\f
\n 新的一行
\r 按Enter
\t 垂直的tab
\v 橫向的tab
\\ 表示一個\
\" 表示"
' 表示'
\[ 表示[
\] 表示]

最常會用到的是\r \n \\
※如果您對ASCII字元表熟悉的話,跳脫符號也可以用ASCII來表示,例如寫\n和\10是一樣的

意思,不過我建議還是用原來的\n就好了。
如果您的字串很長,像是一篇文章很多行,那您可以如下寫法:

code:--------------------------------------------------------------------------------
text = [[ 第一行的字
第二行的字
第三行的字
..........
..............
最後一行]];
--------------------------------------------------------------------------------

如此text這個變數就存著那麼多的字,而且保留著您斷行的樣子!
※當字串遇上運算符號,如果您的字串可以被轉成數字,那麼AMS會自動把字串的部份轉成

數字進行字串,如:

code:--------------------------------------------------------------------------------
a = "13" + 5 --這個a的值會是18
b = "15" * 3 --這個b的值會是45
--------------------------------------------------------------------------------

不過請注意下列的範例:

code:--------------------------------------------------------------------------------
a = "13 + 5" --a的值便是"13 + 5"這個字串,+號在" "中也視為字串,所以不會計算
b = "您好" * 3 --這式子將會出錯,因為"您好"無法轉換成數字
--------------------------------------------------------------------------------


*Nil 無
這是一個非常特別的資料型別,它的意思就是空值,也就是變數中「沒有值」,您可以指定

一個「空值」給一個變數,這樣可以把它原有的值清空

code:--------------------------------------------------------------------------------
a = nil;
--------------------------------------------------------------------------------

在條件式子中,nil被視為「偽(false)」,如下例:

code:--------------------------------------------------------------------------------
a = nil;
if a then
--這裡的程式將不會執行,因為if條件式必須為「真(true)」才會執行
end
--------------------------------------------------------------------------------


*Booen布林值
這類型的的資料型態所帶的值只有二種,就是「真(true)」和「偽(false)」,這常用在判斷式中

做邏輯運算,如:

code:--------------------------------------------------------------------------------
a = true; --直接指定變數a為真
b = 10 > 12; --右邊比較的結果指定給b,這個b是偽
c = true;
d = false;
if (c and d) then --如果c和d做and運算,答案若為「真」便執行裡面的程式
--程式
end
---當然上面的例子,c and d的結果為false,and運算必需二方都為真,結果才為真
--------------------------------------------------------------------------------


*function 涵數
AMS予許您將一些程式碼集合成自定的一塊,稱為function,這個的用意主要是當您在寫一個

程式時,有一些程式可能常重覆,若每次都再抄一次實在多餘,您便可把這些程式集合成

function,需要時再呼叫function的名字就好了,如此這些程式就中要寫一次就好了。

code:--------------------------------------------------------------------------------
--自定一個名為coffee的涵數
function coffee()
--程式寫在這兒
end

coffee(); --呼叫涵數
--------------------------------------------------------------------------------


*Table 表
這是一個非常強大且有用處的資料型別,它可儲存大量「有索引的值」,若您有其他程式基

礎,您也可以想像,它就是「陣列(arrays)」,以下範例:

code:--------------------------------------------------------------------------------
--以大括號來定義一個名為Myarr的表,內含三個元素,元素間以 , 隔開
Myarr = {"一","二","三"};
--跳出對話盒並顯示陣列的第二個元素值
Dialog.Message( "title", Myarr[2] );
--------------------------------------------------------------------------------


另一個例子:索引未必是數字

code:--------------------------------------------------------------------------------
t_one = {}; --定義一個空的table
--在table中放入二個元素,並且使用文字式的索引
t_one.first = "Apple";
t_one.two = "Orange";
--利用文字式的索引取值
Dialog.Message( "title", t_one.two ); --想要顯示Orange
--------------------------------------------------------------------------------


另一個列子:

code:--------------------------------------------------------------------------------
--建立一個擁有5個元素的table
t1 = {"一","二","我的","範","本"};
t1[6] = "加入第六個元素";
--t1 這時有6個元素了
t2 = t1; --建一個table名為t2並把t1的內容指定給它
--請注意,上面的程式並沒有複製的意思,代表的是有二個不同名稱的變數(t1,t2)指向了同一--

個table
--------------------------------------------------------------------------------


※在我們指定值給變數時,我們使用的是「 = 」這個符號。

code:--------------------------------------------------------------------------------
a = 8;
b = "文字";
c, d = 6, 10; --6指定給c ,10指定給d

Script-Tip004變數型態及變數值

*動態型別處理
AMS的程式語言與其他如C語言不同的地方是:AMS採動態型別處理,定義及使用變數前並

不需要「宣告」「變數」的型別,變數的型別會自動隨著它所握住的值的資料型別動態地改

變。
這可讓程式開發者省了不少犯錯的機會,以往使用C或C++... 的程式員,常因為要記住變數目

前的型別而傷腦筋。
例如:在C++中要使用一個變數j且指定一個值10給它,要如下寫法


code:--------------------------------------------------------------------------------
int j; --這一行便是宣告j的型別是整數
j = 10;
--------------------------------------------------------------------------------

但是在AMS中您只要這樣寫:

code:--------------------------------------------------------------------------------
j = 10;
--------------------------------------------------------------------------------

AMS會視您放入變數中的值是什麼型別而動態地變化變數的型別。
所以您可以如下這樣寫也不會有錯:

code:--------------------------------------------------------------------------------
j = 10;
j = "這是測試";
--------------------------------------------------------------------------------

這種寫法在C++中,便會造成錯誤。
*根據上面所述,我們不用去討論變數的型別問題,只要關係資料有哪些型別就行了。
在AMS中,有六種資料型別:number(數值),string(字串),nil(無),boolean(布林值),function(涵數),table(表)

Script-Tip003關於變數

*什麼是「變數(Variables)」
以寫程式而言,「變數」是非常重要的,不管您使用何種語言,您都會用到,簡單地說,所謂的變數即是一個

「值(Values)」的代號或位置握柄,而這個「值」是可以變更和重覆使用的,例如:


code:--------------------------------------------------------------------------------
amount = 10;
--------------------------------------------------------------------------------

這個程式碼,便是定義一個變數名為 amount ,並且指定一個值→10,請注意,是「指定」不是「等於」,

amount 這個變數的值未必會一直都是10,您可以這樣想,變數是一個容器,而值便是放入容器中的東西,它隨

時可以被更換。
例如:

code:--------------------------------------------------------------------------------
amount = 10;
amount = amount + 20;
--------------------------------------------------------------------------------

上面程式碼的意思是說,定義一個變數叫做 amount,先指定它的值是 10,接著在第二行中,把amount中的值

拿出來加上20再放回amount中,所以程式執行到此,amount的值是30,這樣的式子在數學是不對的,但在程式

中卻是可行的。


code:--------------------------------------------------------------------------------
a = 10;
b = a;
--------------------------------------------------------------------------------

上面的程式的意思是定義一個變數叫做 a,先指定它的值是 10,再定義一個變數名為b,將「a的值」放入b中

,所以這時b的值也變成了10

*變數的範圍
在AMS中,變數預設都是全域的,意即:程式執行後,一旦某變數被定義,則這個變數便在整個程式中的所有

事件(Event)都可使用,不管在哪一頁(page)或哪個物件的事件,當然您必須能確定,在使用(呼叫)某個變數之前

,已定義了該變數,並指定一個值給它。
例如:在On Startup中的程式

code:--------------------------------------------------------------------------------
foo = 10;
--------------------------------------------------------------------------------

接下來在page1中放個按鈕,在按鈕的OnClick中鍵入:

code:--------------------------------------------------------------------------------
Dialog.Message("test",foo);
--------------------------------------------------------------------------------

上面的程式是要求顯示對話並在其中顯示foo的值。
※有個問題是,若您的程式越寫越大,page數也很多,上頭的物件也多,您是不是有可能不小心重新定義了之

前已定義過的變數名稱,那可能會造成意想不到的錯誤!
例如,您可能在某個地方需要寫個式子,所以如下:

code:--------------------------------------------------------------------------------
foo = 1;
text = "";
if foo < 50 then
text = text..foo;
foo = foo+1;
end
Dialog.Message("test",text);
--------------------------------------------------------------------------------

最後foo己經變成50了,但您可能不知道,還一直以為foo是10
所以如果您在某個地方的程式碼或函數中,需要有幾個變數是來計數的、迴圈的…,用完就要算了的,在定義

變數時,可以在前面加上 local 這個關鍵字,如:

code:--------------------------------------------------------------------------------
local t = 4;
local x = (t+1)*8;
--------------------------------------------------------------------------------

上面加上 local 的變數的效力就僅止於正在執行的event或function中,執行完就無效了,就不會有上面所說的困

擾了。
*變數的命名法則:英文、數字、底線的組合
1.一定要以英文字母開頭
2.變數名稱中不可以含有特殊字元% $ $ + 空格.........
3.不可以使用AMS的保留字
合法的命名例子
a
strName
_My_var
data1
bReset
tb_sut
tb_n_count_1

不合法的例子
2
7data
%vari
$strData
for
local
_F+r
User name

※系統保留字
and break do else elseif
end false for function if
in local nil not or
repeat return table then true
until while

Script-Tip002程式的重要概念

*AMS的程式碼是全域性的:
在runtime(執行期)時,程式引擊預設是認得您所定義的所有變數(varable)及函數(function)的,例如您在gobal

function中或On Startup中定義了一些變數值,例如:myvar = 5; 或函數如:
code:
________________________________________


function myfunction(a1,a2)

-----您的程式

end

________________________________________

之類的,在程式開始執行後,在任一個event中,這些變數或function都是可見的,可用的。
*在AMS中,變數及函數的命名是有分別大小寫的:
abc=5;

Abc=3;
這二個式子中,abc與Abc被視為二個不同的變數,同理,您命名函數時也要注意大小寫的問題。

*程式注解:您的程式不斷地發展會越寫越長,您最好養成為自己所寫的程式加上注釋,以便日後維護方便,

而所謂的注解,即是在程式碼中不會被執行的部份,僅是給程式員看的解釋。
在AMS中,定義某一行不執行的方法就是在該行的最前面加上 (--)兩個短線號,也就是說 -- 之後的字元都會被

視為注解而不會執行。如:
code:
________________________________________


--這是注解文字

MyVar = 6;

a = MyVar+3;

b = a; --注解文字也可以在這兒

________________________________________

多行注解可以使用 --[[ 和 ]]-- 來框住注解文字,如:
code:
________________________________________


--[[ 這是多行

注解的

範例 ]]--

a = 10;

________________________________________

*分行符號,在AMS中予許您使用( ; )分號來分開同一行的兩句敘述,以下範例都是可被接受的程式寫法。
第一種:
code:
________________________________________


a = 10

MyVar = a

________________________________________

第二種:
code:
________________________________________


a = 10; MyVar = a;

________________________________________

第三種:
code:
________________________________________


a = 10;

MyVar = a;

________________________________________

不過,建議以第二種或第三種寫法比較好,養成好習慣,在每個敘述的後面,加上分號。

Script-Tip001程式碼寫到那裡

在AMS中,程式碼可以寫的地方很多,主要寫在各物件的Action窗格,不同的物件又會有不同類型的Event(事件),而所謂的事件即是您可指定,在某個物件的什麼狀態下才執行所寫的程式碼。在物件導向的程式中,所有的東西都可視為物件,大到整個程式本身,或小至一個按鈕,AMS也不例外。
以整個程式而言有以下事件:
On Startup :在程式啟動的時候
On Shutdown :在程式關閉的時候
以Page(頁面:AMS的呈現單位)而言
On Show :在頁面呈現的時候
On Close: 在離開這個頁面的時候
On Audio :在這個頁面中的音樂軌的音樂播完時
On Timer: 當計時器啟動時
On Key: 當鍵盤被按下時
On Preload: 當頁面第一次載入記憶體時程式物件及頁面物件是很特別的,它們可能不需要使用者刻意以滑鼠去觸發,可以用來執行一些您想要在程式啟動時就執行的程式。
其他的物件所含的事件就大都由使用者的滑鼠來觸發,常見的是:
On Click: 當物件被按下時
On Double-Click: 當物件被雙按時
On Enter :當滑鼠移上物件時
On Leave: 當滑鼠離開物件時
其他事件還有:
On Focus :當物件成為焦點時→這在input及listbox上才有
On Finish :當影片播完時
On FSCommand: 當Flash有FSCommand時
On Loaded :當某個url載入完畢時
On Navigate: 當網頁物件的網址改變時
On Pause :當影片暫停時
On Play: 當影片開始播放時
On Stop: 當影片停止時
On Select: 當某個選項被選擇時:如listbox....
以上是大略的整理也講得差不多了,唯AMS是開放架構,不斷地有新的PLUG-IN被開發而加入新功能,新的PLUG-IN也可能會有特殊的事件,這就要自行參閱其說明文件了,一般來說PLUG-IN都是要額外付錢買的,一旦您買了,作者都會提供完整的說明書。

咖啡虫的網誌開張

網誌開張