programming-in-lua-1st-t01

來源: programming-in-lua-2ed.pdf


更新記錄

item note
20160817 第一版

目錄


Chapter 1

Chunks

  • Each piece of code that Lua executes, such as a file or a single line in interac-tive mode, is called a chunk.
  • A chunk is simply a sequence of commands (or statements).
  • 單行的程式
    可以使用分號(semicolon)也可以不使用分號
    1
    2
    3
    4
    5
    6
    a = 1
    b = a*2
    a = 1;
    b = a*2;
    a = 1; b = a*2
    a = 1 b = a*2 -- ugly, but valid

Some Lexical Conventions (词法约定)

  • 變數定義不可以使用數字開頭
  • 變數定義可以使用低線(_)開頭
    1
    2
    i j i10 _ij
    aSomewhatLongName _INPUT

Global Variables

  • 預設定義為全域變數
  • 沒有定義的變數使用時將會回傳nil
    1
    2
    3
    print(b) --> nil
    b = 10
    print(b) --> 10

Types and Values

basic types

eight basic types in Lua:

  • nil : 表示未初始化的變數
    變數若設定為nil,即為刪除變數
  • boolean : false or true
    只有false及nil會回傳false, 其它為true
  • number : 數字
    ex. 4 0.4 4.57e-3 0.3e12 5e+20
  • string : 字串
  • userdata,
  • function
  • thread
  • table.

    1
    2
    3
    4
    5
    6
    7
    print(type("Hello world")) --> string
    print(type(10.4*3)) --> number
    print(type(print)) --> function
    print(type(type)) --> function
    print(type(true)) --> boolean
    print(type(nil)) --> nil
    print(type(type(X))) --> string

  • type(x)回傳為字串


String

  • Strings in Lua are immutable values
    表示不能去修改字串內容,需要產生一個新的字串內容以符合需求
    1
    2
    3
    4
    a = "one string"
    b = string.gsub(a, "one", "another") -- change string parts
    print(a) --> one string
    print(b) --> another string

使用 [[ 及 ]] 定義多行字串內容

  • 使用 [[ 及 ]] 定義多行字串內容
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    page = [[
    <html>
    <head>
    <title>An HTML Page</title>
    </head>
    <body>
    <a href="http://www.lua.org">Lu
    </body>
    </html>
    ]]

concatenation operator(連接運算符號)

  • The .. is the string concatenation operator in Lua
    1
    print(10 .. 20) --> 1020

