表达式中的语句 |
|
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
] 进行的转换相同,并且避免创建最外层的 upvalues。
虽然这不是有效的 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
Metalua 中已经实现了 let ... in ...
语法。参见 [1],特别是这个 [2]。
因此,stat <foo> bar
在语义上等同于普通 Lua 中的 ((function() foo end)())
。但是,Metalua 的实现使用了一种更高效的编译方式,它不涉及创建带有 upvalue 的闭包。
例如,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