又LAG隨性筆記
  • 關於我
  • 作品集
  • 生活隨筆
  • 與我聯絡
  • 隨手扎

隨手扎

September 30, 2020

【30天Lua重拾筆記17】基礎2: pcall, xpcall, load (eval, exec, apply)

eval / load

作為一個直譯的環境,幾乎一定會有一個與eval等價的能力,不過在Lua叫做load,與其他程式相同,這個功能是強大而應該小心使用的。下方程式會新建一個全局變數g1,使這個宣告過程原本是一段字串。

load([[
  g1 = 1 -- create a global variable g1
  ]])()

print(g1) -- => Output: 1

要注意的是,load不回直接執行,其實其返回一個包裝函式:

f = load[[g2 = 2]]
print(type(f)) -- => function

print(g2) -- => Output: nil
f()
print(g2) -- => Output: 2

ECMAScript

類似的JS也有。

不過相較於JS,Lua其實有更安全的作法(以後會提到)。

eval('var g1 = 1')
console.log(g1) // => Output: 1

Python

Python有兩個函式做類似事情–eval和exec。不過eval雖有返回值,但無法執行語句,只能執行表達式。所以本例中只能使用exec。exec永遠回傳None,比起eval更加強大。

September 29, 2020

【30天Lua重拾筆記16】基礎2: 多值返回&具名參數

回傳多值/多值返回

Lua函數可以返回多值。在我看來,這個特性是特殊的,只有少數語言真正做到多值返回。什麼意思?這表示在接收一個函數的返回值,可以輕易忽略掉主要值以外的結果。這些結果一般用於輔助,絕大多數時候,我們不關心、不需要,但有時候非常有作用。

先來看看Python裡的dict.get:

_d = {}
_v = _d.get("key", "value")

print(_v) # => Output: value

大多數時候,並不關心_v的值是否真的從_d來的,還是其實是個預設值。如果我們想要確定,那就要在多寫一次:

found = "key" in _d

我們可以替Lua寫一個相似的函數,但其還多返回一個用於解釋是否是有找到的值:

function get(dict, key, default)
  if dict[key] ~= nil then
    return dict[key], true
  else
    return default, false
  end
end

其實可以在簡化一些:

function get(dict, key, default)
  return (dict[key] or default), dict[key] ~= nil
end

這麼一來,平常可以這樣用:

September 28, 2020

【30天Lua重拾筆記15】基礎2: Label and Goto

Label & goto

這是一個強大的工具,要寫的漂亮並不容易,許多語言禁止了他。
Lua保有他。他很靈活,但你也應該慎重考慮是否真的應該使用。
而我認為,開發人員應該要是自由的,這才有創造的價值,這才能體現思考的藝術。就如同松本行弘說的:「語言體現思考的價值」
如果你的想法最初就是這樣想的,就先寫下來吧!「童子軍原則」別忘了逐漸改得更健壯。

Lua保有C/C++裡,goto的能力,可以跳轉到函數區塊內任意可見的標籤(Label)。標籤的形式由兩個冒號夾成:

:: <Label Name> ::

跳出多層迴圈

BTW, Python 認為如果你需要使用到這個功能,表示你應該在拆分程式碼。

有了這強大的能力可以做啥?可以學像ES6那樣跳出外部迴圈:

九九乘法表只計算到6x4

(()=>{
    outer:
    for(let j = 2; j <= 9; j++){
        let line_output = ""
        for(let i = 1; i <= 9; i++){
            line_output += `${j}x${i}=${j*i},\t`
            if(i > 3 && j > 5){
                console.log(line_output)
                break outer
            }
        }
        console.log(line_output)
    }
})()
September 27, 2020

【30天Lua重拾筆記14】基礎2: 控制-while、repeat迴圈

print("鐵人賽開始")

for day=1,30,1 do
  print("第" .. day .. "天: 每天寫文章")
end

