您的位置:首页 > 编程语言 > Lua

Lua: Good, bad, and ugly parts

2015-10-05 11:03 429 查看
25 MAR
2012


Good

Small: 20000 lines of C code that can be built into a 182K executable
interpreter (under Linux).

------ 译文:小!在linux下so的大小为182k,只有2w行的代码

Portable: builds on any platform with an ANSI C compiler. You can see it running on almost anything from microcontrollers and Lego
Minstorms NXT, togame
engines, to mobile toolkits, to game
consoles, to a browser (translated
to JavaScript).

------ 译文:通用性,可以运行到你自己封装的引擎中、游戏中、浏览器中等等。

Embedded and extensible language that provides a straightforward interface
to/from C/C++.

------ 译文:嵌入和可扩展的语言。lua和cpp之间相互调用非常简单。

Sufficiently fast: performs well comparing to other languages and
has a JIT compiler that
noticeably improves performance on many tasks; those who may still be not satisfied with the performance, can implement critical parts in C and, given the ease of integration with C, still benefit from other good aspects. [Updated
3/9/2013] replaced shootout results that are no longer available with benchmarksgame.

------ 译文:

Well documented: reference manual, book, wiki, 6-page
short reference and more.

Friendly and enthusiastic community. Between the excellent documentation, the
wiki, the mailing list, and StackOverflow,
I didn't have any issues finding answers to my questions.

Clean and simple syntax suitable for beginners and accessible to non-programmers. Lua has borrowed most of its control syntax from Modula,
the descendent of Pascal, which was
widely used in education as an introductory language. I still remember using early versions of Philippe Kahn's fast and elegant Turbo
Pascal IDE.

Integrated interpreter: just run
lua
from the command line.

Native support for coroutines to implement iterators and non-preemptive
multi-threading.

Incremental garbage collector that has low latency, no
additional memory cost, little implementation complexity, and support for weak
tables.

Powerful heterogeneous tables that store values of any type (except
nil
)
and can be indexed by values of any type (except
nil
):
{1,
2, 5, foo = "bar", [func] = "something", ["some spaces"] = value()}
.

Lexical scoping.

Functional programming with first
class functions and closures.

Tail calls:
return
functioncall()
.

Recursive functions don't need to be pre-declared:
local function foo() ... foo() ... end
; note
this doesn't work with
local foo = function() ... foo() ... end
.

Functions return multiple values:
return
1, 2, 3
. The caller can expect any number of values returned: if less than three is expected, the rest is discarded and if more than three is expected, the rest is
nil
-initialized.

Functions allow variable number of parameters with
function foo(...) local args = {...}; bar(param, ...)
end
.

Tables can be "unpacked" into a list of parameters with
unpack
(or
table.unpack
in
Lua 5.2):
print(unpack({1, 2, 3}))
prints
1
2 3
.

Manipulating environments (
getfenv
and
setfenv
in
Lua 5.1 and
_ENV
manipulation in Lua 5.2), which allows building sandboxes among
other things.

Assignment to a list of variables:
local a, b, c = 1, 2
,
x,
y = y, x
, or
a, b = foo()
.

Multiline strings (using
[[...]]
; can be enclosed with
[[...[=[...]=]...]]
)
and comments (
--[[...]]
).

Semicolon as a statement separator is optional (mostly used to resolve ambiguous cases as in
a = f; (g).x(a)
).

Overloading using metatables.

Metaprogramming to do things from getting and modifying an abstract syntax
tree to creating a new syntax for your DSL.

The
for
statement has two forms: generic (loops
over iterators:
for a in iter() do ... end
) and numeric (loops
over numbers:
for a = 1, 5, 0.1 do ... end
); the numeric one supports all numeric values for steps
(not just integers).

Syntactic sugar for function calls (
f'string'
,
f"string"
,
f[[string]]
,
and
f{table}
) and method calls (
obj:m()
).

Simple yet powerful debug library.

Fast and powerful JIT compiler/interpreter
(LuaJIT) which includes FFI library and
is ABI-compatible with Lua 5.1 (this means that it can load binary modules compiled for Lua 5.1).


Different

Tables and strings are indexed from 1 rather than 0.

Assigning
nil
as a value removes the element from a table. This is consistent with returning
nil
for
non-existing element, so it makes no difference whether the element does not exist or exists with a value of
nil
.
a
= {b = nil}
produces an empty table.

No integers as a separate numeric type; the number type represent real
numbers. The next version of Lua (5.3) may change
that.

No classes; object-orientation is implemented using tables and functions;
inheritance is implemented using the metatable mechanism.

Method calls are implemented using
object:method(args)
notation, which is the same as
object.method(object,
args)
notation, but with
object
evaluated only once.

nil
and
false
are
the only false values; 0, 0.0, "0" and all other values evaluate as
true
.

Non-equality operator is ~= (for example,
if a ~= 1 then ... end
).

not, or, and
keywords used for logical operators.

Assignments are statements, which means there is no
a=b=1
or
if
(a=1) then ... end
.

No
a+=1
,
a++
,
or similar shorthand forms.

No
continue
statement, although there is an explanation and
a number of alternatives, like using
repeat break until true
inside the loop to break out of or
a
goto
statement introduced in Lua 5.2.

No
switch
statement.

Brackets may be required in some contexts; for example,
a = {}; a.field
works, but
{}.field
doesn't;
the latter needs to be specified as
({}).field
.

A control variable in a loop is localized by default and is not available after the loop.

Limit and step values in the numeric
for
loop are cached;
this means that in
for i = init(), limit(), step() do ... end
all three functions
init
,
limit
,
and
step
are called once before the loop is executed.

Conditionals and other control structures require no brackets.

