天天看點

通過例子學習Lua

  發信人: jUYI (我很笨但我引以為榮), 信區: AnsiC

标 題: 通過例子學習Lua(1)zz

發信站: 瀚海星雲 (2004年12月17日01:59:30 星期五), 站内信件 WWWPOST

通過例子學習Lua(1) ---- Hello World

1.前言

遊戲中少不了用到腳本語言. Lua是一種和C/C++結合非常緊密的腳本語言,效率極高。

一般是對時間要求比較高的地方用C++寫,而經常需要改動的地方用Lua寫。

偶最近在學習Lua, 是以寫出心得和大家共享. Lua是一種完全免費的腳本語言,

它的官方網站在http://www.lua.org.在網站上可以下載下傳到lua的源碼, 沒有可

執行版本, 不過不用擔心, 因為lua源碼可以在任何一種C/C++的編譯器上編譯.

如果要學習Lua, 官方網站上的Reference是必備的,上面有每個指令的用法,非常詳

細。

參考手冊 http://www.lua.org/manual/5.0/

作者寫的Programming in Lua http://www.lua.org/pil/

2.編譯

如果用的VC, 可以下載下傳所需的project檔案,位址在

http://sourceforge.net/project/showfiles.php?group_id=32250&package_id=115604

偶用的是cygwin和linux, 打入以下指令即可,

tar -zxvf lua-5.0.2.tar.gz

cd lua-5.0.2

sh ./configure

make

這樣就OK了。

為了以後使用友善,最好把bin目錄加入到path裡面。

3."Hello, world!"

現在開始偶們的第一個小程式"Hello, world!"

把以下程式打入檔案e01.lua

例1:e01.lua

-- Hello World in Lua

print("Hello World.")

Lua有兩種執行方式,一種是嵌入到C程式中執行,還有一種是直接從指令行方式下執

行。

這裡為了調試友善,采用第二種方式,執行指令 lua e01.lua

輸出結果應該是:

Hello World.

4.程式說明

第一行 -- Hello World in Lua

這句是注釋,其中--和C++中的//意思是一樣的

第二行 print("Hello World.")

調用lua内部指令print,輸出"Hello World."字元串到螢幕,Lua中的字元串全部是

由"括起來的。

這個指令是一個函數的調用,print是lua的一個函數,而"Hello World."是print的參

數。

5.試試看

在Lua中有不少字元串的處理操作,本次的課後試試看的内容就是,找出連接配接兩個字元串

的操作,

并且print出來。

--

IP 位址: 已登入 來自: 已登入

第 2 樓

2005-06-27, 05:03 PM

buxiu

職務: 超級管理者

等級: 連長

注冊: 2005年6月26日

積分: 206

精華: 8

發貼: 72

通過例子學習Lua(2)

發信人: jUYI (我很笨但我引以為榮), 信區: AnsiC

标 題: 通過例子學習Lua(2)

發信站: 瀚海星雲 (2004年12月17日02:00:19 星期五), 站内信件 WWWPOST

通過例子學習Lua(2) --- Lua基礎

1. 函數的使用

以下程式示範了如何在Lua中使用函數, 及局部變量

例e02.lua

-- functions

function pythagorean(a, b)

local c2 = a^2 + b^2

return sqrt(c2)

end

print(pythagorean(3,4))

運作結果

5

程式說明

在Lua中函數的定義格式為:

function 函數名(參數)

...

end

與Pascal語言不同, end不需要與begin配對, 隻需要在函數結束後打個end就可以了.

本例函數的作用是已知直角三角形直角邊, 求斜邊長度. 參數a,b分别表示直角邊長,

在函數内定義了local形變量用于存儲斜邊的平方. 與C語言相同, 定義在函數内的代

碼不會被直接執行, 隻有主程式調用時才會被執行.

local表示定義一個局部變量, 如果不加local剛表示c2為一個全局變量, local的作用域

是在最裡層的end和其配對的關鍵字之間, 如if ... end, while ... end等。全局變量

作用域是整個程式。

2. 循環語句

