Lua 學習筆記(三)—— 表達式

1 數學運算操做符

1.1 % 操做符

Lua 中的 % 操做符與 C 語言中的操做符雖然都是取模的含義,可是取模的方式不同。
在 C 語言中,取模操做是將兩個操做數的絕對值取模後,在添加上第一個操做數的符號。
而在 Lua 中,僅僅是簡單的對商相對負無窮向下取整後的餘數。javascript

+++java

在 C 中,閉包

a1 = abs(a);
b1 = abs(b);
c = a1 % b1 = a1 - floor(a1/b1)*b1;

a % b = (a >= 0) ? c : -c;

在 Lua 中,函數

a % b == a - math.floor(a/b)*b

Lua 是直接根據取模定義進行運算。 C 則對取模運算作了一點處理。code

+++對象

舉例:blog

在 C 中ip

int a = 5 % 6;
int b = 5 % -6;
int c = -5 % 6;
int d = -5 % -6;

printf("a,b,c,d");--5,5,-5,-5

在 Lua 中內存

a = 5 % 6
b = 5 % -6
c = -5 % 6
d = -5 % -6

x = {a,b,c,d}

for i,v in ipairs(x) do
    print(i,v)
end


--> 5
--> -1
--> 1
--> -5

能夠看到,僅當操做數同號時,兩種語言的取模結果相同。異號時,取模結果的符號與數值均不相等。字符串

在 Lua 中的取模運算總結爲:a % b,若是 a,b 同號,結果取 a,b 絕對值的模;異號,結果取 b 絕對值與絕對值取模後的差。取模後值的符號與 b 相同。

2 比較操做符

比較操做的結果是 boolean 型的,非 truefalse

支持的操做符有:

< <= ~= == > >=

不支持 ! 操做符。

+++

對於 == 操做,運算時先比較兩個操做數的類型,若是不一致則結果爲 false。此時數值與字符串之間並不會自動轉換。

比較兩個對象是否相等時,僅當指向同一內存區域時,斷定爲 true。·

a = 123
b = 233
c = "123"
d = "123"
e = {1,2,3}
f = e
g = {1,2,3}

print(a == b)       --> false
print(a == c)       --> false      -- 數字與字符串做爲不一樣類型進行比較
print(c == d)       --> true       
print(e == f)       --> true       -- 引用指向相同的對象
print(e == g)       --> false      -- 雖然內容相同,可是是不一樣的對象
print(false == nil) --> false      -- false 是 boolean,nil 是 nil 型

方便標記,--> 表明前面表達式的結果。

+++

userdatatable 的比較方式能夠經過元方法 eq 進行改變。

大小比較中,數字和字符串的比較與 C 語言一致。若是是其餘類型的值,Lua會嘗試調用元方法 ltle

3 邏輯操做符

and,or,not

僅認爲 falsenil 爲假。

3.1 not

取反操做 not 的結果爲 boolean 類型。(andor 的結果則不必定爲 boolean)

b = not a           -- a 爲 nil,b 爲 true
c = not not a       -- c 爲 false

3.2 and

a and b,若是 a 爲假,返回 a,若是 a 爲真, 返回 b

注意,爲何 a 爲假的時候要返回 a 呢?有什麼意義?這是由於 a 多是 false 或者 nil,這兩個值雖然都爲假,可是是有區別的。

3.3 or

a or b,若是 a 爲假,返回 b,若是 a 爲真, 返回 a。與 and 相反。

+++

提示: 當邏輯操做符用於得出一個 boolean 型結果時,不須要考慮邏輯運算後返回誰的問題,由於邏輯操做符的操做結果符合本來的邏輯含義。

舉例

if (not (a > min and a < max)) then  -- 若是 a 不在範圍內,則報錯
    error() 
end

+++

3.4 其餘

andor 遵循短路原則,第二個操做數僅在須要的時候會進行求值操做。

例子


a = 5 x = a or jjjj() -- 雖而後面的函數並無定義,可是因爲不會執行,所以不會報錯。 print(a) -->5 print(x) -->5

經過上面這個例子,咱們應當對於邏輯操做有所警覺,由於這可能會引入一些未能及時預料到的錯誤。

4 鏈接符

..
鏈接兩個字符串(或者數字)成爲新的字符串。對於其餘類型,調用元方法 concat

5 取長度操做符

#

對於字符串,長度爲字符串的字符個數。

對於表,經過尋找知足t[n] 不是 nil 而 t[n+1] 爲 nil 的下標 n 做爲表的長度。

~~對於其餘類型呢?~~

5.1 例子

