Lua学习笔记-day2

5.3 具名实参

通过名称来指定实参

--os.rename,希望能接受两个具有名称的实参
rename{old="temp.lua",new="temp1.lua"}      --table构造式可以省略圆括号

--将rename改为只接受一个参数
function rename(arg)
    return os.rename(arg.old,arg.new)
end

如果一个函数拥有大量参数,其中大部分参数是可选的话,这种传递风格会特别有用。

--用于检查参数和设置默认参数值的函数
function Window(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

    _Window(options.title,
            options.x or 0,
            options.y or 0,
            options.width,
            options.height,
            options.background or "white",
            options.border
            )
end
--真正实现功能的函数
function _Window( x,y,width,height,title,background,border)
    print(x)
    print(y)
    print(width)
    print(height)
    print(title)
    print(background)
    print(border)
end

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

6 深入函数

  1. 函数与其他传统类型的值具有相同的权利。函数可以存储到变量中或table中,可以作为实参传递给其他函数,还可以作为其他函数的返回值
a = {p = print }        
a.p("Hello World")      --Hello World
print = math.sin        --现在 print 是正弦函数
a.p((print(1)))         --0.841470
sin = a.p               --sin引用print函数
sin(10,20)              -- 10   20

--两者一样,前者是后者的简化书写
function foo(x) reutn 2*x end
foo = function (x) return 2*x end

--table.sort,2个参数,前面的是table表,后面的是排序方式,这里是按照name字段、按反向的字符顺序来对这个table排序
table.sort(network,function (a,b) return(a.name>b.name) end)

--高阶函数,求导
function derivative(f,delta)
    delta =delta or 1e-4
    return funciton (x)
                return (f(x+delta)-f(x))/delta
            end
end

c = derivative(math.sin)
print(math.cos(1o),c(10))       --两者几乎相等

6.1 closure 闭合函数

函数里嵌套函数,位于内部的函数可以访问外部函数中的局部变量

function sortbygrade(names,grades)
    table.sort(names,function(n1,n2)
    return grades[n1] > grades[n2] end)
end                                         --在这个匿名函数内部,grades是“非局部的变量”

oldSin = math.sin
math.sin = function (x)
    return oldSin(x*math.pi/180)
    end

--do-end作为一个程序块
do
    local oldSin = math.sin
    local k = math.pi/180
    math.sin = function(x)
        return oldSin(x*k)
        end
end

6.2 非全局的函数

--三种相同的局部函数声明方式
Lib={}
Lib.foo = function (x,y) return x+y end
Lib.goo = funciton (x,y) return x*y end

Lib={ foo=function (x,y) return x+y end, goo = function(x,y) return x*y end} --table构造式法定义

Lib = {}
function Lib.foo (x,y) return x+y end
function Lib.goo (x,y) return x*y end  

local f = function (<参数>)
    <函数体>
    end

local funtion f (<参数>)
    <函数体>
end

--定义递归的局部函数
local fact                      --一定要先完成fact的定义,然后才能再使用fact进行自身的递归
fact = function (n)             
    if n == 0 then return 1
    else return n*fact(n-1)
    end
    end

6.3 正确的尾调用

  1. function f(x) return g(x) end一个函数调用是另一个函数的最后一个动作,该调用时一条“尾调用”
  2. 尾调用,程序不需要保存任何关于该函数的栈信息,当g返回时,执行控制权可以直接返回到调用f的那个点上,使得尾调用不耗费任何占空间,这种实现称为支持“尾调用消除”
  3. 一些不是尾调用的案例:
funcion f(x) g(x) end           --要丢弃g返回的临时结果
return g(x)+1                   --必须加法一次
return x or g(x)                --必须调整为一个返回值
return (g(x))                   --必须调整为一个返回值,注:前面提到圆括号内只能有一个值
  1. Lua中只有 return <func>(<args>)这样的调用形式才算尾调用,参数可以是任意复杂的表达式

7.1 迭代器与closure

--普通迭代器
function values(t)
    local i = 0 
    return function () 
        i = i + 1
        return t[i]
    end
end

t = {10,20,30}
iter = values(t)
while true do
    local element = iter()
    if element == nil then break end
    print(element)
end

--读取文件,并逐行查找需要的单词
function allwords()
    local line = io.read()
    local pos = 1
    return function ()
        while line do 
            local s,e = string.find(line,"w$",pos)
            if s then
                pos = e + 1
                return string.sub(line,s,e)
            else
                line = io.read()
                pos = 1
            end
        end
        return nil
    end
end

7.3 无状态的迭代器

  1. ipairs用来迭代数组(关键字key是number)可以输出nil,pairs碰到nil会直接跳过
-- ipairs和pairs的区别 --
--ipairs
-ipairs工厂
local 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

--pairs工厂
function pairs(t)
    return next, t, nil
end

for k, v in next, t do
    <loop body>
end
    
tbl = {"alpha", "beta", [3] = "uno", ["two"] = "dos"}
 
for i,v in ipairs(tbl) do    --输出前三个(到第一个不是数字的key
    print( tbl[i] )
end
 
for i,v in pairs(tbl) do    --全部输出
    print( tbl[i] )
end
  1. 可以不通过pairs调用直接使用next,nil可以不写,lua中自动将for循环中表达式列表的结果调整为3个值
    for k, v in next, t do <loop body> end
  2. 无状态迭代器可以遍历链表的迭代器
local function getnext(list, node)
    if not node then
        return list
    else
    return node.next
    end
end

function traverse(list)
    return getnext, list, nil
end

这里将链表的头结点作为恒定状态,将当前节点作为控制变量

list = nil
for line in io.lines() do list = {val = line, next = list} end

for node in traverse(list) do print(node.val) end

7.4具有复杂状态的迭代器

  1. 循环过程中恒定状态总是同一个table,但这个table 的内容可以发生改变,通常可以忽略泛型for提供的第二个参数(控制变量
  2. 迭代器重写allwords
local iterator

function allwords()
    local state = {line = io.read(), pos = 1}
    return iterator,state
end

function iterator(state)
    while state.line do
        local s, e = string.find(state.line,"%w+", state.pos)
        if s then
            state.pos = e + 1
            return string.sub(state.line,s,e)
        else
            state.line = io.read()
            state.pos = 1
        end
    end
    return nil
end

7.5 真正的迭代器

迭代器只是为每次迭代提供一些成功后的返回值,更准确的叫“生成器”

function allwords(f)
    for line in io.lines() do 
        for word in string.gmatch(line,"%w+") do
            f(word)
        end
    end
end

--这个迭代器,需要传入一个描述循环体的函数
--还可以使用一个匿名函数作为循环体
local count = 0
allwords(function(w) if w == "hello" then count = count + 1 end end)
print(count)

8 编译、执行与错误

dofile与loadfile的区别

loadfile不会引发错误,只是返回错误值并不处理错误,

如果需要多次运行一个文件,那么只需在调用一次loadfile后,多次调用它的返回结果。

loadstring与loadfile类似,前者是从一个字符串中读取代码,后者是从文件中读取

f = loadstring(“i = i + 1”)

f变成了一个函数,每次调用时就执行“i = i + 1”

开销较大慎用,并且可能会导致难以理解的代码。

i = 32
local i = 0
f = loadstring("i = i + 1; print(i)")
g = function() i = i + 1 print(i) end
f()                     -->33
g()                     -->1

loadstring总是在全局环境中编译它的字符串

8.3 错误

print"enter a number:"
n = assert(io.read("*number"),"invalid input")

assert函数检查第一个参数是否为true。若为true则简单地返回该参数;否侧引发一个错误。第二个参数是一个可选的信息字符串

函数io.open,用于打开一个文件们如果一个文件不存在,那么它应返回nil,并附加一条错误消息,就可以采取适当的做法来处理异常情况

local file, msg
repeat
print"enter a file name:"
local name = io.read()
if not name then return end	--无输入
file, msg = io.open(name,"r")
if not file then print(msg) end --如果file为nil,那么打印错误消息
until file

在这里我们可以使用assert来检测操作

file = assert(io.open(name,"r"))

如果io.open失败,assert就引发一个错误

8.4 错误处理与异常

所有的Lua活动都是由应用程序的一次调用而开始的,这类调用通常是要求Lua执行一个程序块。如果执行中发生了错误,此调用会返回一个错误代码,这样应用程序就能采取适当的行动来处理错误。

如果需要在Lua中处理错误,则必须使用函数pcall来包装需要执行的代码

function foo()
	<some code>
	if inexpectant conditions then error() end
	<some code>
    print(a[i])
    <some code>
end

--使用pcall来调用foo
if pcall(foo) then
    <常规代码>
else
    <错误处理代码>
end

9 协同程序

与线程差不多,一条执行序列,拥有自己独立的栈、局部变量和指令指针,同时又与其他协同程序共享全局变量和其他大部分东西

9.1 协同程序基础

co = coroutine.create(function() print("hi") end)
print(co)		-->thread:0x8071d98

一个协同程序可以出于4种不同状态:挂起(suspended)、运行(running)、死亡(dead)和正常(normal)

9.2 管道和过滤器

--消费者-生产者问题
function producer()
	while true do
		local x = io.read()
		send(x)				--发送给消费者,将x挂起
    end
end

function consumer()
    while true do
        local x = receive()
        io.write(x,"\n")
    end
end

function receive()
    local status, value = coroutine.resume(producer)
    return value
end

function send(x)
    coroutine.yield(x)
end

producer = coroutine.create(function()
    while true do
          local x = io.read()
          send(x)
     end
    end)

--通过消费者启动,唤醒生产者,返回一个新值之后停止运行,等待再次唤醒

扩展实现“过滤器”,一种位于生产者和消费者之间的处理功能,它既是一个消费者又是一个生产者,它唤醒生产者促使其产生新值,然后又将变换后的值传递给消费者。

function receive(prod)
	local status, value = coroutine.resume(prod)
	return value
end

function send(x)
	coroutine.yield(x)
end

function producer()
	return coroutine.create(function()
	while true do
		local x = io.read()
		send(x)
	end
	end)
end

function filter(prod)
    return coroutine.create(function()
        for line = 1, math.huge do
                local x = receive(prod)
                x = string.format("%5d %s",line,x)
                send(x)
        end
        end)
end

function consumer(prod)
    while true do
        local x = receive(prod)
        io.write(x,"\n")
    end
end

consumer(filter(producer()))

11 数据结构

11.1 数组

Lua中习惯一般是以1作为数组的起始索引。Lua库和长度操作符都遵循这个约定。如果你的数组不是从1开始的,那就无法使用这些功能

11.2 矩阵与多维数组

  1. 矩阵的表示方式:数组的数组,也就是一个table里每个元素都是另外一个table(跟C差不多,直接声明更烦琐一些

  2. 两个索引合并为一个索引

    --两个索引是整数
    mt = {}
    for i = 1, N do
    	for j = 1, M do
    		mt[(i - 1) * M + j] = 0
    	end
    end
    
    --索引是字符串,拼接起来,中间使用一个字符来分隔
    m[s .. ":" .. t]
    --其中,s和t都不能包含冒号,否则会变成 "a::b"
    
    • 通过“稀疏矩阵”表示一个图,这个矩阵大多数元素为0或者nil,当m, n位置上有一个值x,即表示图中的节点m和n是相连的,权重(cost)为x。

    • 在Lua中只需要为非nil元素付出空间

    • 不能对稀疏矩阵使用长度操作符(#),因为在有效条目之间存在nil值,所以一般使用pairs对稀疏矩阵遍历,只读那些非nil的元素

      function mult(a, rowindex, k)
      	local row = a[rowindex]
      	for i, v in pairs(row) do
      		row[i] = v * k
      	end
      end
      
    • table中key是无序的,所以使用pairs的迭代并不保证会按递增次序来访问元素

11.3 链表

list = nil
list = {next = list, value = v}
local l = list
while l do
    <操作l.value>
    l = l.next
end    

Lua中很少用链表,一般都使用更简单的方式来表示数据,eg:通过一个无限大的数组来表示一个栈

11.4 队列与双向队列

List = {}
function List.new()
    return {first = 0, last = -1}
end

function List.pushfirst(list, value)
    local first = list.first - 1
    list.first = first
    list[first] = value
end

function List.pushlast(list, value)
    local last = list.last + 1
    list.last = last
    list[last] = value
end

function List.popfirst(list)
    local first = list.first
    if first > list.last then error("list is empty") end
    local value = list[first]
    list[first] = nil
    list.first = first + 1
    return value
end

function List.poplast(list)
    local last = list.last
    if list.first > last then error("list is empty") end
    local value = list[last]
    list[last] = nil
    list.last = last - 1
    return value
end

11.5 集合与无序组

  1. 集合就是把集合元素作为索引放入一个table中,只需要用该值来索引table,并查看结果是否为nil

  2. 包,也叫“多重集合”,与普通集合不同子啊其每个元素可以出现多次。在Lua中包的表示类似于集合表示,只不过包需要将一个计数器与table的key关联,若要插入一个元素,则需要递增其计数器:

    function insert(bag, element)
    	bag[element] = (bag[element] or 0) + 1
    end
    
    function remove(bag, element)
    	local count = bag[element]
    	bag[element] = (count and count > 1) and count - 1 or nil --不太明白
    end
    --只有当计数器已存在或大于0时,才保留它
    

11.6 字符串缓冲

--文件读取1
local buff = ""
for line in io.lines() do
	buff = buff .. line .. "\n"
end

--文件读取2
local t = {}
for line in io.lines() do
	t[#t + 1] = line .. "\n"
end
local s = table.concat(t)

读取1,假设每行有20个字节,已读2500行,那么buff现在就是一个50KB大小的字符串,当Lua作字符串连接buff … line … "\n"时,就创建了一个长50020字节的新字符串,并且从buff中辅助了50000字节到这个新字符串

读取2相比于读取1,将一个table作为字符串缓冲,使用table.concat,会将给定列表中的所有字符串连接起来,并返回连接的结果。

11.7 图

结点表示为对象及边表示为结点间的引用

每个结点表示为一个table,这个table有两个字段:name(结点的名称)和adj(与此结点邻接的结点集合),额外一个table来将名称对应到结点。

local function name2node(graph, name)
	if not graph[name] then
		graph[name] = {name = name, adj = {}}
     end
    reutrn graph[name]
end
  1. 构造一个图,逐行地读取一个文件,文件中的每行都有两个结点名称,表示在两个结点之间有一条边,边的方向从第一个结点到第二个结点。
function readgraph()
	local graph = {}
	for line in io.lines() do
		local namefrom, nameto = string.match(line,"(%S+)%s+(%S+)")
		local from = name2node(graph, namefrom)
		lcoal to = name2node(graph, nameto)
		from.adj[to] = true
	end
	return graph
end
  1. 使用图的算法
function findpath(curr, to, path, visited)
	path = path or {}
	visited = visited or {}
	if visited[curr] then		--判定结点是否访问过
		return nil
	end
	visited[curr] = true		--将结点标记为已访问过
	path[#path + 1] = curr		
	if curr == to then
		return path
	end
	
	for node in pairs(curr.adj) do
		local p = findpath(node, to, path, visited)
		if p then return p end
	end
	path[#path] = nil
end
  1. 测试
function printpath(path)
	for i = 1, #path do
		print(path[i].name)
	end
end

g = readgraph()
a = name2node(g, "a")
b = name2node(g, "b")
p = findpath(a, b)
if p then printpath(p) end
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值