例e03.lua

-- Loops

for i=1,5 do

print("i is now " .. i)

end

運作結果

i is now 1

i is now 2

i is now 3

i is now 4

i is now 5

程式說明

這裡偶們用到了for語句

for 變量 = 參數1, 參數2, 參數3 do

循環體

end

變量将以參數3為步長, 由參數1變化到參數2

例如:

for i=1,f(x) do print(i) end

for i=10,1,-1 do print(i) end

這裡print("i is now " .. i)中,偶們用到了..,這是用來連接配接兩個字元串的,

偶在(1)的試試看中提到的,不知道你們答對了沒有。

雖然這裡i是一個整型量,Lua在處理的時候會自動轉成字元串型,不需偶們費心。

3. 條件分支語句

例e04.lua

-- Loops and conditionals

for i=1,5 do

print("i is now " .. i)

if i < 2 then

print("small")

elseif i < 4 then

print("medium")

else

print("big")

end

end

運作結果

i is now 1

small

i is now 2

medium

i is now 3

medium

i is now 4

big

i is now 5

big

程式說明

if else用法比較簡單, 類似于C語言, 不過此處需要注意的是整個if隻需要一個end,

哪怕用了多個elseif, 也是一個end.

例如

if op == "+" then

r = a + b

elseif op == "-" then

r = a - b

elseif op == "*" then

r = a*b

elseif op == "/" then

r = a/b

else

error("invalid operation")

end

4.試試看

Lua中除了for循環以外, 還支援多種循環, 請用while...do和repeat...until改寫本文

中的for程式

--

IP 位址: 已登入 來自: 已登入

第 3 樓

2005-06-27, 05:11 PM

buxiu

職務: 超級管理者

等級: 連長

注冊: 2005年6月26日

積分: 206

精華: 8

發貼: 72

通過例子學習Lua(3)

發信人: jUYI (我很笨但我引以為榮), 信區: AnsiC

标 題: 通過例子學習Lua(3)

發信站: 瀚海星雲 (2004年12月17日02:00:38 星期五), 站内信件 WWWPOST

通過例子學習Lua(3) ---- 資料結構

1.簡介

Lua語言隻有一種基本資料結構, 那就是table, 所有其他資料結構如數組啦,

類啦, 都可以由table實作.

2.table的下标

例e05.lua

-- Arrays

myData = {}

myData[0] = "foo"

myData[1] = 42

-- Hash tables

myData["bar"] = "baz"

-- Iterate through the

-- structure

for key, value in myData do

print(key .. "=" .. value)

end

輸出結果

0=foo

1=42

bar=baz

程式說明

首先定義了一個table myData={}, 然後用數字作為下标賦了兩個值給它. 這種

定義方法類似于C中的數組, 但與數組不同的是, 每個數組元素不需要為相同類型,

就像本例中一個為整型, 一個為字元串.

程式第二部分, 以字元串做為下标, 又向table内增加了一個元素. 這種table非常

像STL裡面的map. table下标可以為Lua所支援的任意基本類型, 除了nil值以外.

Lua對Table占用記憶體的處理是自動的, 如下面這段代碼

a = {}

a["x"] = 10

b = a -- `b' refers to the same table as `a'

print(b["x"]) --> 10

b["x"] = 20

print(a["x"]) --> 20

