`
univasity
  • 浏览: 800372 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

[LUA]《Programming in Lua》学习摘录

阅读更多

** 仅仅摘录自《Programming in Lua》,这里只是1-8章的摘录,希望了解更多的请阅读原文。

 

== 第一章 起步

- Chunks -
Chunk 是一系列语句,Lua 执行的每一块语句,比如一个文件或者交互模式下的每
一行都是一个Chunk。

**********************************
每个语句结尾的分号(;)是可选的
**********************************

 

- 全局变量 -
全局变量不需要声明,给一个变量赋值后即创建了这个全局变量,默认初始值为nil。

将变量赋值为nil,即删除一个变量。

 

- 词法约定 -
lua的保留字:
and  break  do  else  elseif
end  false  for  function  if
in  local  nil  not  or
repeat  return  then  true  until
while

Lua 是大小写敏感的.

单行注释:--

多行注释:--[[ --]]

- 命令行方式 -
lua [options] [script [args]]
-e:直接将命令传入Lua
-l:加载一个文件.
-i:进入交互模式.

prompt> lua -e "print(math.sin(12))" --> -0.53657291800043

**************************************************************************
 在运行以前,Lua 使用所有参数构造arg 表。脚本名索引为0,脚本的参数从1 开始
 增加。脚本前面的参数从-1 开始减少。
**************************************************************************


== 第二章 类型和值

Lua 是动态类型语言,变量不要类型定义。

Lua 中有8 个基本类型分别为:nil、boolean、number、string、userdata、function、thread 和table。

变量没有预定义的类型,每一个变量都可能包含任一种类型的值。

***********************************************************************
函数type 可以测试给定变量或者值,print(type("Hello world")) --> string
***********************************************************************

 

- Nil -
Lua 中特殊的类型,他只有一个值:nil;
一个全局变量没有被赋值以前默认值为nil;
给全局变量负nil 可以删除该变量。

 

- Boolean -
两个取值false 和true。

Lua 中所有的值都可以作为条件:
在控制结构的条件中除了false 和nil 为假,其他值都为真。所以Lua 认为0 和空串都是真。

 

- Number -
表示实数,Lua 中没有整数。

 

- String -
指字符的序列。

lua 是8 位字节,可以包含任何数值字符,包括嵌入的0。

Lua 中字符串是不可以修改的,你可以创建一个新的变量存放你要的字符串

****************************************************
 string 和其他对象一样,Lua 自动进行内存分配和释放
 可以使用单引号或者双引号表示字符串
****************************************************

..在Lua 中是字符串连接符(当在一个数字后面写..时,必须加上空格以防止被解释错)。

运行时,Lua 会自动在string 和numbers 之间自动进行类型转换,当一个字符串使
用算术操作符时,string 就会被转成数字。print("10" + 1) --> 11

可以使用[[...]]表示字符串。这种形式的字符串可以包含多行,也可以嵌套且不会
解释转义序列,如果第一个字符是换行符会被自动忽略掉。

*****************************
 字符串转数字:tonumber(args)
 数字转字符串:tostring(args)
*****************************

 

- Function -
函数是第一类值(和其他变量相同),意味着函数可以存储在变量中,可以作为函数
的参数,也可以作为函数的返回值。

 

- Userdata and thread -
userdata 可以将C 数据存放在Lua 变量中,userdata 在Lua 中除了赋值和相等比较外没有预定义的操作。
thread ?

 

- Table -


== 第三章 表达式

Lua 中的表达式包括数字常量、字符串常量、变量、一元和二元运算符、函数调用。
还可以是非传统的函数定义和表构造。

 

- 基本运算符 -
优先级从高到低:
^
not  - (unary)
*  /
+  -
..
<  >  <=  >=  ~=  ==
and
or

******************************************************************
* and 和or 的运算结果不是true 和false,而是和它的两个操作数相关:                                *
* a and b -- 如果a 为false,则返回a,否则返回b                                                            *
* a or b  -- 如果a 为true,则返回a,否则返回b                                                               *
******************************************************************

 

- 表的构造 -
构造器是创建和初始化表的表达式。最简单的构造函数是{},用来创建一个空表。

*************************************************
 第一个元素索引为1
 元素间必须以','或';'划分
 每次调用构造函数,Lua 都会创建一个新的table
*************************************************

/\构造函数可以使用任何表达式初始化:
 tab = {sin(1), sin(2), sin(3), sin(4), sin(5), sin(6), sin(7), sin(8)}

/\初始化一个表作为record 使用可以这样:
 a = {x=0, y=0}  <-->  a = {}; a.x=0; a.y=0

/\使用table 构造一个list:
 list = nil
 for line in io.lines() do
   list = {next=list, value=line}
 end
 这段代码从标准输入读进每行,然后反序形成链表。

我们可以用[expression]显式地表示将被初始化的索引:
opnames = {["+"] = "add", ["-"] = "sub",
           ["*"] = "mul", ["/"] = "div"}
print(opnames["-"]) --> sub
**************************************************************************
 [expression]可以是除了nil的任意类型
 重复定义一个[expression],其值将以最后一个为准list = {[1]=123, [1]=321}
**************************************************************************

 

== 第四章 基本语法
Lua 像C 和PASCAL 几乎支持所有的传统语句:
赋值语句、控制结构语句、函数调用等,同时也支持非传统的多变量赋值、局部变量声明。

 

- 赋值语句 -
Lua 可以对多个变量同时赋值,变量列表和值列表的各个元素用逗号分开,赋值语
句右边的值会依次赋给左边的变量。a, b = 10, 2*x <--> a=10; b=2*x

/\可以这样进行交换变量的值:
             x, y = y, x -- swap 'x' for 'y'
 a[i], a[j] = a[j], a[i] -- swap 'a[i]' for 'a[j]'

当变量个数和值的个数不一致时:
 变量个数>值的个数 -> 按变量个数补足nil
 变量个数<值的个数 -> 多余的值会被忽略

*********************************************************
 遇到赋值语句Lua 会先计算右边所有的值然后再执行赋值操作
 多值赋值经常用来交换变量,或将函数调用返回给变量
*********************************************************

 

- 局部变量与代码块(block)-
使用local 创建一个局部变量,与全局变量不同,局部变量只在被声明的那个代码块内有效。
[代码块:指一个控制结构内,一个函数体,或者一个chunk(变量被声明的那个文件或者文本串)]。

当你想更好的控制局部变量的作用范围的时候可以用do..end划定block的界限(相当于C/C++的{..}块)。

*********************************
* 应该尽可能的使用局部变量,有两个好处:    *
* 1. 避免命名冲突                                       *
* 2. 访问局部变量的速度比全局变量更快.        *
*********************************

 

- 控制结构语句 -
控制结构的条件表达式结果可以是任何值,Lua 认为false 和nil 为假,其他值为真。

<if 语句>
01、
if conditions then
  //then-part
end;

 

02、
if conditions then
  //then-part
else
  //else-part
end;

 

03、
if conditions then
  //then-part
elseif conditions then
  //elseif-part
..   --->多个elseif
else
  //else-part
end;

 

<while 语句>
while condition do
  //statements;
end;

 

<repeat-until 语句>
repeat
  //statements;
until conditions;

 

<for 语句>

01.数值for循环
for var=exp1,exp2,exp3 do
  //loop-part
end
***********************************************************************
for 将用exp3 作为step 从exp1(初始值)到exp2(终止值),执行loop-part。
其中exp3 可以省略,默认step=1

有几点需要注意:                                            
1. 三个表达式只会被计算一次,并且是在循环开始前。           
2. 控制变量var 是局部变量自动被声明,并且只在循环内有效。     
3. 循环过程中不要改变控制变量的值,那样做的结果是不可预知的。
   如果要退出循环,使用break 语句。     
***********************************************************************

 

02.范型for 循环
-- print all values of array 'a'
for i,v in ipairs(a) do
    print(v)
end
****************************************
i不可手动赋值,默认从1开始递增
v为a[i]的值
如果a的索引为非默认的,该元素将被忽略掉
list = {['a']=a, b, c} --> b,c
****************************************

-- print all keys of table 't'
for k in pairs(t) do
    print(k)
end
********************************************
k不可手动赋值,默认从1开始递增
如果a的索引为非默认的,该元素将最后依次列出
list = {['a']=a, b, c, ['b']=d} --> 2,3,1,4
********************************************

范型for 和数值for 有两点相同:
1. 控制变量是局部变量
2. 不要修改控制变量的值

 

- break 和return 语句 -
break 语句用来退出当前循环(for、repeat、while)。在循环外部不可以使用。
return 用来从函数返回结果,当一个函数自然结束时,结尾会有一个默认的return。

Lua 语法要求break 和return 只能出现在block 的结尾一句。
**************************************************************************
有时候为了调试或者其他目的需要在block 的中间使用return 或者break,可以显式
的使用do..end 来实:
function foo ()
  return   --<< SYNTAX ERROR
  -- 'return' is the last statement in the next block
  do return end -- OK
  ...   -- statements not reached
end
**************************************************************************


== 第五章 函数
函数有两种用途:
1.完成指定的任务,这种情况下函数作为调用语句使用;
2.计算并返回值,这种情况下函数作为赋值语句的表达式使用。

function func_name (arguments-list)
  //statements-list;
end;
*********************************************************************
1.调用函数的时候,如果参数列表为空,必须使用()表明是函数调用。
2.当函数只有一个参数并且这个参数是字符串或者表构造的时候,()可有可无:
  print "Hello World" <--> print("Hello World")
  print [[a multi-line <--> print([[a multi-line
             message]]                 message]])
3.函数的参数可以被省略,默认值为Nil:
  function f(t, i)
      i = i or 0;
      return t+i;
  end
  print(f(1)); --> 1
*********************************************************************

 

- 多返回值 -
Lua 函数可以返回多个结果值,比如string.find,其返回匹配串“开始和结束的下标”
(如果不存在匹配串返回nil)。

*************************************
可以使用圆括号()强制使调用返回一个值。
*************************************

函数多值返回的特殊函数unpack,接受一个数组作为输入参数,返回数组的所有元素。

 

- 可变参数 -
Lua 函数可以接受可变数目的参数,和C 语言类似在函数参数列表中使用三点(...)
表示函数有可变的参数。Lua 将函数的参数放在一个叫arg 的表中,除了参数以外,arg
表中还有一个域n 表示参数的个数:
function g (a, b, ...) end
 CALL             PARAMETERS
 g(3)              a=3, b=nil, arg={n=0}
 g(3, 4)          a=3, b=4, arg={n=0}
 g(3, 4, 5, 8)  a=3, b=4, arg={5, 8; n=2}
Lua 会将前面的实参传给函数的固定参数,后面的实参放在arg 表中。

 

- 命名参数 -
Lua 的函数参数是和位置相关的,调用时实参会按顺序依次传给形参。

w = Window {
  x=0, y=0, width=300, height=200,
  title = "Lua", background="blue",
  border = true
}

function Window (options)
  -- check mandatory options
  if type(options.title) ~= "string" then
    error("no title")
  elseif type(options.width) ~= "number" then
    error("no width")
  elseif type(options.height) ~= "number" then
    error("no height")
  end
 
  -- everything else is optional
  _Window(options.title,
    options.x or 0, -- default value
    options.y or 0, -- default value
    options.width, options.height,
    options.background or "white", -- default
    options.border -- default is false (nil)
  )
end


== 第六章 再论函数
Lua 中的函数是带有词法定界(lexical scoping)的第一类值(first-class values)。
*****************************************************************************
第一类值指:在Lua 中函数和其他值(数值、字符串)一样,函数可以被存放在变量中,
            也可以存放在表中,可以作为函数的参数,还可以作为函数的返回值。
词法定界指:嵌套的函数可以访问他外部函数中的变量。
*****************************************************************************

 

Lua 中函数也可以没有名字,匿名的。
函数名实际上是一个指向函数的变量,和其他变量是一样的。
*******************************************
function foo (x) return 2*x end
这实际上是Lua 语法的特例,下面是原本的函数:
foo = function (x) return 2*x end
*******************************************

 

以其他函数作为参数的函数在Lua 中被称作高级函数(higher-order function)。
在Lua 中,高级函数与普通函数没有区别,它们只是把“作为参数的函数”当作第一类值(first-class value)处理而已。
**************************************************************************
table 标准库提供一个排序函数,接受一个表作为输入参数并且排序表中的元素:
  table.sort(network, function (a,b) return (a.name > b.name) end)
**************************************************************************

 

- 闭包 -
当一个函数内部嵌套另一个函数定义时,内部的函数体可以访问外部的函数的局部
变量,这种特征我们称作词法定界。
%% 具体内容较复杂,请自行查看原文 %%

 

- 非全局函数 -

 

- 正确的尾调用(Proper Tail Calls) -
当函数最后一个动作是调用另外一个函数时,我们称这种调用尾调用。
尾调用之后程序不需要在栈中保留关于调用者的任何信息,递归的层次可以无限制的。


== 第七章 迭代器与泛型for

- 泛型for的语义 -
在循环过程中泛型for 在自己内部保存迭代函数,实际上它保存三个值:
 迭代函数、状态常量、控制变量

文法:
for <var-list> in <exp-list> do
  <body>
end
***********************************************
<var-list>是以一个或多个逗号分隔的变量名列表,
<exp-list>是以一个或多个逗号分隔的表达式列表,
通常情况下exp-list 只有一个值:迭代工厂的调用。
***********************************************

 

在很多情况下变量列表也只有一个变量
我们称变量列表中第一个变量为控制变量,其值为nil 时循环结束。

for var_1, ..., var_n in explist do block end
等价于
do
  local _f, _s, _var = explist
  while true do
    local var_1, ... , var_n = _f(_s, _var)
    _var = var_1
    if _var == nil then break end
    block
  end
end

 

- 无状态的迭代器 -
无状态的迭代器是指不保留任何状态的迭代器
无状态迭代器的典型的简单的例子是ipairs,他遍历数组的每一个元素。

迭代的状态包括被遍历的表(循环过程中不会改变的状态常量)和当前的索引下标
(控制变量),ipairs 和迭代函数都很简单,我们在Lua 中可以这样实现:
function iter (a, i)
  i = i + 1
  local v = a[i]
  if v then
      return i, v
  end
end

 

function ipairs (a)
   return iter, a, 0
end

*****************************************************************************
我们应该尽可能的写无状态的迭代器,因为这样循环的时候由for 来保存状态,不
需要创建对象,花费的代价小;如果不能用无状态的迭代器实现,应尽可能使用闭包;尽
可能不要使用table 这种方式,因为创建闭包的代价要比创建table 小,另外Lua 处理闭
包要比处理table 速度快些。
******************************************************************************

 

- 真正的迭代器 -

function allwords (f)
    -- repeat for each line in the file
    for l in io.lines() do
        -- repeat for each word in the line
        for w in string.gfind(l, "%w+") do
            -- call the function
            f(w)
        end
    end
end


== 第八章 编译·运行·错误信息
虽然我们把Lua 当作解释型语言,但是Lua 会首先把代码预编译成中间码然后再执行。

dofile 编译并执行代码,如果发生错误会抛出错误信,每次执行都要重新编译
loadfile 编译但不执行代码,如果发生错误会返回错误代码,只需要编译一次便可多次运行
dostring 与dofile相似,从一个串中读入
loadstring 与loadfile相似,从一个串中读入

*****************************************
function dofile (filename)
  local f = assert(loadfile(filename))
  return f()
end
*****************************************

loadfile 和loadstring 都不会有边界效应产生,他们仅仅编译chunk 成为自己内部实现的一个匿名函数。
通常对他们的误解是他们定义了函数。Lua 中的函数定义是发生在运行时的赋值而不是发生在编译时。

 

- require 函数 -
Lua 提供高级的require 函数来加载运行库。

粗略的说require 和dofile 完成同样的功能但有两点不同:
1. require 会搜索目录加载文件
2. require 会判断是否文件已经加载避免重复加载同一文件。

由于上述特征,require在Lua 中是加载库的更好的函数。

 

- 错误处理 -
你也可以通过调用error函数显式地抛出错误,error的参数是要抛出的错误信息。
print "enter a number:"
n = io.read("*number")
if not n then error("invalid input") end

Lua提供了专门的内置函数assert来完成上面类似的功能:
print "enter a number:"
n = assert(io.read("*number"), "invalid input")

assert首先检查第一个参数,若没问题,assert不做任何事情;否则,assert以第二个参数作为错误信息抛出。第二个参数是可选的。注意,assert会首先处理两个参数,然后才调用函数,所以下面代码,无论n是否为数字,字符串连接操作总会执行:
n = io.read()
assert(tonumber(n), "invalid input: " .. n .. " is not a number")

当函数遇到异常有两个基本的动作:返回错误代码或者抛出错误。选择哪一种方式,没有固定的规则,不过基本的原则是:对于程序逻辑上能够避免的异常,以抛出错误的方式处理之,否则返回错误代码。

 

如果在Lua中需要处理错误,需要使用pcall函数封装你的代码。
pcall在保护模式(protected mode)下执行函数内容,同时捕获所有的异常和错误。若一切正常,pcall返回true以及“被执行函数”的返回值;否则返回nil和错误信息。

 

第十章例举了两个完整的例子... 其他章节在继续学习中...^-^

最后附上《Programming In Lua》的word版:

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics