補齊遺失副本,時間線回歸。
本文同步發表於個人網站
現在,我們來嘗試從C去執行一個Lua程式,Lua程式就用最簡單的Hello,並命名為hello.lua
print "Hello"
然後來寫C程式 -- hello_C.c
。
需要下載含有標頭檔和函式庫的版本
#include "lua.h"
#include "lauxlib.h"
// new a lua VM
lua_State *L = luaL_newstate();
通常而言,不會全部開啟所有功能。
這只是範例,讓hello.lua
檔案擁有所有能力。
// open all libraries
luaL_openlibs(L);
luaL_dofile(L, "hello.lua");
#include "lua.h"
#include "lauxlib.h"
int main(int argc, char *argv[]){
// new a lua VM
lua_State *L = luaL_newstate();
// open all libraries
luaL_openlibs(L);
// dofile
luaL_dofile(L, "hello1");
return 0;
}
Hello
如此一來可以隨意更動
hello.lua
檔案,而無須重新編譯C程式。
目錄結構:
接者要來嘗試從C實現以下Lua函數:
function hello(name)
print("Hello, World")
end
不過在此之前,得先了解到,Lua與C交互,是抽象在一個平坦的記憶體堆疊空間,通常總是從堆疊上放存取會放置資料。寫過組合語言的可能會這種模式會有些熟悉。
int CHello(lua_State *L){
const char *name = lua_tostring(L,-1); // 從堆疊頂部取得一個字串
lua_pop(L, 1); // 將字串彈出堆疊
printf("Hello, %s\n", name);
return LUA_OK;
}
最好還去檢查輸入的參數型別是否正確。
在錯誤處理曾經處理過。
lua_register(L, "CHello", CHello);
CHello("World")
#include "lua.h"
#include "lauxlib.h"
#include <stdio.h>
int CHello(lua_State*);
int main(int argc, char *argv[]){
// new a lua VM
lua_State *L = luaL_newstate();
// open all libraries
luaL_openlibs(L);
// regist C function to Lua
lua_register(L, "CHello", CHello);
// dofile
luaL_dofile(L, "chello.lua");
return 0;
}
int CHello(lua_State *L){
const char *name = lua_tostring(L,-1); // 從堆疊頂部取得一個字串
lua_pop(L, 1); // 將字串彈出堆疊
printf("Hello, %s\n", name);
return 0; // no return. 多回傳值時,填入回傳數目。這個函數沒有回傳值,故為0。
}
在C無法除0:
double x = 1.0 / 0; // Error: 無法除0
雖然在C寫
1.0 / 0
會報錯,但是可以寫1 / 0.0
。這與C自動轉型有關。
Lua在這部份處理的更人性化,但儘管Lua可以直接寫1 / 0
,你仍應該知道背後的原因。
不過在Lua可以除0,會得到無限大:
x = 1 / 0 -- inf
雖然Lua的數字也有限制,但可能比C來的更安全。現在,希望使用部份Lua數值計算的能力:
function div(a,b)
return a/b
end
div
,他會被放至於堆疊頂部。lua_Number LuaDiv(lua_State *L, lua_Number a, lua_Number b){
int get_type = lua_getglobal(L, "div"); // get Lua Function
/* check th global variable -- div, is a Lua Function
if(get_type == LUA_TFUNCTION){
printf("[in C] is Lua Function.\n");
}
if(lua_isfunction(L, -1)){ // check top of stack is Lua Function
printf("[in C] top of stack is Lua Function.\n");
}
*/
lua_pushnumber(L, a); // pass paramter
lua_pushnumber(L, b); // pass paramter
lua_call(L, /*nargs = */ 2, /* nresult = */ 1); //兩個輸入,一個回傳值。
lua_Number result = lua_tonumber(L, -1); // 取得回傳值
lua_pop(L, 1);
return result; // 回傳結果
}
#include "lua.h"
#include "lauxlib.h"
lua_Number LuaDiv(lua_State *L, lua_Number a, lua_Number b);
int main(int argc, char *argv[]){
// new a lua VM
lua_State *L = luaL_newstate();
// open all libraries
luaL_openlibs(L);
// dofile
luaL_dofile(L, "div.lua"); // load Lua's div function
double r = LuaDiv(L, 1, 0);
printf("1 / 0 = %f\n", r);
return 0;
}
lua_Number LuaDiv(lua_State *L, lua_Number a, lua_Number b){
int get_type = lua_getglobal(L, "div"); // get Lua Function
/* check th global variable -- div, is a Lua Function
if(get_type == LUA_TFUNCTION){
printf("[in C] is Lua Function.\n");
}
if(lua_isfunction(L, -1)){ // check top of stack is Lua Function
printf("[in C] top of stack is Lua Function.\n");
}
*/
lua_pushnumber(L, a); // pass paramter
lua_pushnumber(L, b); // pass paramter
lua_call(L, /*nargs = */ 2, /* nresult = */ 1); //兩個輸入,一個回傳值。
lua_Number result = lua_tonumber(L, -1); // 取得回傳值
lua_pop(L, 1);
return result; // 回傳結果
}
本小節參考:Lua:一個Python的秘密武器
我很好奇Lua到底能否作為Python的加速,照著Lua:一個Python的秘密武器做了一次。
import time
import random
size = 5000_000
st = time.time()
a = [random.randint(1, size) for _ in range(size)]
b = [random.randint(1, size) for _ in range(size)]
print("Pure Python init", time.time() - st)
def test():
for i in range(size):
if a[i] != b[i]:
a[i] = a[i] + b[i]
st = time.time()
test()
print("Pure Python Sum", time.time() - st)
以下是使用Python 3.8.2,於LinuxMint 20.3執行的結果。
Pure Python init 29.92748188972473
Pure Python Sum 2.812898635864258
接著是使用lupa
的結果
from lupa import LuaRuntime
lua = LuaRuntime()
lua_code = r'''
function (size)
a = {}
b = {}
st = os.clock()
for i=0, size-1 do
a[i] = math.random(size)
end
for i=0, size-1 do
b[i] = math.random(size)
end
print("Lua init: " .. (os.clock() - st))
st = os.clock()
for i = 0, size - 1 do
if a[i] ~= b[i] then
a[i] = a[i] + b[i]
end
end
print("Lua sum: " .. (os.clock() - st))
end
'''
test = lua.eval(lua_code)
size = 5000_000
test(size)
Lua init: 2.650081
Lua sum: 1.75244
Pure Python init 4.170244216918945
Pure Python Sum 0.04040670394897461
我沒有去嘗試Numpy和C的版本。其實還有Cython的方式可以加速Python程式。但就我看法,要為了加速而使用Lua有點不太有價值。在我的測試中,Lua初始化快了許多,但Lua的數值型態與Python的也有差異。Python的整數可以到非常之大,端看記憶體大小。當然,如果你確定你的資料值域,有可能使用Lua進行加速是恰當的。
userdata
是從C語言定義的資料型態。分成兩種形式:
full userdata完全有Lua掌控,包含創建與記憶體回收。相對的light userdata其實就只是一個C指標而已。
本系列未打算對userdata
多做講解,各位只要了解到其有兩種型態,並且所有操作,其實都是由C定義即可。