-- 字符串取長
print(#"abc\0")                         --> 4
-- 表取長
print(#{[1]=1,[2]=2,[3]=3,x=5,y=6})     --> 3
print(#{[1]=1,[2]=nil,[3]=3,x=5,y=6})   --> 1

6 優先級

由低到高:

or
and
 <     >     <=    >=    ~=    ==
 ..
 +     -
 *     /     %
 not   #     - (unary)
 ^

冪運算>單目運算>四則運算>鏈接符>比較操做符>and>or

7 Table 構造

Table 構造的 BNF 定義

tableconstructor ::= `{´ [fieldlist] `}´
fieldlist ::= field {fieldsep field} [fieldsep]
field ::= `[´ exp `]´ `=´ exp | Name `=´ exp | exp
fieldsep ::= `,´ | `;´

BNF 定義參考 BNF範式簡介

舉例:

a = {}
b = {["price"] = 5; cost = 4; 2+5}
c = { [1] = 2+5, [2] = 2, 8, price = "abc", ["cost"] = 4} -- b 和 c 構造的表是等價的


print(b["price"])   --> 5
print(b.cost)       --> 4
print(b[1])         --> 7       -- 未給出鍵值的,按序分配下標,下標從 1 開始

print(c["price"])   --> abc
print(c.cost)       --> 4
print(c[1])         --> 8       
print(c[2])         --> 2

注意:

  • 未給出鍵值的,按序分配下標,下標從 1 開始
  • 若是表中有相同的鍵,那麼以靠後的那個值做爲鍵對應的值

上面這兩條的存在使得上面的例子中 c1 的輸出值爲 8。

+++

若是表中有相同的鍵,那麼以靠後的那個值做爲鍵對應的值。

a = {[1] = 5,[1] = 6} -- 那麼 a[1] = 6

+++

若是表的最後一個域是表達式形式,而且是一個函數,那麼這個函數的全部返回值都會加入到表中。

a = 1
function order()
    a = a + 1
    return 1,2,3,4
end

b = {order(); a; order(); }

c = {order(); a; (order());}

print(b[1])                     --> 1       
print(b[2])                     --> 2       -- 表中的值並非一次把表達式都計算結束後再賦值的
print(b[3])                     --> 1       
print(b[4])                     --> 2       -- 表達式形式的多返回值函數

print(#b)                       --> 6       -- 表的長度爲 6                 
print(#c)                       --> 3       -- 函數添加括號後表的長度爲 3

8 函數

函數是一個表達式,其值爲 function 類型的對象。函數每次執行都會被實例化。

8.1 函數定義

Lua 中實現一個函數能夠有如下三種形式。

f = function() [block] end
local f; f = function() [block] end
a.f = function() [block] end

Lua 提供語法糖分別處理這三種函數定義。

function f() [block] end
local function f() [block] end
function a.f() [block] end

+++

上面 local 函數的定義之因此不是 local f = function() [block] end,是爲了不以下錯誤:

local f = function()
    print("local fun")
    if i==0 then 
        f()             -- 編譯錯誤:attempt to call global 'f' (a nil value)
        i = i + 1
    end
end

8.2 函數的參數

形參會經過實參來初始化爲局部變量。

參數列表的尾部添加 ... 表示函數能接受不定長參數。若是尾部不添加,那麼函數的參數列表長度是固定的。

f(a,b)
g(a,b,...)
h(a,...,b)              -- 編譯錯誤
f(1)                    --> a = 1, b = nil
f(1,2)                  --> a = 1, b = 2
f(1,2,3)                --> a = 1, b = 2

g(1,2)                  --> a = 1, b = 2, (nothing)
g(1,2,3)                --> a = 1, b = 2, (3)
g(1,f(4,5),3)           --> a = 1, b = 4, (3)
g(1,f(4,5))             --> a = 1, b = 4, (5)

+++

還有一種形參爲self的函數的定義方式:

a.f = function (self, params) [block] end

其語法糖形式爲:

function a:f(params) [block] end

使用舉例:

a = {name = "唐衣可俊"}
function a:f()
    print(self.name)
end
a:f()                       --> 唐衣可俊   -- 若是這裏使用 a.f(),那麼 self.name 的地方會報錯 attempt to index local 'self';此時應該寫爲 a.f(a)

: 的做用在於函數定義與調用的時候能夠少寫一個 self 參數。這種形式是對方法的模擬

8.3 函數調用

Lua 中的函數調用的BNF語法以下:

functioncall ::= prefixexp args

若是 prefixexp 的值的類型是 function, 那麼這個函數就被用給出的參數調用。 不然 prefixexp 的元方法 "call" 就被調用, call 的第一個參數就是 prefixexp 的值,接下來的是 args 參數列表(參見 2.8 元表 | Metatable)。

函數調用根據是否傳入 self 參數分爲 . 調用和 : 調用。
函數調用根據傳入參數的類型,能夠分爲參數列表調用、表調用、字符串調用

[待完善]

8.4 函數閉包

若是一個函數訪問了它的外部變量,那麼它就是一個閉包。

因爲函數內部的變量均爲局部變量,外界沒法對其進行訪問。這時若是外界想要改變局部變量的值,那麼就可使用閉包來實現這一目的。
具體的實現過程大體是這樣,函數內部有可以改變局部變量的子函數,函數將這個子函數返回,那麼外界就能夠經過使用這個子函數來操做局部變量了。

例子:利用閉包來實現對局部變量進行改變

-- 實現一個迭代器

function begin(i)
    local cnt = i

    return function ()      -- 這是一個匿名函數,實現了自增的功能;同時它也是一個閉包,由於訪問了外部變量 cnt
        cnt = cnt + 1
        return cnt
    end
end


iterator = begin(2)     -- 設置迭代器的初值爲 2 ,返回一個迭代器函數

print(iterator())           -- 執行迭代
print(iterator())

提示: 關於閉包的更多說明可參考JavaScript 閉包是如何工做的?——StackOverflow


參考連接

BNF範式簡介 (簡要介紹 BNF)
How do JavaScript closures work?——StackOverflow(詳細介紹了 Javascript 中閉包的概念)

相關文章
相關標籤/搜索