加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

Lua基础

发布时间:2020-12-14 21:59:07 所属栏目:大数据 来源:网络整理
导读:Lua基础 Lua没有main的概念,只能嵌入到一个宿主程序中工作,这个宿主程序被称作embedding program 或 host. ddd : ddd是一串最多三位的十进制数字,用来描述一个字符. 10,如果该字符后面接一个数字5,则105 下面五种描述了完全相同的字符串: a = “abcn123

Lua基础

  • Lua没有main的概念,只能嵌入到一个宿主程序中工作,这个宿主程序被称作embedding program 或 host.

  • ddd : ddd是一串最多三位的十进制数字,用来描述一个字符. 10,如果该字符后面接一个数字5,则105

  • 下面五种描述了完全相同的字符串:
    a = “abcn123”;
    a = [[abc
    123]];
    a = [==[ – [后面立刻跟了一个换行符被忽略
    abc
    123]==];

  • 长注释 –[ …….
    … ]
    [[
    /type

    ]] 这种类型的字符串可以包含多行,且不会解释转义序列;如果第一个是换行符会自动忽略;
    nil,number,string,boolean (nil,false为假其它都为真包括0),function,userdata:

  • 用来将任意C数据保存在Lua变量中,该类型仅被定义了赋值和相同性判断;
  • 使用metatable,可以为userdata自定义一组操作,;
  • 不能在Lua中创建出来,也不能在Lua中修改,只能通过API;
    thread,
    table:
  • 可以用任何东西做索引,可以包含任何类型的值(nil除外);
  • a.name 来表示a[“name”];

– table,userdata,thread变量本身不会真正的存放值,仅仅放了一个对象的引用;

  • 数字与字符串之间的自动转换,需要完全掌握数字怎么转换成字符串,可以使用字符串库中的format函数;

  • 三种变量: 全局,局部,table域
    对全局和table域的访问的含义可以通过metatable来改变,如
    t[i]<==>gettable_event(t,i).

全局变量:

是放在一个特定的lua table域中,这个table叫做environment table简称环境,每一个函数都有对一个环境的引用,函数可见的全局变量都放在这个函数所引用的环境表里,可以调用getfenv取得环境,setfenv改变环境,对全局变量x的访问等价于_env.x,又等价于gettable_event(_env,x)

Chunk: Lua的一个执行单元,一个Chunk就是一串语句段,每个语句段以分号结束;
Lua把一个Chunk当作一个拥有不定参数的匿名函数,因此Chunk可以定义局部变量,接收参数,和返回值;

Block: 语句块,语法上说,语句块就是语句段,一个语句块可以被显式的写成一个单独的语句段 do block end

assignment赋值: 多个变量以,分开,左边多的变量会被赋成nil,右边多的变量会忽略,赋值是先运算完再进行赋值;

metatable改变全局/table域中的赋值操作: t[i] = val; 等价于settable_event(t,i,val); 对于全局变量的赋值x=val; 等价于_env.x = val; 即settable_event(_env,“x”,val);

控制语句

while exp do block end;
repeat block until exp;
if exp then block {else if exp then block} end;
return [explist]: 用于从函数或chunk中返回值,且返回的值个数不止一个;
break用于从while/for/repeat中跳出循环;
注:break/return必须用于一个语句块的最后一句,即chunk最后一句/end前/until前/else前,如果要在一个语句块的中间return或 break,则可以do break end;

for:
- 数字形式: for v= start,end,step do block end;
step没给出的情况为1,start end step 三个表达式只运算一次,计算只在循环开始之前,且必须为数字;V的值是局部变量,当for循环结束后就不能再使用;
- 迭代器函数形式(泛型for): for namelist in explist do block end
namelist如: var1,var2,var3,…;
explist只会被计算一次,返回三个值: 迭代器函数,状态,迭代器初始值;
循环变量是局部变量,循环结束后不能再使用;
for i,v in ipairs(a) do print(v) end –遍历值
for k in pairs(t) do print(j) end –遍历键

表达式

  • 如果一个表达式昨为一个独立的语句出现,则其返回值将会被对齐到零个元素,即忽略所有的返回值;
    如果表达式作为列表的最后一个元素,则不会有任何对齐;
    任何情况下,Lua将表达式看成单一元素,忽略掉除第一个元素外的任何值;
    如:
    f(); 返回0个结果
    g(f(),x); f()仅取第一个元素
    g(x,f()); f()取所有的元素
    a,b,c = x,f(); f()调整为2个结果
    a,c = f(),x; f()取1个结果,c = nil
    a,b = … ; a,b分别取可变参数的第1,2个元素
    a,c = f(); f()调整为3个元素

  • {f()}; 用f()的所有元素创建一个列表; {…} 可变参数创建一个列表;
    {f(),nil}; f()被调整为1个结果;
    被括号括起来的表达式永远被当作一个值,除非函数调用(如上面)才可能会是多个值. (f(x,y)) 无论f(x,y)返回多少个值,永远只取第一个值!

总结:出发f()在最后一个表达式时才取全部数据,否则只取1个元素;

操作符

  • 比较操作符
    数字和字符串的比较与常规的比较一样; 对table,thread以及function的比较是以引用的方式进行: 两个对象只有在指向同一个的东西时才会相等;

  • 逻辑操作符
    and,or,not 把false nil为假其它都为真;
    and和or遵循短路原则,and在第一个为false或nil时返回第一个,否则返回第二个; or 在第一个不为false或nil时返回第一个,否则返回第二个; (该方法在完成真假判断的同时还有一定的选择功能) 假与1,假或2

  • .. 字符串连接操作符,数字后面加..时必须先加上空格以防解释成小数; 10 .. 2

  • “#” 取长度操作符,长度是字节数,一个字符一个字节;
    table的长度n是以最后一个非nil所在的下标来计算的,即t[n] ~=nil 而
    t[n+1] = nil; 数组是1…n,如果:break/return必须用于一个语句块的最后一句,则可以do break en数组中存在nil值则#t则可能得到任意一个nil值的下标;function

  • table构造
    每次table构造子被执行,就会造出一个新的table,
    用table实现一个list:

>list = nil;
local i = 0;
for line in io.lines() do
-- 逆向构建一个list,next指向之前的list
list = {next = list,value = line}
i = i + 1;
if(i == 5) then break end
end
-- 遍历
while list ~= nil do
print(list.value)
list = list.next

函数

  • 函数调用
    prefixexp arg: prefix与arg是先被求值,如果prefixexp是function,则这个函数就会被参数调用,否则prefixexp的元方法会被调用,元方法的第一个参数就是prefixexp的值,后面的参数就是arg;
    参数的构造: args = ([explist]); = tableconstructor; = string;

  • 调用方法: predixexp : name args,类似v.name(args) –> v.name(v.args)
    f{fields} –> f({fields}); 而这样的形式:f’string’或f”string”/ f[[string]] 都–>f(‘string’),这里的参数列表仅仅是一个字符串;

  • 尾调用return functioncall;
    尾调用: 被调用的函数将重用调用它的函数的堆栈,因此无论多少此尾调用层次都不会造成过多的资源消耗; 同时尾调用将删除调用它的函数的任何调试信息;

  • 函数定义
    function funcname funcbody;
    local function name funcbody;
    function t.a.b.c.f () body end –> t.a.b.c.f = function () body end;
    local function f () body end –> local f; f = function () body end;

    • C++中可以通过函数指针来调用不同的函数,但函数指针的参数是不变的,而Lua中是可以调用可变参数的可变函数,通过unpack()来实现可变参数:
      func(unpack(a)); –a为可变参数
    • …变长参数: 变长参数… Lua中将参数存放在一个arg的表中;
    • 函数返回多个值时,可以用select(index,func)来选择值,选择的范围是第index个到最后一个;
  • 命名参数:
    即函数可以直接用一个表来作为参数,同时在表中对参数进行命名,这样在函数定义中容易应用
>function f(arg)
return arg.v1 + arg.v2
end
print(f{v1 = 4,v2 = 5}); <-->9
  • 形参列表
    如果实参该表达式处于另一表达式的中间,则表达式只返回一个值,如果放于最后一个,则不会做调整,参考前面的表达式
    …变长形参: 实参中存在表达式,则表达式放于最后位置时会做相应的调整,多余的实参传入…中
    不边长形参: 则是正常的表达式用法

  • 可视规则
    变量的局部性类似C++

函数是first class即函数和其它值一样可以被存放在变量中,也可以放在表中,函数的参数和函数的返回值;

闭包 closure

词法界定: 一个函数嵌套另一个函数时,内部的函数可以访问外部的函数的局部变量; 这些局部变量在内部函数中被称为upvalue/外部局部变量; 这个功能与C++不同,如下例:

function out()
local i = 1;
return function () local j = 0; j = j + 1; i = i + 1; return i,j; end
end
print(out()());
print(out()());  --两个函数生成了不同的函数对象,out()返回不同的函数指针
local j = out(); --一个函数指针,i为堆栈中的局部变量
print(j()); --此时i = 1 + 1;j = 0 + 1; <-->2,1
print(j()); --此时表现出了与C++的不同处,C++上面j()返回后内存被销毁,此处i将重新分配内存;而Lua的闭包功能就是函数仍然保留了前面j对象的内存,则局部变量i=2保留到了当前的函数,而j是局部变量,函数会重新得到所以不一样,因此 i = 2 + 1;j = 0 + 1; <-->3,1
  • 函数重定义
    函数可以存储在普通的变量内,可以很方便的重定义或预定义函数;通过do ..end来封装则可以创建安全的运行环境–也称作沙箱;
function add(a,b) return a + b end
do
local he= add;
add = function (a,b) return a*b end; -- 函数重定义
print(he(2,3)); -- he保留了旧的函数运算:2 + 3
print(add(2,3)); -- 重定义函数:2 * 3
end
print(add(2,3)); -- 重定义:2*3
--上面的例子中,将旧的add函数保存在了he局部变量中,通过do..end包装,可以保证he的局部性,同时如果在end前不执行add = he,则add函数会被永久替换;

协同程序

协同程序类似多线程,但其实现并不是多线程的机制,而只是类似非抢占线程,同一时刻只有有一个协程在运行(没有真正实现并发),只能被显示的挂起coroutine.yield(co)–没参数时挂起自己和恢复coroutine.resume(co);

协程相对多线程的优点:
- 1.协程是单线程,协程的切换仅仅是程序控制,没有线程上下文的切换问题,所以执行效率高;
- 2.协程不需要锁机制,也不需要处理互斥访问,不会有死锁;

协程注意点:

  • co = coroutine.create(function()…end) 协同创建的参数只有一个: 将要运行的协同程序封装成的函数,返回的co为thread类型
  • 三种状态: 挂起,运行,停止; 协同程序刚开始被创建时是挂起状态;企图resume终止状态下的协同程序时会返回false和error information
  • resume传递额外的参数:
thd = function(a)  --协同程序
while true do
if not a then a = 0 end
print(a .. ‘n’)
coroutine.field()
end
end
--每次通过thread类型创建协程时都会生产新的协程
co1 = coroutine.create(thd)
print(coroutine.resume(co1))   -->thd,nil
print(coroutine.resume(co1,4)) -->thd,nil --这里第一次启动协程的参数才是里面的a,后面传人的参数没有用,因为协程一直在while循环中运行或挂起,外部传人的变量并实际上不会改变局部变量a;

不对称性: 即挂起一个正在执行的协程的函数与使一个挂起的协程再次执行的函数不是同一个函数,所以由执行到挂起之间状态的转换函数是不同的

  • yield 与 resume之间的数据传递:
--yield可以传人参数,参数会返回给下一次对该函数使用resume的协程,如协程p中执行挂起时有yield(a,b),那么如果下次有local status,v1,v2 = coroutine.resume(p); 则v1 = a,v2 = b;
例子:
co = coroutine.create(
function () 
  local x = 0;
  while true do
    x = io.read()
    coroutine.yield(x+1,x+2)
  end
end
)

print(coroutine.resume(co));  --可以看成是函数调用然后返回了相应的值
-- 如果输入2; 则打印: true 3 4
  • 环境
    thread,userdata除了metadata外还关联一个环境表,多个对象可以共享一个环境表;

  • Coroutine
    类似多线程,就具体以后再看

  • C API
    堆栈: Lua和C传递值是通过一个虚拟栈来实现,栈上的每一个元素都是lua值(nil,number..); 每次lua调用C都会得到一个新的栈,该栈独立于C函数本身,也独立于以前的栈,里面包含lua传给C的数据也包含c返回给lua的结果;
    该堆栈的操作可以看成一个数组,可以通过索引来查找内容,
    1~n表示栈上的绝对位置即数组的位置; 而栈顶位于n的位置;
    因此-1~-n表示栈顶到栈底的过程;
    0不能作为索引;

  • 伪索引: 函数可以接受一个有效的索引–伪索引,通过它可以访问一些不在栈上的lua值,可以用来引用线程环境,函数环境,注册表等,
    线程的环境通常在伪索引LUA_GLOBALSINDEX,C环境则放在伪索引LUA_ENVIRONINDEX处;
    只要给定环境表的位置就可以访问该表,如访问全局变量的值
    lua_getfield(L,LUA_GLOBALSINDEX,varname)

  • C closure
    C函数被创建出来,可能需要一些值关联起来,从而生成一个C closure,这些被关联的值叫做upvalue,这些值对应的伪索引用lua_upvalueindex宏生产;

  • 注册表 用于保存任何C代码想要保存的lua值,伪索引LUA_REGISTRYINDEX

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读