print("鐵人賽結束")

for迴圈適合用於次數明確的情況。例如鐵人賽30天發文不間斷,每天發一篇,發完30完賽。向這樣知道進步條件(每天一篇),中止條件(30篇),就非常適合用 for 迴圈。

Repeat/until迴圈

但有時候只知道中止條件,過程並不確定。像是在第n天,就寫n篇文章來囤稿,對我而言會是幾天完賽呢?

print("鐵人賽開始")
day = 1 -- 第幾天
completed = 0 -- 完成篇數

repeat
  completed = completed + day
  print("第" .. day .. "天: 每天寫文章,完成"..completed.."篇")
  day = day + 1
until completed > 30

print("鐵人賽結束,完成篇數:"..completed)  -- Output: 鐵人賽結束,完成篇數:36  -- (1+8)*8/2

鐵人賽開始
第1天: 每天寫文章,完成1篇
第2天: 每天寫文章,完成3篇
第3天: 每天寫文章,完成6篇
第4天: 每天寫文章,完成10篇
第5天: 每天寫文章,完成15篇
第6天: 每天寫文章,完成21篇
第7天: 每天寫文章,完成28篇
第8天: 每天寫文章,完成36篇
鐵人賽結束,完成篇數:36

需要每天寫文章,直到累積的文章超過30篇,也就完賽了。如果在第n天寫n篇,也只需要8天就完賽。

While迴圈

September 26, 2020

【30天Lua重拾筆記13】基礎2: 控制-For迴圈

相較於if,Lua的for迴圈有兩種,或說是三種。

進步的for迴圈

印出1-10:

for i = 1, 10, 1 do
  print(i)
end

很類似C語言

#include <stdio.h>

int main(void){
  int i = 0;
  for(i = 1; i <= 10; i++){
    printf("%d\n", i);
  }
}

不同的是以逗點代替分號,中止條件相同時仍會在執行一次,進步(step)使用表達是,會自動相加並重新賦予值。

了解後也可來寫成10到1的程式:

for i = 10, 1, -1 do
  print(i)
end

for-do也是一個區塊,於一開始宣告的變數僅do-end內部可見。這表示下方寫法中,進步條件指的是外部的i,其值為1。結果與輸出1-10相同。

i = 1
for i = 1, 10, i do
  print(i)
end

pairs & ipairs初探 - for-in迴圈

for-in迴圈用於迭代陣列(array)或表格(table)時。

Lua是沒有array型態的(還記得8種基礎型別嗎),關於這點之後會在說明。
至於什麼是table?可以想像成是ES6裡的object,或者更接近於Map

可以分別用ipairs和pairs來處理:

迭代陣列

September 25, 2020

【30天Lua重拾筆記12】基礎2: 控制 - 條件

分支條件控制 - if/elseif/else

Lua的分支控制條件就僅有這麼一組:if-then/elseif-then/else/end

It's all very simple

和其他語言一樣,elseif可以出現多次。你想用else if寫成巢狀也沒人會管你。他們兩個寫起來也真的蠻像的,除了你可能要多寫幾次end:

if true then
  print("if block")
elseif true then
  print("elseif block")
else
  print("else")
end
if true then
  print("if block")
else if true then
  print("elseif block")
else
  print("else")
end
end

注意到最後有兩個end。實際上我試故意將他這樣排版的,這樣兩個看起來比較像。

通常需要使用到巢狀分支,還是好好縮排比較好。

switch

雖然只有if蠻簡單的,但有時候會想要使用switch這類結構。沒事,可以模擬:

option = "one"

switch = {
  ["one"] = function () print "run one" end,
  ["tow"] = function () print "run two" end,
}

switch[option]() -- => run one
  • ««
  • «
  • 1
  • 2
  • 3
  •  … 
  • 16
  • 17
  • 18
  •  … 
  • 34
  • »
  • »»
© 又LAG隨性筆記 2025