Strings and numbers are automatically converted (if string is used where a number is expected and vice versa), but not in equality/inequality comparisons:
0
== "0"
is
false
,
{}
~= 1
is
true
, and
foo["0"]
and
foo[0]
refer
to two different keys in the table; other relational operators generate errors on comparing values of different types.

Both commas and semicolons can be used in table literals; both can be
placed before the closing curly bracket as an optional separator:
a
= {a = 1, b = 2, }
.

Smaller than expected number of components that are available "out of the box"; some people see this as "batteries not included". This is the other side of having a compact and portable core and is well compensated by having LuaRocks and
libraries like Penlight.


Bad

Limited error handling support (using pcall and xpcall),
although some may argue that it is sufficient and
just needs some syntactic sugar and more feature support (like deterministic finalizers). The combination of
pcall
and
error
is
quite powerful, especially given that
error
can return anything (for example, a table) rather than
just a string, but having
catch ... finally
constructs may be cleaner and simpler to read in many
cases.

Global scoping by default (this has been partially
addressed in Lua 5.2, which has no globals). There is a strict module
that requires all global variables to be initialized. I have not had many issues caused by uninitialized globals, but still put this one into the "bad" category as I once made a mistake of calling a variable "next" and not localizing it, which caused
an issue with an iterator in a completely different module as it overwrote the next function
used with iterators.

No Unicode support (at the very least you don't get
string.len
and
pattern matching functions to recognize Unicode characters); there is a binding toICU library
that implements full Unicode support. See also this message and
follow-ups for a good summary of what is already supported and what modifications may be required for
string.*
functions.

Limited pattern-matching support, although the included one is still
quite powerful. After using Perl for over 15 years, I miss some of the regexp features (mostly look-aheads, optional groups
(group
)?
, and groups inside groups), but nothing that warrants the additional complexity in the implementation. Those who need more power in their regexps can use LPeg and
its re module.

No ternary operator; several alternatives are available. I
usually end up using
foo = test and value1 or value2
form with the caveat that
value2
can
be assigned if both
test
and
value1
end
up being
false
.

No POSIX functions built-in. There is the luaposix module,
but it requires compilation, which is not always an option. I didn't miss this much, but I did come across a case where I needed to get/set an environment variable, and having access to
getenv
and
setenv
would
be convenient [Updated 6/1/2012] As miko noted in the comments, there is os.getenv,
but no corresponding
os.setenv
.

No class/object finalizers. Lua provides finalizer functionality through the __gc
metamethod, but it is available only for userdata types (and not tables) and doesn't match the functionality provided by other languages, for example, DESTROY and END methods
in Perl. [Updated 05/27/2012] There is an undocumented newproxy feature
in Lua 5.1 that allows implementation of finalizers on tables; Lua 5.2 removed that feature as it added support for __gc
metamethod on tables.

No yielding between Lua and C code:
coroutine.yield
call across Lua/C boundary fails with
attempt
to yield across metamethod/C-call boundary
. I happened to come across this error several times as I was doing async programming with luasocket and
coroutines, but solved it using thecopas module. This has been
addressed in Lua 5.2.

No built-in bit operations in Lua 5.1. This is addressed in LuaJIT (BitOp)
and Lua 5.2 (bit32), which both include bit libraries.


Ugly

Number of elements in a table is not easy to get and the result depends on how you do this (or what you mean by "length"). This is probably not surprising, given how powerful tables are in Lua and the fact that they support flexible indexing (by numbers and
any other Lua type except
nil
). Tables in Lua have two parts: an "array/vector" part (generated
with
t = {1, 2, 3}
) and a "hash" part (generated with
t
= {a = "foo", ["b"] = 2}
); the two can be flexibly combined.
#table
returns the length of
the shortest "array/vector" part (without any gaps) and
table.maxn(t)
returns the longest "array/vector"
part (this function is removed in Lua 5.2). The "hash" part doesn't have a defined length. Both parts can be iterated over using the
pairs
method,
which allows you to count the number of elements in them. However,
print(#{1, 2, nil, 3})
prints
4 and not 2 as one may expect, whereas
print(#{1, 2, nil, 3, nil})
prints 2. I'm sure there is a
good reasonable explanation for this, but for now it is in the "ugly" bucket. [Updated 11/17/2012] As FireFly noted in the comments, in Lua
5.2 the length operator is only defined for tables
that don't have holes in them.

return
statement can't be used if it's not the last statement in a block; in other words,
function
foo() print(1); return; print(2) end
will trigger an error
'end' expected...
or
unexpected
symbol near <whatever statement you have after 'return'>
(depending on whether you have semicolon after
return
or
not). Not that anyone would want use this for anything other than debugging, but I got bitten by it couple of times. I would have put this in the "different" category, but I find it inconsistent that I can't use
return
,
but can use
do return end
in exactly the same place.[Updated
5/19/2012] This also applies to
break
statement, although in Lua 5.2
break
is no
longer required to be the last statement in a block.

Only one value is returned from a function if it's not the last one in a list; for example:
function f123() return 1, 2, 3 end
function f456() return 4, 5, 6 end
print(f123(), f456()) -- prints 1, 4, 5, 6
print(f456(), f123()) -- prints 4, 1, 2, 3

The related behavior of
return
is also affected by this rule:
return
f456()
returns three values, but
return (f456())
returns only one value (note the extra
pair of parentheses). This matches the overall simplicity of the language and is well
documented, but I still find it to be a bit ugly (although ugliness as beauty is in the eye of the beholder).

Overall, I have so far enjoyed the simplicity and consistency of the language, although there are few things I wish were done a bit differently. My eight-year-old son also picked Lua syntax quickly, which reminded me a lot about my experience with Turbo Pascal
decades ago.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: