表達式中的敘述 |
|
while((c = fgetc(fh)) != EOF) { fputc(c, fh2); }
或
double x, y, z; if (strcmp(v, "0,0,0") == 0) printf("zeros\n"); else if(sscanf(v, "%f,%f,%f", &x, &y, &z) == 3) { printf("tuple (%d,%d,%d)\n", x, y, z); } else printf("unknown\n");
以 Lua 範例來說,請考慮以下狀況:
local w = (x+y+z)^2 + (x+y+z) + 1
包含一個單一的表達式,但為冗餘,而且通常僅透過將程式碼移至個別敘述中的賦值進行簡化計算
local xyz = x+y+z local w = xyz^2 + xyz + 1
甚至
local w; do local xyz = x+y+z w = xyz^2 + xyz + 1 end
這有些見仁見智,不過我們喪失了將計算作為單一表達式(w = ...
)的便利性。風格更偏向 [命令式]。
有很多方法,例如封閉、函式/元表格副作用(甚至備忘錄),可以透過單一表達式撰寫,但在此處並非有效率,而且通常是不佳的選擇
local w = (function() local xyz = x+y+z; return xyz^2 + xyz + 1 end)()
也可以這樣做
local w = (function(xyz) return xyz^2 + xyz + 1 end)(x + y + z)
這是 Scheme 使用 [let
] 所使用的相同轉換方式,且避免建立最外層上值。
儘管這不是有效的 Lua 語法,但可以選擇將此撰寫為以下單一表達式
local w = let xyz = x+y+z in xyz^2 + xyz + 1
至少有一些理論上的理由說明為什麼這樣有助用,在撰寫函式型程式設計風格的程式或用於修改其他 Lua 程式的程式,例如 MetaLua。事實上,Metalua 納入一個類似的機制以允許更有效率的程式碼。
請注意與 Lisp 的相似性
(let ((xyz (+ x y z))) (+ (* xyz xyz) xyz 1) )
以及 OCaml。
我們可以透過讓表達式呼叫一個接著執行一些賦值的函式,在表達式中達成類似區域變數的效果。它可以有以下的語法
local ex = StoredExpression() for _,v in ipairs{"4,5,6", "7,8,9", "0,0,0"} do if v == "0,0,0" then print("zeros") elseif ex(string.match(v, "(%d),(%d),(%d)")) then print("tuple", ex[1], ex[2], ex[3], "of size", ex.n) else print("unknown") end end -- Outputs: tuple 4 5 6 of size 3 -- tuple 7 8 9 of size 3 -- zeros
以下為 StoredExpression
的實作
do local function call(self, ...) self.__index = {n = select('#', ...), ...} return ... end function StoredExpression() local self = {__call = call} return setmetatable(self, self) end end
這也允許類似以下的狀況:
result = ex(math.random()) and (ex[1] < 0.3 and "low" or ex[1] > 0.7 and "high" or "med")
可能需要小心留意,因為子表達式的執行順序並非總是已定義。
--DavidManura,2007-02。StoredExpression
實作已由 RiciLake 改善。
與 RiciLake 討論的建議,是為 Lua 語言新增一個新的「let」建構函式,以便在表達式中嵌入敘述,包括區域變數宣告。
建議的語法是
let <chunk> in <expr>
其中「let <chunk> in <expr>」的作用如同一個表達式(或表達式清單?),而「let <chunk> in」的作用則如同一個低優先權的前置運算子(例如 not
或 #
,但不具有低優先權)
<chunk> 中的區域變數會在 <expr> 中可見。
-- typical usage y = let local t = complex_function(x) in t and g(t) -- any statement (not just local variable declarations) can be used y = let local x = 5; print("hello") in x*2 -- can be nested y = let local x = 5 in let local y = x in y*2 -- sets y=10 -- useful when declaring closures this way local func = let local x = 10 in function() x = x + 1 return x end local y = let local x = 0 for _,v in pairs(t) do x = x + v end in x+x^2+x^3 -- using let with tuple proposal t[let x... = 1,2,3 in x] = true -- if statments: local y if x == 1 then print(x) elseif let y = compute(x) in y > z then print("more", y) elseif y < -z then print("less", y) end
let ... in ...
語法已在 Metalua 中實作。請參閱 [1],特別是 [2]。
因此,在一般 Lua 中,stat <foo> bar
在語義上等於 ((function() foo end)())
。不過,Metalua 實作使用更有效率的編譯,不需要建立帶有上值的閉包。
例如,print(stat local x=21; return 2*x end)
將會印出 42,就像較慢且較難閱讀的 print(((function()local x=21; return 2*x; end)()))
一樣。
警告:下列內容純屬學術性質,實際情況下並不太建議使用。
讓我們先定義下列函式
local save, restore; do local saved save = function(value) saved = value; return true end restore = function() return saved end end
然後我們可以進行
local z = save(x+y+z) and restore()^3 + restore() + math.sqrt(restore())
儘管會增加函式呼叫的代價,但這樣的寫法更為簡潔。如果在 Lua 中將儲存/還原設為內建運算,就可以消除該代價。它的作用有點類似 [Forth] 中的堆疊,但只有一個元素。
此概念或許可以擴充,支援多於一個記憶體位置
local save, restore do local saved = {} let = function(name, value) saved[name] = value; return true end get = function(name) return saved[name] end end
接下來我們就可以做一些事情,例如
local z = let('n', x+y+z) and let('m', x^2+y^2+z^2) and get('n')^3 + get('n') + math.sqrt(get('m'))
這似乎是一種以非有效率的方式重新實作局部變數的複雜方法,而且變數並不像局部變數一樣真的是局部的
最終我們會想要清除儲存的表格,以避免它無限增長。可採取的方式包括使用循序佇列或定期清除此表格。
這是另一個範例
-- How I might like to write it -- Assuming rotate_coordinates() returns a tuple of three numbers. -- Note: Invalid Lua. function transform_object(o) return is_vector(o) and do local x, y, z = rotate_coordinates(o[x], o[y], o[z]) return {x*2, y*2, z*2} end or o*2 end
在對 x、y 和 z 進行運算之前,必須先將其值儲存在暫時變數中,也就是說,在假設我們不想呼叫 rotate_coordinates 三次的情況下
--Yuck function transform_object(o) return is_vector(o) and { select(1, rotate_coordinates(o[x], o[y], o[z])) * 2, select(2, rotate_coordinates(o[x], o[y], o[z])) * 2, select(3, rotate_coordinates(o[x], o[y], o[z])) * 2 } or o*2 end
這似乎不是非常建議的方法,但這是我想得出來最好的方法,抱歉讓你的表達式沒有語法高亮...
function Let(statement) local locals = {} return function(In) return function(expression) if In == "In" or In == "in" then table.insert(locals, statement) local func = load(locals[1] .. ' return ' .. expression) return func() else error("'In' or 'in' expected near " .. In, 2) end end end end val = Let 'local x = 10' 'In' 'x - x' local val2 = Let 'local x = 9' 'In' 'x * x' print(Let 'local x = 5' 'In' 'x + x') print(val + 1 + val2)
Lua 5.1