a = nil -- now only `b' still refers to the table

b = nil -- now there are no references left to the table

b和a都指向相同的table, 隻占用一塊記憶體, 當執行到a = nil時, b仍然指向table,

而當執行到b=nil時, 因為沒有指向table的變量了, 是以Lua會自動釋放table所占記憶體

3.Table的嵌套

Table的使用還可以嵌套,如下例

例e06.lua

-- Table 'constructor'

myPolygon = {

color="blue",

thickness=2,

npoints=4;

{x=0, y=0},

{x=-10, y=0},

{x=-5, y=4},

{x=0, y=4}

}

-- Print the color

print(myPolygon["color"])

-- Print it again using dot

-- notation

print(myPolygon.color)

-- The points are accessible

-- in myPolygon[1] to myPolygon[4]

-- Print the second point's x

-- coordinate

print(myPolygon[2].x)

程式說明

首先建立一個table, 與上一例不同的是,在table的constructor裡面有{x=0,y=0},

這是什麼意思呢? 這其實就是一個小table, 定義在了大table之内, 小table的

table名省略了.

最後一行myPolygon[2].x,就是大table裡面小table的通路方式.

--

IP 位址: 已登入 來自: 已登入

第 4 樓

2005-06-27, 05:12 PM

buxiu

職務: 超級管理者

等級: 連長

注冊: 2005年6月26日

積分: 206

精華: 8

發貼: 72

通過例子學習Lua(4)

信人: jUYI (我很笨但我引以為榮), 信區: AnsiC

标 題: 通過例子學習Lua(4)

發信站: 瀚海星雲 (2004年12月17日02:01:08 星期五), 站内信件 WWWPOST

通過例子學習Lua(4) -- 函數的調用

1.不定參數

例e07.lua

-- Functions can take a

-- variable number of

-- arguments.

function funky_print (...)

for i=1, arg.n do

print("FuNkY: " .. arg)

end

end

funky_print("one", "two")

運作結果

FuNkY: one

FuNkY: two

程式說明

* 如果以...為參數, 則表示參數的數量不定.

* 參數将會自動存儲到一個叫arg的table中.

* arg.n中存放參數的個數. arg[]加下标就可以周遊所有的參數.

2.以table做為參數

例e08.lua

-- Functions with table

-- parameters

function print_contents(t)

for k,v in t do

print(k .. "=" .. v)

end

end

print_contents{x=10, y=20}

運作結果

x=10

y=20

程式說明

* print_contents{x=10, y=20}這句參數沒加圓括号, 因為以單個table為參數的時候,

不需要加圓括号

* for k,v in t do 這個語句是對table中的所有值周遊, k中存放名稱, v中存放值

3.把Lua變成類似XML的資料描述語言

例e09.lua

function contact(t)

-- add the contact 't', which is

-- stored as a table, to a database

end

contact {

name = "Game Developer",

email = "[email protected]",

url = "http://www.ogdev.net,

quote = [[

There are

10 types of people

who can understand binary.]]

}

contact {

-- some other contact

}

程式說明

* 把function和table結合, 可以使Lua成為一種類似XML的資料描述語言

* e09中contact{...}, 是一種函數的調用方法, 不要弄混了

* [[...]]是表示多行字元串的方法

* 當使用C API時此種方式的優勢更明顯, 其中contact{..}部分可以另外存成一配置文

4.試試看

想想看哪些地方可以用到例e09中提到的配置方法呢?

--

IP 位址: 已登入 來自: 已登入

第 5 樓

2005-06-27, 05:14 PM

buxiu

職務: 超級管理者

等級: 連長

注冊: 2005年6月26日

積分: 206

精華: 8

發貼: 72

通過例子學習Lua(5)

發信人: jUYI (我很笨但我引以為榮), 信區: AnsiC

标 題: 通過例子學習Lua(5)

發信站: 瀚海星雲 (2004年12月17日02:01:32 星期五), 站内信件 WWWPOST

通過例子學習Lua(5) ---- Lua與C互動入門

1.簡介

Lua與C/C++結合是很緊密的, Lua與C++互動是建立在Lua與C的基礎上的, 所

以偶先從Lua與C講起.

正如第一講所說, 運作Lua程式或者說調用Lua主要有兩種方式:

* 通過指令行執行"Lua"指令

* 通過Lua的C庫

雖然此前偶們一直用第一種方式, 但偶要告訴你, 通過Lua的C庫執行才是遊戲中

常用的方式.

2.Lua的C庫

Lua的C庫可以做為Shared Library調用, 但一般開發遊戲時會把Lua的所有源程式

都包含在内, 并不把Lua編譯成共享庫的形式. 因為Lua程式隻有100多K, 而且幾乎

可以在任何編譯器下Clean Compile. 帶Lua源程式的另一個好處時, 可以随時對Lua

本身進行擴充, 增加偶們所需的功能.

Lua的C庫提供一系列API:

* 管理全局變量

* 管理tables

* 調用函數

* 定義新函數, 這也可以完全由C實作

* 垃圾收集器Garbage collector, 雖然Lua可以自動進行, 但往往不是立即執行的,

是以對實時性要求比較高的程式, 會自己調用垃圾收集器

* 載入并執行Lua程式, 這也可以由Lua自身實作

* 任何Lua可以實作的功能, 都可以通過Lua的C API實作, 這對于優化程式的運作速度

有幫助. 經常調用的共用的Lua程式片斷可以轉成C程式, 以提高效率. 連Lua都是C寫的

還有什麼C不能實作呢?

3.Lua與C內建的例子

例e10.c

#include <stdio.h>

#include <lua.h>

int main(int argc, char *argv[]) {

char line[BUFSIZ];

lua_State *L = lua_open(0);

while (fgets(line, sizeof(line), stdin) != 0)

lua_dostring(L, line);

lua_close(L);

return 0;

}

編譯

Linux/Cygwin

* 先編譯Lua, 并把頭檔案放入include路徑

* gcc e10.c -llua -llualib -o e10

VC6/VC2003

* 先編譯Lua, 在Option中設定頭檔案和庫檔案路徑

* 建立工程,在工程配置中加入附加庫lua.lib和lualib.lib

* 編譯成exe

運作結果

本程式的功能是實作一個Lua解釋器, 輸入的每行字元都會被解釋成Lua并執行.

程式說明

* #include <lua.h> 包含lua頭檔案, 然後才可以使用API

* lua_State *L = lua_open(0) 打開一個Lua執行器

* fgets(line, sizeof(line), stdin) 從标準輸入裡讀入一行

* lua_dostring(L, line) 執行此行

* lua_close(L) 關閉Lua執行器

例e11.c

#include <stdio.h>

#include <lua.h>

#include <lualib.h>

int main(int argc, char *argv[]) {

char line[BUFSIZ];

lua_State *L = lua_open(0);

lua_baselibopen(L);

lua_iolibopen(L);

lua_strlibopen(L);

lua_mathlibopen(L);

while (fgets(line, sizeof(line), stdin) != 0)

lua_dostring(L, line);

lua_close(L);

return 0;

}

運作結果

本程式的功能是實作一個Lua解釋器, 輸入的每行字元都會被解釋成Lua并執行.

與上例不同的是, 本例調用了Lua的一些标準庫.

程式說明

* #include <lualib.h> 包含Lua的标準庫

* 以下這幾行是用來讀入Lua的一些庫, 這樣偶們的Lua程式就可以有更多的功能.

lua_baselibopen(L);

lua_iolibopen(L);

lua_strlibopen(L);

lua_mathlibopen(L);

4.試試看

把上面兩個小例子在你熟悉的編譯器中編譯執行, 并試試能否與Lua源碼樹一起編譯

--

IP 位址: 已登入 來自: 已登入

第 6 樓

2005-06-27, 05:15 PM

buxiu

職務: 超級管理者

等級: 連長

注冊: 2005年6月26日

積分: 206

精華: 8

發貼: 72

通過例子學習Lua(6)

發信人: jUYI (我很笨但我引以為榮), 信區: AnsiC

标 題: 通過例子學習Lua(6)

發信站: 瀚海星雲 (2004年12月17日02:02:01 星期五), 站内信件 WWWPOST

通過例子學習Lua(6) ---- C/C++中用Lua函數

參考英文文檔http://tonyandpaige.com/tutorials/lua2.html

1.簡介

偶們這次主要說說怎麼由Lua定義函數, 然後在C或者C++中調用. 這裡偶們

暫不涉及C++的對象問題, 隻讨論調用函數的參數, 傳回值和全局變量的使用.

2.

這裡偶們在e12.lua裡先定義一個簡單的add(), x,y為加法的兩個參數,

return 直接傳回相加後的結果.

例e12.lua

-- add two numbers

function add ( x, y )

return x + y

end

在前一次裡, 偶們說到 lua_dofile() 可以直接在C中執行lua檔案. 因為偶們

這個程式裡隻定義了一個add()函數, 是以程式執行後并不直接結果, 效果相當

于在C中定義了一個函數一樣.

Lua的函數可以有多個參數, 也可以有多個傳回值, 這都是由棧(stack)實作的.

需要調用一個函數時, 就把這個函數壓入棧, 然後順序壓入所有參數, 然後用

lua_call()調用這個函數. 函數傳回後, 傳回值也是存放在棧中. 這個過程和

彙編執行函數調用的過程是一樣的.

例e13.cpp 是一個調用上面的Lua函數的例子

#include <stdio.h>

extern "C" { // 這是個C++程式, 是以要extern "C",

// 因為lua的頭檔案都是C格式的

#include "lua.h"

#include "lualib.h"

#include "lauxlib.h"

}

lua_State* L;

int luaadd ( int x, int y )

{

int sum;

lua_getglobal(L, "add");

lua_pushnumber(L, x);

lua_pushnumber(L, y);

lua_call(L, 2, 1);

sum = (int)lua_tonumber(L, -1);

lua_pop(L, 1);

return sum;

}

int main ( int argc, char *argv[] )

{

int sum;

L = lua_open();

lua_baselibopen(L);

lua_dofile(L, "e12.lua");

sum = luaadd( 10, 15 );

printf( "The sum is %d/n", sum );

lua_close(L);

return 0;

}

程式說明:

main中過程偶們上次已經說過了, 是以這次隻說說luaadd的過程

* 首先用lua_getglobal()把add函數壓棧

* 然後用lua_pushnumber()依次把x,y壓棧

* 然後調用lua_call(), 并且告訴程式偶們有兩個參數一個傳回值

* 接着偶們從棧頂取回傳回值, 用lua_tonumber()

* 最後偶們用lua_pop()把傳回值清掉

運作結果:

The sum is 25

編譯方法

Linux下把程式存成e13.cpp

g++ e13.cpp -llua -llualib -o e13

./e13

VC下編譯方法

* 首先建立一個空的Win32 Console Application Project

* 把e13.cpp加入工程中

* 點project setting,然後設定link選項, 再加上lua.lib lualib.lib兩個額外的庫

* 最後編譯

建立好的project可以在這裡下載下傳

VC http://tonyandpaige.com/tutorials/luaadd.zip

Linux http://tonyandpaige.com/tutorials/luaadd.tar.gz

3.全局變量

上面偶們用到了lua_getglobal()但并沒有詳細講, 這裡偶們再舉兩個小例子來說下全局

變量

lua_getglobal()的作用就是把lua中全局變量的值壓入棧

lua_getglobal(L, "z");

z = (int)lua_tonumber(L, 1);

lua_pop(L, 1);

假設Lua程式中定義了一個全局變量z, 這段小程式就是把z的值取出放入C的變量z中.

另外Lua中還有一個對應的函數lua_setglobal(), 作用是用棧頂的值填充指定的全局變

lua_pushnumber(L, 10);

lua_setglobal(L, "z");

例如這段小程式就是把lua中的全局變量z設為10, 如果lua中未定義z的話, 就會自動創

建一個

全局變量z并設為10.

4.試試看

自己寫個函數用C/C++來調用下試試

--

IP 位址: 已登入 來自: 已登入

第 7 樓

2005-06-27, 05:17 PM

buxiu

職務: 超級管理者

等級: 連長

注冊: 2005年6月26日

積分: 206

精華: 8

發貼: 72

通過例子學習Lua(7)

發信人: jUYI (我很笨但我引以為榮), 信區: AnsiC

标 題: 通過例子學習Lua(7)

發信站: 瀚海星雲 (2004年12月17日02:02:34 星期五), 站内信件 WWWPOST

通過例子學習Lua(7)

通過例子學習Lua(7) ---- Lua中調用C/C++函數

1.前言

上次偶說到從C/C++中調用Lua的函數, 然後就有朋友問從Lua中如何調用C/C++的

函數, 是以偶們這次就來說說這個問題. 首先偶們會在C++中建立一個函數, 然後

告知Lua有這個函數, 最後再執行它. 另外, 由于函數不是在Lua中定義的, 是以

無法确定函數的正确性, 可能在調用過程中會出錯, 是以偶們還會說說Lua出錯處

理的問題.

2.Lua中調用C函數

在lua中是以函數指針的形式調用函數, 并且所有的函數指針都必須滿足如下此種

類型:

typedef int (*lua_CFunction) (lua_State *L);

也就是說, 偶們在C++中定義函數時必須以lua_State為參數, 以int為傳回值才能

被Lua所調用. 但是不要忘記了, 偶們的lua_State是支援棧的, 是以通過棧可以

傳遞無窮個參數, 大小隻受記憶體大小限制. 而傳回的int值也隻是指傳回值的個數

真正的傳回值都存儲在lua_State的棧中. 偶們通常的做法是做一個wrapper, 把

所有需要調用的函數都wrap一下, 這樣就可以調用任意的函數了.

下面這個例子是一個C++的average()函數, 它将展示如何用多個參數并傳回多個值

例e14.cpp

#include <stdio.h>

extern "C" {

#include "lua.h"

#include "lualib.h"

#include "lauxlib.h"

}

lua_State* L;

static int average(lua_State *L)

{

int n = lua_gettop(L);

double sum = 0;

int i;

for (i = 1; i <= n; i++)

{

sum += lua_tonumber(L, i);

}

lua_pushnumber(L, sum / n);

lua_pushnumber(L, sum);

return 2;

}

int main ( int argc, char *argv[] )

{

L = lua_open();

lua_baselibopen(L);

lua_register(L, "average", average);

lua_dofile(L, "e15.lua");

lua_close(L);

return 0;

}

例e15.lua

-- call a C++ function

avg, sum = average(10, 20, 30, 40, 50)

print("The average is ", avg)

print("The sum is ", sum)

程式說明:

* lua_gettop()的作用是傳回棧頂元素的序号. 由于Lua的棧是從1開始編号的,

是以棧頂元素的序号也相當于棧中的元素個數. 在這裡, 棧中元素的個數就

是傳入的參數個數.

* for循環計算所有傳入參數的總和. 這裡用到了數值轉換lua_tonumber().

* 然後偶們用lua_pushnumber()把平均值和總和push到棧中.

* 最後, 偶們傳回2, 表示有兩個傳回值.

* 偶們雖然在C++中定義了average()函數, 但偶們的Lua程式并不知道, 是以需

要在main函數中加入

lua_register(L, "average", average);

這兩行的作用就是告訴e15.lua有average()這樣一個函數.

* 這個程式可以存成cpp也可以存成c, 如果以.c為擴充名就不需要加extern "C"

編譯的方法偶們上次說過了, 方法相同.

e15.lua執行的方法隻能用上例中的C++中執行, 而不能用指令行方式執行.

3.錯誤處理

在上例中, 偶們沒有對傳入的參數是否為數字進行檢測, 這樣做不好. 是以這裡偶

們再加上錯誤處理的片斷.

把這段加在for循環之内:

if (!lua_isnumber(L, i)) {

lua_pushstring(L, "Incorrect argument to 'average'");

lua_error(L);

}

這段的作用就是檢測傳入的是否為數字.

加上這段之後, 偶們debug的時候就會簡單許多. 對于結合兩種語言的程式設計, 它們之

間傳遞資料的正确性檢測是非常重要的.

這裡有别人寫好的例子:

VC的 http://tonyandpaige.com/tutorials/luaavg.zip

Linux的 http://tonyandpaige.com/tutorials/luaavg.tar.gz

至此, Lua與C的結合就基本講完了, 下次偶要開始說說Lua與面向對象.

但是偶自己還沒有學完, 是以大家可能要多等兩天了. Sorry!

繼續閱讀