取得字串長度

  • In Lua 5.1, you can get the length of a string using the operator `#’
    1
    2
    3
    4
    5
    6
    7
    a = "hello"
    print(#a) --> 5
    print(#"good\0bye") --> 8

    for i=1, #a do
    print(a[i])
    end

Table

  • table { } 實現一個array,此array可以存放number,string或其它,但不可以是nil

    1
    2
    3
    4
    5
    6
    7
    8
    9
    a = {} -- create a table and store its reference in 'a'
    k = "x"
    a[k] = 10 -- new entry, with key="x" and value=10
    a[20] = "great" -- new entry, with key=20 and value="great"
    print(a["x"]) --> 10
    k = 20
    print(a[k]) --> "great"
    a["x"] = a["x"] + 1 -- increments entry "x"
    print(a["x"]) --> 11
  • providing a.name as syntactic sugar for a[“name”]

    1
    2
    3
    a.x = 10 -- same as a["x"] = 10
    print(a.x) -- same as print(a["x"])
    print(a.y) -- same as print(a["y"])

Chapter 3 Expressions

優先順序

seq item
1 ^
2 not # - (unary)
3 * / %
4 + -
5 ..
6 < > <= >= ~= ==
7 and
8 or
  • ex
    1
    2
    3
    4
    5
    a+i < b/2+1 <--> (a+i) < ((b/2)+1)
    5+x^2*8 <--> 5+((x^2)*8)
    a < y and y <= z <--> (a < y) and (y <= z)
    -x^2 <--> -(x^2)
    x^y^z <--> x^(y^z)

3.6 Table Constructors

  • ex1

    1
    a = {x=10, y=20}
  • This previous line is equivalent to these commands:

    1
    a = {}; a.x=10; a.y=20
  • We can mix record-style and list-style initializations in the same constructor:

    1
    2
    3
    4
    5
    6
    polyline = {color="blue", thickness=2, npoints=4,
    {x=0, y=0},
    {x=-10, y=0},
    {x=-10, y=1},
    {x=0, y=1}
    }
  • Each of the elements polyline[i] is a table representing a record:

    1
    2
    print(polyline[2].x) --> -10
    print(polyline[4].y) --> 1

Chapter 4 Statement

4-1 Assignment

  • Assignment is the basic means of changing the value
    a = “hello” .. “world”
    multiple assignment

    1
    2
    3
    a, b = 10, 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(即除刪)

    1
    2
     a, b, c = 0, 1
    print(a, b, c) --> 0 1 nil

4-2 Local Variables and Blocks

  • Besides global variables, Lua supports local variables

    1
    2
    j = 10 -- global variable
    local i = 1 -- local variable
  • Unlike global variables, local variables have their scope limited to the block

  • It is good programming style to use local variables whenever possible
    Local variables help you avoid cluttering the global environment with unnecessary names.
    1
    2
    3
    4
    5
    6
    7
    local a, b = 1, 10
    if a < b then
    print(a) --> 1
    local a -- '= nil' is implicit
    print(a) --> nil
    end -- ends the block started at 'then'
    print(a, b) --> 1 10

4-3 Control Structures

  • if,while,repeat,for 等需要end表示結束(terminates)
  • Lua treats as true all values different from false and nil

if then else

  • ex1

    1
    2
    3
    4
    5
    6
    if a < 0 then a = 0 end
    if a < b then return a else return b end
    if line > MAXLINES then
    showpage()
    line = 0
    end
  • ex2

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    if op == "+" then
    r = a + b
    elseif op == "-" then
    r = a - b
    elseif op == "*" then
    r = a*b
    elseif op == "/" then
    r = a/b
    else
    error("invalid operation")
    end

The for statement

  • 由exp1到exp2, 每次增加exp3(若無exp3則預設值為1)

    1
    2
    3
    for var=exp1,exp2,exp3 do
    <something>
    end
  • ex.

    1
    2
    for i=1,f(x) do print(i) end
    for i=10,1,-1 do print(i) end
  • If you want to end a for loop before its normal termination, use break

  • The generic for loop traverses all values returned by an iterator function:
    1
    2
    -- print all values of array 'a'
    for i,v in ipairs(a) do print(v) end

4-4 break and return

  • The break and return statements allow us to jump out of a block.
  • This statement breaks the inner loop (for, repeat, or while) that contains it
    1
    2
    3
    4
    5
    local i = 1
    while a[i] do
    if a[i] == v then break end
    i = i + 1
    end

Chapter5 Function

function

  • function定義方式
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    a={[1]=11,[2]=12,[3]=10}                                                                                                               

    function add (a)
    local sum = 0
    for i,v in ipairs(a) do
    sum = sum + v
    end
    return sum
    end

    print(add(a))

    xx :[/tmp/tt]# lua t1.lua
    33

function parameter

function f(a, b) return a or b end

CALL PARAMETERS
f(3) a=3, b=nil
f(3, 4) a=3, b=4
f(3, 4, 5) a=3, b=4 (5 is discarded)

如何設定給函數預設參數值

  • 未給預參數值n, 則此時為nil
  • 當未代入參數,則設定預設值n為1
    1
    2
    3
    4
    function incCount (n)
    n = n or 1
    count = count + n
    end


Chapter 6 -More About Function

Lua為First Class Value
指任何程式中的實體(ex. function,object,..)
即表示:function可以被當成參數傳遞或回傳
絕大多數語言中,數值與基礎型別都是第一類物件,然而不同語言中對函數的區別很大

  • C語言與C++中的函數不是第一類物件,因為在這些語言中函數不能在執行期創造,而必須在設計時全部寫好
  • Scheme中的函數是第一類物件,因為可以用lambda語句來創造匿名函數並作為第一類物件來操作

Closure(閉合函數)

將函數寫在另一個函數裡面,內部函數可以取得外部變數中的區域變數

  • 範例內容
    c1及c2是同一個函數創建兩個不同的Closure
    個自擁有區域變數i

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    function newCounter()
    local i = 0
    return function ()
    i = i + 1
    return i
    end
    end

    c1 = newCounter()
    print(c1())
    print(c1())

    c2 = newCounter()
    print(c2())
    print(c1())
    print(c2())

  • 執行結果

    1
    2
    3
    4
    5
    6
    gk350a :[/tmp/tt]# lua t2.lua 
    1
    2
    1
    3
    2

非全域的函數(non-global function)

  • When we store a function into a local variable, we get a local function
  • 此local function會被限制在某些做用區使用
  • package can use these local functions:

    1
    2
    3
    local f = function(<param>)
    <body>
    end

  • 正確方式

    1
    2
    3
    4
    5
    6
    7
    8
    local fact
    fact = function (n)
    if n == 0 then return 1
    else return n*fact(n-1)
    end
    end

    print(fact(3))

  • 錯誤的方式
    在recursive funtion時,lua變數需要提前宣告
    在fact(n-1)時fact尚未定義,看來lua是無js的variable hositing方式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    local fact = function (n)
    if n == 0 then return 1
    else return n*fact(n-1)
    end
    end

    print(fact(3))

    # lua local-fu.lua
    lua: local-fu.lua:3: attempt to call global 'fact' (a nil value)
    stack traceback:
    local-fu.lua:3: in function 'fact'
    local-fu.lua:7: in main chunk
    [C]: ?
  • 此方式的寫法在node js是沒問題,看來lua及node js還有些不同

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    $ cat local-t1.js 
    var fact = function (n) {
    if(n == 0){
    return 1;
    } else {
    return n*fact(n-1);
    }
    }

    console.log("tt");
    console.log(fact(3));

    $ node local-t1.js
    tt
    6

提前宣告

在indirect recursive funtion時,lua變數需要提前宣告

1
2
3
4
5
6
7
8
9
local f,g   -- ' forwared decalartions'

function g ()
<some code> f() <some code>
end

function f ()
<some code> g() <some code>
end

函數允許多個回傳值(5-1 Multiple Results)

  • 定義

    1
    2
    3
    function foo0 () end -- returns no results
    function foo1 () return "a" end -- returns 1 result
    function foo2 () return "a","b" end -- returns 2 results

  • 用法

    1
    2
    3
    x,y = foo2() -- x="a", y="b"
    x = foo2() -- x="a", "b" is discar
    x,y,z = 10,foo2() -- x=10, y="a", z="b"

Proper Tail Calls

  • Lua does tail-call elimination

  • Tail Calls 模式
    在撰寫遞迴時,每次的遞迴呼叫都會導致新的Stack frame產生,導致整個呼叫所編譯出來的Call Stack過大
    Tail Call算是一種演算法,指的是在該子程式的尾端再進行遞迴呼叫
    目的是希望藉此不必再額外產生新的Stack frame;不過必非所有的程式語言都有支援這樣特性
    出處:初探ECMAScript 6

    1
    2
    3
    4
    5
    6
    7
    8
    //es6
    function fact(n,acc=1){
    'use strict'
    if (n <=1) return acc;
    return factorial(n-1,n*acc);
    }

    console.log(fact(10000));
  • 範例
    房間移動範例,目前有4間如下
    使用tail calls寫法,讓程式更簡單

    1
    2
    3
    | room1 | room2 |
    -------------------------
    | room3 | room4 |
  • 範例程式
    xx :[~]# cat room.lua

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    function room1()
    print("entry room1")
    local move = io.read()
    if move == "south" then return room3()
    elseif move == "east" then return room2()
    else
    print("invaild move")
    return room1()
    end
    end

    function room2()
    print("entry room2")
    local move = io.read()
    if move == "south" then return room4()
    elseif move == "west" then return room1()
    else
    print("invaild move")
    return room2()
    end
    end

    function room3()
    print("entry room3")
    local move = io.read()
    if move == "north" then return room1()
    elseif move == "east" then return room4()
    else
    print("invaild move")
    return room3()
    end
    end

    function room4()
    print("entry room4")
    print ("congratulations!");
    end

    room1()

  • 測試
    :[~]# lua room.lua

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    entry room1
    north
    invaild move
    entry room1
    sorth
    invaild move
    entry room1
    south
    entry room3
    north
    entry room1
    east
    entry room2
    south
    entry room4
    congratulations!

Chapter7 Iterator and the Generic for

如何使用Closure建立itera

  • iter = values(t)
    此時建立Closure函數 (好處,不會響影其它的變數)
    回傳nil則表示結束
    $ cat chapter7-for.lua

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    function values (t)
    local i = 0
    return function () i = i + 1; return t[i] end
    end

    t = {10, 20, 30}
    iter = values(t)
    print(iter())
    print(iter())
    print(iter())
    print(iter())

    xx :[~]# lua chapter7-for.lua
    10
    20
    30
    nil
  • iterator: 與while一起使用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    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
  • xx :[~]# lua chapter7-while.lua

    1
    2
    3
    10
    20
    30
  • iterator: 與for一起使用
    用更少變數,程式變更簡單 (不需要iter變數)
    The generate for does all the bookkeeping for an iteration loop
    it keeps the iterator function interanlly, so we do not need the itera variable
    $ cat chapter7-for-t1.lua

    1
    2
    3
    4
    5
    6
    7
    8
    9
    function values (t)
    local i = 0
    return function () i = i + 1; return t[i] end
    end

    t = {10, 20, 30}
    for element in values(t) do
    print(element)
    end
  • xx :[~]# lua chapter7-for-t1.lua

    1
    2
    3
    10
    20
    30
  • 將目前輸入的單子找出來(只允語數字及字母), 且回傳每個找到的單字
    使用itera closure方式,將整個功能獨立分開(不會影響主程式變數)
    $ cat chapter7-words.lua

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    function allwords()
    local line = io.read()
    print('<1>line:',line) -- debug print
    local pos = 1
    return function ()
    print('<2>line:',line,' ,pos:',pos) -- debug print
    while line do
    local s, e = string.find(line,"%w+", pos) -- string.find: 會回找到的s,e
    if s then
    pos = e + 1
    return string.sub(line, s, e)
    else
    line = io.read()
    print('<3>line:',line) -- debug print
    pos = 1
    end
    end
    return nil
    end
    end

    for word in allwords() do
    print(word)
    end

  • xx :[~]# lua chapter7-words.lua
    string.find: 會回找到的s,e
    pattern %w: represents all alphanumeric characters.
    5.4.1 – Patterns

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    t11 t22 t33
    <1>line: t11 t22 t33
    <2>line: t11 t22 t33 ,pos: 1
    t11
    <2>line: t11 t22 t33 ,pos: 4
    t22
    <2>line: t11 t22 t33 ,pos: 8
    t33
    <2>line: t11 t22 t33 ,pos: 12
    taa tbb tcc
    <3>line: taa tbb tcc
    taa
    <2>line: taa tbb tcc ,pos: 4
    tbb
    <2>line: taa tbb tcc ,pos: 8
    tcc
    <2>line: taa tbb tcc ,pos: 12
    ##!!@@aa%%^^ ^^&& **((11
    <3>line: ##!!@@aa%%^^ ^^&& **((11
    aa
    <2>line: ##!!@@aa%%^^ ^^&& **((11 ,pos: 9
    11
    <2>line: ##!!@@aa%%^^ ^^&& **((11 ,pos: 25

Chapter 7-2 The Semantics of the Generic for

  • lua內部for語法動作程序
  • use the generic for itself to keep the iteration state
  • In this section we will see the facilities that the generic for offers to hold state
  • The syntax for the generic for is as follows

    1
    2
    3
    for <var-list> in <exp-list>
    <body>
    end
  • for實際範例

    1
    for k,v in piars(t) do print(k,v) end
  • for內部會保存3個變數

    • iterator function ( _f )
    • invariant state ( _s )
    • a control variable( _var )
  • More precisely, a construction like

    1
    for var_1,...,var_n in <explist> do <block> end
  • is equivalen to the following code:
    實際程序如下,內部會保存3個變數(_f,_s,_var)
    需要回傳(_f,_s,_var)
    此時以for而言內部只允了,儲存1狀態變數
    (若_f裡面需要由外部帶入,兩個以上的變數,要如何做?)

    1
    2
    3
    4
    5
    6
    7
    8
    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
    end
    end

Chapter 7-3 無需儲存狀態的iterator (Stateless Iterators)

表示自身不保存任可狀態的選代器(iterator)

  • $cat chapter7-3-ipairs.lua

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    local function iter (a,i)
    i = i + 1
    local v = a[i]
    if v then
    return i,v
    end
    end

    function myipairs(a)
    return iter, a, 0
    end
    a = {"one", "two", "three"}
    for i,v in myipairs(a) do
    print(i,v)
    end
  • xx :[~]# lua chapter7-3-ipairs.lua

    1
    2
    3
    1       one
    2 two
    3 three

Chapter 7-4 Iterators with Complex State

  • 通常,Iteration需要保存許多狀態,但for只提供1個(invariant state)及1個控制變量(a control variable)用於狀態的保存
  • 解決方式

    • 1.使用clousre
    • 2.或是將所有狀態打包為1個table,保存在(invariant state) -> 為此節要示範的例
  • $ cat chapter7-4-itra.lua

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    local iterator

    function allword()
    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

    for word in allword() do
    print(word)
    end

  • xx:[~]# lua chapter7-4-itra.lua

    1
    2
    3
    4
    t11 t22 t33
    t11
    t22
    t33

Chapter 7-5 True Iterators (allword iterator)

  • $ cat chapter7-5-allword.lua

    1
    2
    3
    4
    5
    6
    7
    8
    function allwords(f)
    for line in io.lines() do
    for word in string.gmatch(line, "%w+") do
    f(word)
    end
    end
    end
    allwords(print)
  • xx :[~]# lua chapter7-5-allword.lua

    1
    2
    3
    4
    t11 t22 t33
    t11
    t22
    t33

使用匿名函數做為參數輸入

  • $ cat chapter7-5-allword-t2.lua

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    function allwords(f)
    line = io.read()
    for word in string.gmatch(line, "%w+") do
    f(word)
    end
    end

    local count = 0
    allwords(function (w)
    if w == "hello" then count = count +1 end
    end)
    print(count)

  • xx :[~]# lua chapter7-5-allword-t2.lua

    1
    2
    t11 hello t22 hello t33
    2

Chapter8 Compilation,Excution,and Errors

Lua允語由外部載入precompiles source code(ex. dofile )

Chapter8-1 Compilation

  • dofile as a kind of primitive operation to run chunks of Lua code
  • dofile is actually an auxiliary function
    • loadfile does the hard work
    • loadfile loads a Lua chunk from a file, but it does not run the chunk
  • loadfile只是將代碼編譯,但不會執行,並且回傳一個函數返回
  • loadfile不會引發錯誤(它是返回錯誤並不處理錯誤)
  • dofile可以看成下例功能

    1
    2
    3
    4
    function dofile (filename)
    local f = assert (loadfile(filename))
    return f()
    end

  • dofile ([filename])
    Opens the named file and executes its contents as a Lua chunk.

  • load (func [, chunkname])
    Loads a chunk using function func to get its pieces.

  • loadfile ([filename])
    Similar to load, but gets the chunk from file filename or from the standard input, if no file name is given.

loadfile example

  • loadfile(“foo.lua”)
    之後,函數foo完成編譯,但還沒定義

  • f()
    定義此foo function

  • $ cat foo.lua

    1
    2
    3
    function foo (x)
    print(x)
    end
  • $ cat chapter8-2-loadfile.lua

    1
    2
    3
    4
    5
    f = loadfile("foo.lua")
    print(foo)
    f()
    print(foo)
    foo("ok")
  • gk350a :[~]# lua chapter8-2-loadfile.lua

    1
    2
    3
    nil
    function: 0xd6bcc0
    ok

Chapter8.2 Code (如何載入C定義的lua library )

  • package.loadlib (libname, funcname)
    Dynamically links the host program with the C library libname.
    funcname must follow the protocol (see lua_CFunction)

  • $ cat chapter8-2-loadlib.lua

    1
    2
    local path = "/usr/local/lib/lua/5.1/socket/core.so"
    local f = package.loadlib(path,"luaopen_socket")

Chapter8.3 Error (如何取得程式的錯誤訊息)

通過error()函數傳入錯誤訊息

  • error (message [, level])

    • Terminates the last protected function called and returns message as the error message. Function error never returns.
    • Usually, error adds some information about the error position at the beginning of the message
    • With level 1 (the default), the error position is where the error function was called
    • Level 2 points the error to where the function that called error was called
  • $ cat chapter8-3-err.lua

    1
    2
    3
    print "enter a number:"
    n = io.read("*number")
    if not n then error("invaild input") end
  • xx :[~]# lua chapter8-3-err.lua

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    enter a number:
    dd
    lua: chapter8-3-err.lua:3: invaild input
    stack traceback:
    [C]: in function 'error'
    chapter8-3-err.lua:3: in main chunk
    [C]: ?
    gk350a :[~]# lua chapter8-3-err.lua
    enter a number:
    10

使用assert()功能來實現

  • assert (v [, message])
    Issues an error when the value of its argument v is false (i.e., nil or false); otherwise, returns all its arguments.
    message is an error message; when absent, it defaults to “assertion failed!”

  • $ cat chapter8-3-err2.lua

    1
    2
    print "enter a number:"
    n = assert(io.read("*nubmer"),"invaild input")

Chapter8-4 Error Handling and Exceptions

若需要lua中處理錯誤,則必需使用pcall函數來包裝要執行的代碼

  • pcall (f, arg1, ···)
    Calls function f with the given arguments in protected mode.

  • $ cat chapter8-4-pcall.lua

    1
    2
    3
    4
    5
     cat chapter8-4-pcall.lua 
    local status,err = pcall(function () error({code=121}) end)
    print(status,err.code)
    local status,err = pcall(function () error("my error") end)
    print(status,err)

  • gk350a :[~]# lua chapter8-4-pcall.lua

    1
    2
    false   121
    false chapter8-4-pcall.lua:3: my error

Chapter9 Coroutines (協同程序)

  • A coroutine is similar to a thread (in the sense of multithreading)
    • it is a line of execution, with its own stack, its own local variables, and its own instruction pointer;
    • but sharing global variables and mostly anything else with other coroutines
  • 多thead,是指同時多個程序一起跑
  • 但coroutine,主要強調在多個thead中如何協同處理

Coroutine Manipulation

  • 2.11 – Coroutines
    • Lua supports coroutines, also called collaborative multithreading.
    • A coroutine in Lua represents an independent thread of execution
func note
coroutine.create create a coroutine , it does not start the coroutine execution.
coroutine.resume passing as its first argument a thread, the coroutine starts its execution
  • A coroutine can terminate its execution in two ways:
    normally, when its main function returns (coroutine.resume returns true)
    and abnormally

  • 5.2 – Coroutine Manipulation
    The operations related to coroutines comprise a sub-library of the basic library and come inside the table coroutine.

  • coroutine有下例狀態

    • suspended
      coroutine.create之後則為suspened
    • running
      coroutine.resume之後則為running,若程式跳完離開則變成dead
    • dead
    • normal
  • coroutine.yield()會讓一個運行中的coroutine變成suspended狀態(即此時,程式停止)

  • $cat chapter9-yield.lua

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    co = coroutine.create(function ()
    for i=1,5 do
    print("co",i)
    coroutine.yield()
    end
    end)

    coroutine.resume(co)
    print("t1",coroutine.status(co))
    coroutine.resume(co)
    coroutine.resume(co)
    coroutine.resume(co)
    coroutine.resume(co)
    coroutine.resume(co)
    print("t2",coroutine.status(co))
    coroutine.resume(co)

  • xx:[~]# lua chapter9-yield.lua

    1
    2
    3
    4
    5
    6
    7
    co      1
    t1 suspended
    co 2
    co 3
    co 4
    co 5
    t2 dead

通過resume-yield交換數據

  • $cat chapter9-2-data.lua

    1
    2
    3
    4
    5
    6
    co = coroutine.create( function (a,b,c)
    print("co",a,b,c)
    coroutine.yield(a+10,b+10,c+10)
    end)

    print(coroutine.resume(co,1,2,3))

  • xx:[~]# lua chapter9-2-data.lua

    1
    2
    co      1       2       3
    true 11 12 13

chapter9-2 producer-consumer problem

  • 由coroutine.yield(x)回傳值x
  • 此處為consumer-driver design
    • 由consumer啟動
    • 當consumber需要新值時,喚醒producer
    • producer產生1個新的值,停止運行,等待consumer
  • $ cat chapter9-2-pipe.lua

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    local producer

    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
    )

    print(receive())

  • xx:[~]# lua chapter9-2-pipe.lua

    1
    2
    test
    test

chapter 9-2 filter

  • filter example
    • 由filter來處理過程的資料
    • 決定是否打用producer資料
    • 是否將傳給consumer資料做格式轉換
chapter-9-2.png
  • $cat chapter9-2-filter.lua

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    function receive (prod)
    local status, value = coroutine.resume(prod)
    return value
    end

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

    function producer(x)
    return coroutine.create(function ()
    while true do
    print("producer:")
    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("filter: %5d %s",line,x)
    send(x)
    end
    end)
    end

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

    p = producer()
    f = filter(p)
    consumer(f)

  • xxx :[~]# lua chapter9-2-filter.lua

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    producer:
    test11
    cosumer:filter: 1 test11
    producer:
    aabb
    cosumer:filter: 2 aabb
    producer:
    ccdd
    cosumer:filter: 3 ccdd
    producer:

Chapter 11 Data Structures

  • all structure that other language offer
    • arrays, records, lists, queues, sets

Chapter 11-1 Arrays

  • 注意lua內部library的array皆為1開始

  • $cat chapter11-1-array.lua

    1
    2
    3
    4
    5
    a = {}
    for j=1, 1000 do
    a[j] = 0
    end
    print(#a)
  • gk350a :[~]# lua chapter11-1-array.lua

    1
    1000
  • 也可以建立非零開始的index

  • $cat chapter11-1-array2.lua
    1
    2
    3
    4
    a = {}
    for i=-5, 5 do
    a[i] = 0
    end

Chapter 11-2 matrix(矩陣)

  • $cat chapter11-2-matrix.lua

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    mt = {}

    N = 3
    M = 4
    for i=1,N do
    mt [i] = {}
    for j=1,M do
    mt[i][j] = 0
    end
    end
    print(#mt)
    print(#mt[1])
  • xx :[~]# lua chapter11-2-matrix.lua

    1
    2
    3
    4

Chapter 11-4 Queues

  • 由List.New初始化變數
    first: 指向list頭
    last: 指向list尾
  • pushfist: 由first新增
  • popfist: 由first取出
  • pushlast: 由last新增
  • poplast: 由last取出
  • $cat chapter11-4-queues.lua

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    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
  • test code

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    tt = {}
    tt = List.new()
    print(tt.first,tt.last)
    List.pushfirst(tt,"11")
    print("pushfirst 11",tt.first,tt.last)
    List.pushfirst(tt,"21")
    print("pushfirst 21",tt.first,tt.last)
    List.pushfirst(tt,"31")
    print("pushfirst 31",tt.first,tt.last)
    print(tt[-1],tt[-2],tt[-3])
    print(tt[-2])
    print(tt[-3])
    print("poplast value:",List.poplast(tt))
    print("poplast",tt.first,tt.last)
    print(tt[-1],tt[-2],tt[-3])
    print(tt[-2])
    print(tt[-3])
  • xx:[~]# lua chapter11-4-queues.lua

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    0       -1
    pushfirst 11 -1 -1
    pushfirst 21 -2 -1
    pushfirst 31 -3 -1
    11 21 31
    21
    31
    poplast value: 11
    poplast -3 -2
    nil 21 31
    21
    31

PARTIII; The Standard Library (待增加)


PARTIV; The C API (待增加)


其它

pairs及ipairs差異

  • pairs:依table內的順序一個一個找出來
    會找到全部

    1
    2
    3
    4
    5
    6
    7
    8
    9
    a={[1]=11,[2]=22,[5]=55,["erwin"]=90, ["peter"]=92}                                                                                    
    for i,v in pairs(a) do print(i,v) end

    xx :[/tmp/tt]# lua t1.lua
    2 22
    1 11
    p 92
    5 55
    e 90
  • ipairs
    pairs進階的找法,只依1,2,依順找,當沒有找到則離開

    1
    2
    3
    4
    5
    6
    7
    a={[1]=11,[2]=22,[5]=55,["erwin"]=90, ["peter"]=92}                                                                                    
    for i,v in ipairs(a) do print(i,v) end


    xx :[/tmp/tt]# lua t1.lua
    1 11
    2 22

參考來源