Lua教程(十七):C API简介(共5篇)由网友“诡计多端的小狗”投稿提供,以下是小编为大家准备的Lua教程(十七):C API简介,仅供参考,大家一起来看看吧。
篇1:Lua教程(十七):C API简介
这篇文章主要介绍了Lua教程(十七):C API简介,本文讲解了基础知识、栈、C API中的错误处理、Lua调用C程序、C程序调用Lua代码的错误处理等内容,需要的朋友可以参考下
Lua是一种嵌入式脚本语言,即Lua不是可以单独运行的程序,在实际应用中,主要存在两种应用形式,第一种形式是,C/C++作为主程序,调用Lua代码,此时可以将Lua看做“可扩展的语言”,我们将这种应用称为“应用程序代码”。第二种形式是Lua具有控制权,而C/C++代码则作为Lua的“库代码”。在这两种形式中,都是通过Lua提供的C API完成两种语言之间的通信的。
1. 基础知识:
C API是一组能使C/C++代码与Lua交互的函数。其中包括读写Lua全局变量、调用Lua函数、运行一段Lua代码,以及注册C函数以供Lua代码调用等。这里先给出一个简单的示例代码:
代码如下:
#include
#include
#include
#include
#include
int main(void)
{
const char* buff = “print(\”hello\“)”;
int error;
lua_State* L = luaL_newstate;
luaL_openlibs(L);
error = luaL_loadbuffer(L,buff,strlen(buff),“line”) || lua_pcall(L,0,0,0);
int s = lua_gettop(L);
if (error) {
fprintf(stderr,“%s”,lua_tostring(L,-1));
lua_pop(L,1);
}
lua_close(L);
return 0;
}
下面是针对以上代码给出的具体解释:
1). 上面的代码是基于我的C++工程,而非C工程,因此包含的头文件是lua.hpp,如果是C工程,可以直接包含lua.h。
2). Lua库中没有定义任何全局变量,而是将所有的状态都保存在动态结构lua_State中,后面所有的C API都需要该指针作为第一个参数。
3). luaL_openlibs函数是用于打开Lua中的所有标准库,如io库、string库等。
4). luaL_loadbuffer编译了buff中的Lua代码,如果没有错误,则返回0,同时将编译后的程序块压入虚拟栈中。
5). lua_pcall函数会将程序块从栈中弹出,并在保护模式下运行该程序块。执行成功返回0,否则将错误信息压入栈中。
6). lua_tostring函数中的-1,表示栈顶的索引值,栈底的索引值为1,以此类推。该函数将返回栈顶的错误信息,但是不会将其从栈中弹出。
7). lua_pop是一个宏,用于从虚拟栈中弹出指定数量的元素,这里的1表示仅弹出栈顶的元素。
8). lua_close用于释放状态指针所引用的资源。
2. 栈:
在Lua和C语言之间进行数据交换时,由于两种语言之间有着较大的差异,比如Lua是动态类型,C语言是静态类型,Lua是自动内存管理,而C语言则是手动内存管理。为了解决这些问题,Lua的设计者使用了虚拟栈作为二者之间数据交互的介质。在C/C++程序中,如果要获取Lua的值,只需调用Lua的C API函数,Lua就会将指定的值压入栈中。要将一个值传给Lua时,需要先将该值压入栈,然后调用Lua的C API,Lua就会获取该值并将其从栈中弹出。为了可以将不同类型的值压入栈,以及从栈中取出不同类型的值,Lua为每种类型均设定了一个特定函数。
1). 压入元素:
Lua针对每种C类型,都有一个C API函数与之对应,如:
代码如下:
void lua_pushnil(lua_State* L); --nil值
void lua_pushboolean(lua_State* L, int b); --布尔值
void lua_pushnumber(lua_State* L, lua_Number n); --浮点数
void lua_pushinteger(lua_State* L, lua_Integer n); --整型
void lua_pushlstring(lua_State* L, const char* s, size_t len); --指定长度的内存数据
void lua_pushstring(lua_State* L, const char* s); --以零结尾的字符串,其长度可由strlen得出。
对于字符串数据,Lua不会持有他们的指针,而是调用在API时生成一个内部副本,因此,即使在这些函数返回后立刻释放或修改这些字符串指针,也不会有任何问题。
在向栈中压入数据时,可以通过调用下面的函数判断是否有足够的栈空间可用,一般而言,Lua会预留20个槽位,对于普通应用来说已经足够了,除非是遇到有很多参数的函数。
int lua_checkstack(lua_State* L, int extra) --期望得到extra数量的空闲槽位,如果不能扩展并获得,返回false。
2). 查询元素:
API使用“索引”来引用栈中的元素,第一个压入栈的为1,第二个为2,依此类推。我们也可以使用负数作为索引值,其中-1表示为栈顶元素,-2为栈顶下面的元素,同样依此类推。
Lua提供了一组特定的函数用于检查返回元素的类型,如:
代码如下:
int lua_isboolean (lua_State *L, int index);
int lua_iscfunction (lua_State *L, int index);
int lua_isfunction (lua_State *L, int index);
int lua_isnil (lua_State *L, int index);
int lua_islightuserdata (lua_State *L, int index);
int lua_isnumber (lua_State *L, int index);
int lua_isstring (lua_State *L, int index);
int lua_istable (lua_State *L, int index);
int lua_isuserdata (lua_State *L, int index);
以上函数,成功返回1,否则返回0。需要特别指出的是,对于lua_isnumber而言,不会检查值是否为数字类型,而是检查值是否能转换为数字类型。
Lua还提供了一个函数lua_type,用于获取元素的类型,函数原型如下:
代码如下:
int lua_type (lua_State *L, int index);
该函数的返回值为一组常量值,分别是:LUA_TNIL、LUA_TNUMBER、LUA_TBOOLEAN、LUA_TSTRING、LUA_TTABLE、LUA_TFUNCTION、LUA_TUSERDATA、LUA_TTHREAD和LUA_TLIGHTUSERDATA,
这些常量通常用于switch语句中。
除了上述函数之外,Lua还提供了一组转换函数,如:
代码如下:
int lua_toboolean (lua_State *L, int index);
lua_CFunction lua_tocfunction (lua_State *L, int index);
lua_Integer lua_tointeger (lua_State *L, int index);
const char *lua_tolstring (lua_State *L, int index, size_t *len);
lua_Number lua_tonumber (lua_State *L, int index);
const void *lua_topointer (lua_State *L, int index);
const char *lua_tostring (lua_State *L, int index);
void *lua_touserdata (lua_State *L, int index);
--string类型返回字符串长度,table类型返回操作符‘#‘等同的结果,userdata类型返回分配的内存块长度。
size_t lua_objlen (lua_State *L, int index);
对于上述函数,如果调用失败,lua_toboolean、lua_tonumber、lua_tointeger和lua_objlen均返回0,而其他函数则返回NULL。在很多时候0不是一个很有效的用于判断错误的值,但是ANSI C没有提供其他可以表示错误的值。因此对于这些函数,在有些情况下需要先使用lua_is*系列函数判断是否类型正确,而对于剩下的函数,则可以直接通过判断返回值是否为NULL即可。
对于lua_tolstring函数返回的指向内部字符串的指针,在该索引指向的元素被弹出之后,将无法保证仍然有效。该函数返回的字符串末尾均会有一个尾部0。
下面将给出一个工具函数,可用于演示上面提到的部分函数,如:
代码如下:
static void stackDump(lua_State* L)
{
int top = lua_gettop(L);
for (int i = 1; i <= top; ++i) {
int t = lua_type(L,i);
switch(t) {
case LUA_TSTRING:
printf(“‘%s‘”,lua_tostring(L,i));
break;
case LUA_TBOOLEAN:
printf(lua_toboolean(L,i) ? “true” : “false”);
break;
case LUA_TNUMBER:
printf(“%g”,lua_tonumber(L,i));
break;
default:
printf(“%s”,lua_typename(L,t));
break;
}
printf(“”);
}
printf(“\n”);
}
3). 其它栈操作函数:
除了上面给出的数据交换函数之外,Lua的C API还提供了一组用于操作虚拟栈的普通函数,如:
代码如下:
int lua_gettop(lua_State* L); --返回栈中元素的个数。
void lua_settop(lua_State* L, int index); --将栈顶设置为指定的索引值。
void lua_pushvalue(lua_State* L, int index); --将指定索引的元素副本压入栈。
void lua_remove(lua_State* L, int index); --删除指定索引上的元素,其上面的元素自动下移。
void lua_insert(lua_State* L, int index); --将栈顶元素插入到该索引值指向的位置。
void lua_replace(lua_State* L, int index); --弹出栈顶元素,并将该值设置到指定索引上。
Lua还提供了一个宏用于弹出指定数量的元素:
代码如下:
#define lua_pop(L,n) lua_settop(L, -(n) - 1)
见如下示例代码:
代码如下:
int main()
{
lua_State* L = luaL_newstate();
lua_pushboolean(L,1);
lua_pushnumber(L,10);
lua_pushnil(L);
lua_pushstring(L,“hello”);
stackDump(L); //true 10 nil ‘hello‘
lua_pushvalue(L,-4);
stackDump(L); //true 10 nil ‘hello‘ true
lua_replace(L,3);
stackDump(L); //true 10 true ‘hello‘
lua_settop(L,6);
stackDump(L); //true 10 true ‘hello‘ nil nil
lua_remove(L,-3);
stackDump(L); //true 10 true nil nil
lua_settop(L,-5);
stackDump(L); //true
lua_close(L);
return 0;
}
3. C API中的错误处理:
1). C程序调用Lua代码的错误处理:
通常情况下,应用程序代码是以“无保护”模式运行的。因此,当Lua发现“内存不足”这类错误时,只能通过调用“紧急”函数来通知C语言程序,之后在结束应用程序。用户可通过lua_atpanic来设置自己的“紧急”函数。如果希望应用程序代码在发生Lua错误时不会退出,可通过调用lua_pcall函数以保护模式运行Lua代码。这样再发生内存错误时,lua_pcall会返回一个错误代码,并将解释器重置为一致的状态。如果要保护与Lua的C代码,可以使用lua_cpall函数,它将接受一个C函数作为参数,然后调用这个C函数。
2). Lua调用C程序:
通常而言,当一个被Lua调用的C函数检测到错误时,它就应该调用lua_error,该函数会清理Lua中所有需要清理的资源,然后跳转回发起执行的那个lua_pcall,并附上一条错误信息。
篇2:Lua教程(二十二):userdata
这篇文章主要介绍了Lua教程(二十二):userdata,这里我们通过一个简单完整的示例来学习一下Lua中userdata的使用方式,需要的朋友可以参考下
在Lua中可以通过自定义类型的方式与C语言代码更高效、更灵活的交互,这里我们通过一个简单完整的示例来学习一下Lua中userdata的使用方式。需要说明的是,该示例完全来自于Programming in Lua。其功能是用C程序实现一个Lua的布尔数组,以提供程序的执行效率。见下面的代码和关键性注释。
代码如下:
#include
#include
#include
#include
#define BITS_PER_WORD (CHAR_BIT * sizeof(int))
#define I_WORD(i) ((unsigned int)(i))/BITS_PER_WORD
#define I_BIT(i) (1 << ((unsigned int)(i)%BITS_PER_WORD))
typedef struct NumArray {
int size;
unsigned int values[1];
} NumArray;
extern “C” int newArray(lua_State* L)
{
//1. 检查第一个参数是否为整型。以及该参数的值是否大于等于1.
int n = luaL_checkint(L,1);
luaL_argcheck(L, n >= 1, 1, “invalid size.”);
size_t nbytes = sizeof(NumArray) + I_WORD(n - 1) * sizeof(int);
//2. 参数表示Lua为userdata分配的字节数。同时将分配后的userdata对象压入栈中。
NumArray* a = (NumArray*)lua_newuserdata(L,nbytes);
a->size = n;
for (int i = 0; i < I_WORD(n - 1); ++i)
a->values[i] = 0;
//获取注册表变量myarray,该key的值为metatable。
luaL_getmetatable(L,“myarray”);
//将userdata的元表设置为和myarray关联的table。同时将栈顶元素弹出。
lua_setmetatable(L,-2);
return 1;
}
extern “C” int setArray(lua_State* L)
{
//1. Lua传给该函数的第一个参数必须是userdata,该对象的元表也必须是注册表中和myarray关联的table。
//否则该函数报错并终止程序。
NumArray* a = (NumArray*)luaL_checkudata(L,1,“myarray”);
int index = luaL_checkint(L,2) - 1;
//2. 由于任何类型的数据都可以成为布尔值,因此这里使用any只是为了确保有3个参数。
luaL_checkany(L,3);
luaL_argcheck(L,a != NULL,1,“‘array‘ expected.”);
luaL_argcheck(L,0 <= index && index < a->size,2,“index out of range.”);
if (lua_toboolean(L,3))
a->values[I_WORD(index)] |= I_BIT(index);
else
a->values[I_WORD(index)] &= ~I_BIT(index);
return 0;
}
extern “C” int getArray(lua_State* L)
{
NumArray* a = (NumArray*)luaL_checkudata(L,1,“myarray”);
int index = luaL_checkint(L,2) - 1;
luaL_argcheck(L, a != NULL, 1, “‘array‘ expected.”);
luaL_argcheck(L, 0 <= index && index < a->size,2,“index out of range”);
lua_pushboolean(L,a->values[I_WORD(index)] & I_BIT(index));
return 1;
}
extern “C” int getSize(lua_State* L)
{
NumArray* a = (NumArray*)luaL_checkudata(L,1,“myarray”);
luaL_argcheck(L,a != NULL,1,“‘array‘ expected.”);
lua_pushinteger(L,a->size);
return 1;
}
extern “C” int array2string(lua_State* L)
{
NumArray* a = (NumArray*)luaL_checkudata(L,1,“myarray”);
lua_pushfstring(L,“array(%d)”,a->size);
return 1;
}
static luaL_Reg arraylib_f [] = {
{“new”, newArray},
{NULL, NULL}
};
static luaL_Reg arraylib_m [] = {
{“set”, setArray},
{“get”, getArray},
{“size”, getSize},
{“__tostring”, array2string}, //print(a)时Lua会调用该元方法,
{NULL, NULL}
};
extern “C” __declspec(dllexport)
int luaopen_testuserdata(lua_State* L)
{
//1. 创建元表,并将该元表指定给newArray函数新创建的userdata。在Lua中userdata也是以table的身份表现的。
//这样在调用对象函数时,可以通过验证其metatable的名称来确定参数userdata是否合法。
luaL_newmetatable(L,“myarray”);
lua_pushvalue(L,-1);
//2. 为了实现面对对象的调用方式,需要将元表的__index字段指向自身,同时再将arraylib_m数组中的函数注册到
//元表中,之后基于这些注册函数的调用就可以以面向对象的形式调用了。
//lua_setfield在执行后会将栈顶的table弹出。
lua_setfield(L,-2,“__index”);
//将这些成员函数注册给元表,以保证Lua在寻找方法时可以定位。NULL参数表示将用栈顶的table代替第二个参数。
luaL_register(L,NULL,arraylib_m);
//这里只注册的工厂方法。
luaL_register(L,“testuserdata”,arraylib_f);
return 1;
}
轻量级userdata:
之前介绍的是full userdata,Lua还提供了另一种轻量级userdata(light userdata)。事实上,轻量级userdata仅仅表示的是C指针的值,即(void*)。由于它只是一个值,所以不用创建。如果需要将一个轻量级userdata放入栈中,调用lua_pushlightuserdata即可。full userdata和light userdata之间最大的区别来自于相等性判断,对于一个full userdata,它只是与自身相等,而light userdata则表示为一个C指针,因此,它与所有表示同一指针的light userdata相等。再有就是light userdata不会受到垃圾收集器的管理,使用时就像一个普通的整型数字一样。
篇3:Lua教程(十九):C调用Lua
这篇文章主要介绍了Lua教程(十九):C调用Lua,本文讲解了C调用Lua基础知识、table操作、调用Lua函数等内容,需要的朋友可以参考下
1. 基础:
Lua的一项重要用途就是作为一种配置语言,现在从一个简单的示例开始吧。
代码如下:
--这里是用Lua代码定义的窗口大小的配置信息
width = 200
height = 300
下面是读取配置信息的C/C++代码:
代码如下:
#include
#include
#include
#include
#include
void load(lua_State* L, const char* fname, int* w, int* h) {
if (luaL_loadfile(L,fname) || lua_pcall(L,0,0,0)) {
printf(“Error Msg is %s.\n”,lua_tostring(L,-1));
return;
}
lua_getglobal(L,“width”);
lua_getglobal(L,“height”);
if (!lua_isnumber(L,-2)) {
printf(“‘width‘ should be a number\n” );
return;
}
if (!lua_isnumber(L,-1)) {
printf(“‘height‘ should be a number\n” );
return;
}
*w = lua_tointeger(L,-2);
*h = lua_tointeger(L,-1);
}
int main
{
lua_State* L = luaL_newstate();
int w,h;
load(L,“D:/test.lua”,&w,&h);
printf(“width = %d, height = %d\n”,w,h);
lua_close(L);
return 0;
}
下面是针对新函数的解释:
lua_getglobal是宏,其原型为:#define lua_getglobal(L,s) lua_getfield(L, LUA_GLOBALSINDEX, (s))。
每次调用这个宏的时候,都会将Lua代码中与之相应的全局变量值压入栈中,第一次调用时将全局变量“width”的值压入栈中,之后再次调用时再将“height”的值也压入栈中。
2. table操作:
我们可以在C语言的代码中操作Lua中的table数据,这是一个非常非常方便且实用的功能。这样不仅可以使Lua代码的结构更加清晰,也可以在C语言代码中定义等同的结构体与之对应,从而大大提高代码的可读性。见如下代码:
代码如下:
#include
#include
#include
#include
#include
void load(lua_State* L) {
if (luaL_loadstring(L,“background = { r = 0.30, g = 0.10, b = 0 }”)
|| lua_pcall(L,0,0,0)) {
printf(“Error Msg is %s.\n”,lua_tostring(L,-1));
return;
}
lua_getglobal(L,“background”);
if (!lua_istable(L,-1)) {
printf(“‘background‘ is not a table.\n” );
return;
}
lua_getfield(L,-1,“r”);
if (!lua_isnumber(L,-1)) {
printf(“Invalid component in background color.\n”);
return;
}
int r = (int)(lua_tonumber(L,-1) * 255);
lua_pop(L,1);
lua_getfield(L,-1,“g”);
if (!lua_isnumber(L,-1)) {
printf(“Invalid component in background color.\n”);
return;
}
int g = (int)(lua_tonumber(L,-1) * 255);
lua_pop(L,1);
lua_pushnumber(L,0.4);
lua_setfield(L,-2,“b”);
lua_getfield(L,-1,“b”);
if (!lua_isnumber(L,-1)) {
printf(“Invalid component in background color.\n”);
return;
}
int b = (int)(lua_tonumber(L,-1) * 255);
printf(“r = %d, g = %d, b = %d\n”,r,g,b);
lua_pop(L,1);
lua_pop(L,1);
return;
}
int main()
{
lua_State* L = luaL_newstate();
load(L);
lua_close(L);
return 0;
}
void lua_getfield(lua_State *L, int idx, const char *k); 第二个参数是table变量在栈中的索引值,最后一个参数是table的键值,该函数执行成功后会将字段值压入栈中。
void lua_setfield(lua_State *L, int idx, const char *k); 第二个参数是table变量在栈中的索引值,最后一个参数是table的键名称,而字段值是通过上一条命令lua_pushnumber(L,0.4)压入到栈中的,该函数在执行成功后会将刚刚压入的字段值弹出栈,
下面的代码示例是在C语言代码中构造table对象,同时初始化table的字段值,最后再将table对象赋值给Lua中的一个全局变量。
代码如下:
#include
#include
#include
#include
#include
void load(lua_State* L)
{
lua_newtable(L);
lua_pushnumber(L,0.3);
lua_setfield(L,-2,“r”);
lua_pushnumber(L,0.1);
lua_setfield(L,-2,“g”);
lua_pushnumber(L,0.4);
lua_setfield(L,-2,“b”);
lua_setglobal(L,“background”);
lua_getglobal(L,“background”);
if (!lua_istable(L,-1)) {
printf(“‘background‘ is not a table.\n” );
return;
}
lua_getfield(L,-1,“r”);
if (!lua_isnumber(L,-1)) {
printf(“Invalid component in background color.\n”);
return;
}
int r = (int)(lua_tonumber(L,-1) * 255);
lua_pop(L,1);
lua_getfield(L,-1,“g”);
if (!lua_isnumber(L,-1)) {
printf(“Invalid component in background color.\n”);
return;
}
int g = (int)(lua_tonumber(L,-1) * 255);
lua_pop(L,1);
lua_getfield(L,-1,“b”);
if (!lua_isnumber(L,-1)) {
printf(“Invalid component in background color.\n”);
return;
}
int b = (int)(lua_tonumber(L,-1) * 255);
printf(“r = %d, g = %d, b = %d\n”,r,g,b);
lua_pop(L,1);
lua_pop(L,1);
return;
}
int main()
{
lua_State* L = luaL_newstate();
load(L);
lua_close(L);
return 0;
}
上面的代码将输出和之前代码相同的结果。
lua_newtable是宏,其原型为:#define lua_newtable(L) lua_createtable(L, 0, 0)。调用该宏后,Lua会生成一个新的table对象并将其压入栈中。
lua_setglobal是宏,其原型为:#define lua_setglobal(L,s) lua_setfield(L,LUA_GLOBALSINDEX,(s))。调用该宏后,Lua会将当前栈顶的值赋值给第二个参数指定的全局变量名。该宏在执行成功后,会将刚刚赋值的值从栈顶弹出。
3. 调用Lua函数:
调用函数的API也很简单。首先将待调用函数压入栈,再压入函数的参数,然后使用lua_pcall进行实际的调用,最后将调用结果从栈中弹出。见如下代码:
代码如下:
#include
#include
#include
#include
#include
const char* lua_function_code = “function add(x,y) return x + y end”;
void call_function(lua_State* L)
{
//luaL_dostring 等同于luaL_loadstring() || lua_pcall()
//注意:在能够调用Lua函数之前必须执行Lua脚本,否则在后面实际调用Lua函数时会报错,
//错误信息为:“attempt to call a nil value.”
if (luaL_dostring(L,lua_function_code)) {
printf(“Failed to run lua code.\n”);
return;
}
double x = 1.0, y = 2.3;
lua_getglobal(L,“add”);
lua_pushnumber(L,x);
lua_pushnumber(L,y);
//下面的第二个参数表示带调用的lua函数存在两个参数。
//第三个参数表示即使带调用的函数存在多个返回值,那么也只有一个在执行后会被压入栈中。
//lua_pcall调用后,虚拟栈中的函数参数和函数名均被弹出。
if (lua_pcall(L,2,1,0)) {
printf(“error is %s.\n”,lua_tostring(L,-1));
return;
}
//此时结果已经被压入栈中。
if (!lua_isnumber(L,-1)) {
printf(“function ‘add‘ must return a number.\n”);
return;
}
double ret = lua_tonumber(L,-1);
lua_pop(L,-1); //弹出返回值。
printf(“The result of call function is %f.\n”,ret);
}
int main()
{
lua_State* L = luaL_newstate();
call_function(L);
lua_close(L);
return 0;
}
篇4:Lua教程(三):表达式和语句
这篇文章主要介绍了Lua教程(三):表达式和语句,本文讲解了算术操作符、关系操作符、逻辑操作符、字符串连接、table构造器、控制结构等内容,需要的朋友可以参考下
一、表达式:
1. 算术操作符:
Lua支持常规算术操作符有:二元的“+”、“-”、“*”、“/”、“^”(指数)、“%”(取模),一元的“-”(负号),所有这些操作符都可用于实数。然而需要特别说明的是取模操作符(%),Lua中对该操作符的定义为:
代码如下:
a % b == a - floor(a / b) * b
由此可以推演出x % 1的结果为x的小数部分,而x - x % 1的结果则为x的整数部分。类似的,x - x % 0.01则是x精确到小数点后两位的结果。
2. 关系操作符:
Lua支持的关系操作符有:>、<、>=、<=、==、~=,所有这些操作符的结果均为true或false。
操作符==用于相等性测试,操作符~=用于不等性测试。这两个操作符可以应用于任意两个值。如果两个值的类型不同,Lua就认为他们不等。nil值与其自身相等。对于table、userdata和函数,Lua是通过引用进行比较的。也就是说,只有当他们引用同一个对象时,才视为相等。如:
代码如下:
a = {}
a.x = 1
a.y = 0
b = {}
b.x = 1
b.y = 1
c = a
其结果是a == c,但a ~= b。
对于字符串的比较,Lua是按照字符次序比较的。
3. 逻辑操作符:
Lua支持的逻辑操作符有:and、or和not。与条件控制语句一样,所有的逻辑操作符都将false和nil视为假,其他的结果均为真。和其他大多数语言一样,Lua中的and和or都使用“短路原则”。在Lua中有一种惯用写法“x = x or v”,它等价于:if not x then x = v end。这里还有一种基于“短路原则”的惯用写法,如:
代码如下:
max = (x >y) and x or y
这等价于C语言中max = (x >y) ? x : y。由于x和y均为数值,因此它们的结果将始终为true。
4. 字符串连接:
前一篇Blog已经提到了字符串连接操作符(..),这里再给出一些简单的示例。
代码如下:
/>lua
>print(“Hello ” .. “World)
Hello World
>print(0 .. 1) --即使连接操作符的操作数为数值类型,在执行时Lua仍会将其自动转换为字符串。
01
5. table构造器:
构造器用于构建和初始化table的表达式。这是Lua特有的表达式,也是Lua中最有用、最通用的机制之一。其中最简单的构造器是空构造器{},用于创建空table。我们通过构造器还可以初始化数组,如:
代码如下:
days = {”Sunday“,”Monday“,”Tuesday“,”Wednesday“,”Thursday“,”Friday“,”Saturday“}
for i = 1,#days do
print(days[i])
end
--输出结果为
--Sunday
--Monday
--Tuesday
--Wednesday
--Thursday
--Friday
--Saturday
从输出结果可以看出,days在构造后会将自动初始化,其中days[1]被初始化为”Sunday“,days[2]为”Monday“,以此类推。
Lua中还提供了另外一种特殊的语法用于初始化记录风格的table。如:a = { x = 10, y = 20 },其等价于:a = {}; a.x = 10; a.y = 20
在实际编程时我们也可以将这两种初始化方式组合在一起使用,如:
代码如下:
polyline = {color = ”blue“, thickness = 2, npoints = 4,
{x = 0, y = 0},
{x = 10, y = 0},
{x = -10, y = 1},
{x = 0, y = 1} }
print(polyline[”color“]);
print(polyline[2].x)
print(polyline[4].y)
--输出结果如下:
--blue
--10
--1
除了以上两种构造初始化方式之外,Lua还提供另外一种更为通用的方式,如:
代码如下:
pnames = { [”+“] = ”add“, [”-“] = ”sub“, [”*“] = ”mul“, [”/“] = ”div“}
print(opnames[”+“])
i = 20; s = ”-“
a = { [i + 0] = s, [i + 1] = s .. s, [i + 2] = s..s..s }
print(a[22])
对于table的构造器,还有两个需要了解的语法规则,如:
代码如下:
a = { [1] = ”red“, [2] = ”green“, [3] = ”blue“, }
这里需要注意最后一个元素的后面仍然可以保留逗号(,),这一点类似于C语言中的枚举。
代码如下:
a = {x = 10, y = 45; ”one“, ”two“, ”three“ }
可以看到上面的声明中同时存在逗号(,)和分号(;)两种元素分隔符,这种写法在Lua中是允许的,
我们通常会将分号(;)用于分隔不同初始化类型的元素,如上例中分号之前的初始化方式为记录初始化方式,而后面则是数组初始化方式。
二、语句:
1. 赋值语句:
Lua中的赋值语句和其它编程语言基本相同,唯一的差别是Lua支持“多重赋值”,如:a, b = 10, 2 * x,其等价于a = 10; b = 2 * x。然而需要说明的是,Lua在赋值之前需要先计算等号右边的表达式,在每一个表达式都得到结果之后再进行赋值。因此,我们可以这样写变量交互:x,y = y,x。如果等号右侧的表达式数量少于左侧变量的数量,Lua会将左侧多出的变量的值置为nil,如果相反,Lua将忽略右侧多出的表达式。
2. 局部变量与块:
Lua中的局部变量定义语法为:local i = 1,其中local关键字表示该变量为局部变量。和全局变量不同的是,局部变量的作用范围仅限于其所在的程序块。Lua中的程序可以为控制结构的执行体、函数执行体或者是一个程序块,如:
下面的x变量仅在while循环内有效。
代码如下:
while i <= x do
local x = i * 2
print(x)
i = i + 1
end
如果是在交互模式下,当执行local x = 0之后,该变量x所在的程序即以结束,后面的Lua语句将被视为新的程序块。如果想避免此类问题,我们可以显式的声明程序块,这样即便是在交互模式下,局部变量仍然能保持其块内有效性,如:
代码如下:
do
local a2 = 2 * a
local d = (b ^ 2 - 4 * a) ^ (1 / 2)
x1 = (-b + d) / a2
x2 = (-b - d) / a2
end --a2和d的作用域至此结束。
和其它编程语言一样,如果有可能尽量使用局部变量,以免造成全局环境的变量名污染。同时由于局部变量的有效期更短,这样垃圾收集器可以及时对其进行清理,从而得到更多的可用内存。
3. 控制结构:
Lua中提供的控制语句和其它大多数开发语言所提供的基本相同,因此这里仅仅是进行简单的列举。然后再给出差异部分的详细介绍。如:
1). if then else
代码如下:
if a < 0 then
b = 0
else
b = 1
end
2). if elseif else then
代码如下:
if a < 0 then
b = 0
elseif a == 0 then
b = 1
else
b = 2
end
3). while
代码如下:
local i= 1
while a[i] do
print(a[i])
i = i + 1
end
4). repeat
代码如下:
repeat
line = io.read()
until line ~= ”“ --直到until的条件为真时结束。
print(line)
5). for
代码如下:
for var = begin, end, step do --如果没有step变量,begin的缺省步长为1。
i = i + 1
end
需要说明的是,for循环开始处的三个变量begin、end和step,如果它们使表达式的返回值,那么该表达式将仅执行一次。再有就是不要在for的循环体内修改变量var的值,否则会导致不可预知的结果。
6). foreach
代码如下:
for i, v in ipairs(a) do --ipairs是Lua自带的系统函数,返回遍历数组的迭代器。
print(v)
end
for k in pairs(t) do --打印table t中的所有key。
print(k)
end
见如下示例代码:
代码如下:
days = {”Sunday“, ”Monday“, ”Tuesday“, ”Wednesday“, ”Thursday“, ”Friday“, ”Saturday“ }
revDays = {}
for k, v in ipairs(days) do
revDays[v] = k
end
for k in pairs(revDays) do
print(k .. ” = " .. revDays[k])
end
--输出结果为:
--Saturday = 7
--Tuesday = 3
--Wednesday = 4
--Friday = 6
--Sunday = 1
--Thursday = 5
--Monday = 2
7). break
和C语言中的break语义完全相同,即跳出最内层循环。
篇5:Lua教程(一):Lua脚本语言介绍
这篇文章主要介绍了Lua教程(一):Lua脚本语言介绍,需要的朋友可以参考下
Lua 是一个扩展式程序设计语言,它被设计成支持通用的过程式编程,并有相关数据描述的设施, Lua 也能对面向对象编程,函数式编程,数据驱动式编程提供很好的支持。 它可以作为一个强大、轻量的脚本语言,供任何需要的程序使用。 Lua 以一个用 clean C 写成的库形式提供。(所谓 Clean C ,指的 ANSI C 和 C++ 中共通的一个子集)
作为一个扩展式语言,Lua 没有 “main” 程序的概念:它只能 嵌入 一个宿主程序中工作,这个宿主程序被称作 embedding program 或简称为 host ,
宿主程序可以通过调用函数执行一小段 Lua 代码,可以读写 Lua 变量,可以注入 C 函数让 Lua 代码调用。 这些扩展的 C 函数,可以大大的扩展了 Lua 可以处理事务的领域,这样就可以订制出各种语言, 而它们共享一个统一的句法格式的框架。 Lua 的官方发布版就包含了一个叫做 lua 的简单的宿主程序,它用 Lua 库提供了一个保证独立的 Lua 解释器。
Lua 是一个自由软件,它的使用许可决定了对它的使用过程一般没有任何保证。 这份手册中描述的东西的实现,可以在 Lua 的 www.lua.org 找到,
跟其它的许多参考手册一样,这份文档有些地方比较枯燥。 关于 Lua 的设计想法的探讨,可以看看 Lua 网站上提供的技术论文。 有关用 Lua 编程的细节介绍,可以读一下 Roberto 的书,Programming in Lua (Second Edition) 。
★ 详解Lua中repeat...until循环语句的使用方法
【Lua教程(十七):C API简介(共5篇)】相关文章:
杏花春雨湿信风2022-10-05
理科毕业生简历自我评价2023-10-13
c语言函数知识点总结2023-04-03
收信日记2022-06-11
FreePOPs实现Gmail客户端收信2022-11-13
提供思考和帮助《逆商》读书笔记2022-11-10
计算机专业策划书范文2023-02-26
nosql数据库 tiger2023-07-02
关于创业时机的文章2023-09-29
豆蔻年华,谢谢你们陪着我现代诗歌欣赏2022-12-30