Lua: 好的, 坏的, 和坑爹的

 
好的
       小巧: 20000行C代码 可以编译进182K的可执行文件 (Linux下).
       可移植: 只要是有ANSI C 编译器的平台都可以编译. 你可以看到它可以在几乎所有的平台上运行:从 microcontrollers Lego Minstorms NXT, 到 移动平台, 到 游戏主机,甚至 浏览器 (翻译成JavaScript). 作为一个嵌入式可扩展语言 提供了简单直接的 C/C++交互接口.
       足够快:见 与其它语言的性能比较, 还有一个 JIT编译器可以显著地提高多数任务的性能; 对于那些仍然对性能不满意的人, 可以把关键部分使用C实现, 然后与其集成, 这样还可以享受其它方面的好处. Lua 从 Modula (Pascal的分支, 已经广泛应用于教育做为教学语言)借鉴了多数的控制语法. 我现在仍然记得早期使用过Philippe Kahn的快速而优雅的 Turbo Pascal IDE. 集成的解释器:只需要在命令行下运行 lua. 先天的协程支持, 用于实现 迭代器 和非抢占式多线程. 低延迟的增量垃圾回收, 没有额外的内存开销, 低实现复杂度, 并且支持 weak tables.
@1,  强大并多样化的表 可以保存任意类型的数据 (除了 nil) , 还可以使用任意类型的值进行索引 (除了 nil): {1, 2, 5, foo = "bar", [func] = "something", ["some spaces"] = value()}.
@2, 词法作用域. 一流的函数 和 闭包 支持的 函数式编程. 
@3, 尾调用: return functioncall()
@4, 递归函数不需要事先声明: local function foo() ... foo() ... end; 注意这样不行 local foo = function() ... foo() ... end. 
@5, 函数返回 多个值: return 1, 2, 3. 调用者可以认为返回值是任意个数的: 如果多于3个, 其余会被丢弃; 如果少于3个, 那其它的会是未初始化的 nil. 
@6,  函数允许变化的变量个数, function foo(...) local args = {...}; bar(param, ...) end. 
@7, Table可以 "拆包" 成参数列表,unpack (或 Lua 5.2的 table.unpack): print(unpack({1, 2, 3})) 打印1 2 3. 
@8, 操作环境变量 (Lua 5.1中的getfenv 和setfenv 和Lua 5.2中的_ENV 操作), 此外还可以构造 沙盒 . 
@9, 同时赋值多个变量: local a, b, c = 1, 2, x, y = y, x, or a, b = foo(). 
@10, 多行字符串 (using [[...]]; 可以使用 [[...[=[...]=]...]])包含和注释 (--[[...]]). 
@11, 可选的分号语句分隔符 (多数用于解决模棱两可的的情况 a = f; (g).x(a)). 
@12, 重载使用 metatables. 元编程 可以根据你的 DSL修改抽象语法树来创造新的语法.
@13,  for 语句有两种形式: generic (使用迭代器: for a in iter() do ... end) 和 numeric (使用数字: for a = 1, 5, 0.1 do ... end); 数字的这个支持各种类型的步进 (不仅仅是整数). 
@14,  函数调用的语法糖 (f‘string‘, f"string", f[[string]], and  f{table})和方法调用(obj:m()).  
@15,  简单而强大的 调试 库. 与众不同的 表和字符串索引从1而不是0开始. 对一个表中的值赋 nil 会从表中删除它. 这就是说对于不存在的值返回 nil , 所以元素存不存在跟它是不是 nil是同一个问题. a = {b = nil} 产生一个空表. 
@16,  没有独立的整数类型; 数字类型 表示的是实数. 
@17,  没有类; 面向对象 使用 表 和 函数实现; 继承使用 metatable 机制实现. 方法调用使用 object:method(args) 的写法, 与 object.method(object, args) 的写法是等价的, 但 object 只取值一次.
@18,  nil 和false 是仅有的表示假的值; 0, 0.0, "0" 等其它的一切值都是true. 不等于是 ~= (例如, if a ~= 1 then ... end). 
@19, not, or, and 操作符是逻辑运算符. 赋值是语句, 这就意味着没有 a=b=1 或if (a=1) then ... end的写法.
@20, 没有 a+=1, a++, 或其它简写形式.
@21,  没有 continue 语句, 尽管有一个 解释 和一堆的替代品, 如在循环中使用 repeat break until true 跳出 或者使用一个Lua 5.2中的goto 语句. 没有 switch 语句.
@24, 某些上下文可能会用到括号; 例如, a = {}; a.field 正常, 但{}.field 不行; 后者需要这样写 ({}).field 注释(这样得到的值是哪里的?>>({})是个匿名地址). 
@25, 循环的控制变量默认是局部的, 循环完了就没了. for 循环中的极限和步进值是 缓存过的; 这意味着 for i = init(), limit(), step() do ... end 中的三个函数 init, limit, 和step 只在循环开前调用过一次. 
@26, 条件 和其它控制语言不需要括号. 
@27, 字符串和数字会自动转换 (需要一个数字时提供一个字符串, 反之亦然), 除了相等比较: 0 == "0" 为false, {} ~= 1 为 true, 还有foo["0"] 和foo[0] 引用的是表中不同的值; 其它关系运算符会在比较不同类型的值时产生错误. 
@28, 逗号和分号 都可以作为表中的元素分隔符; 也同样都可以作为 可选的分隔符 放在结束括号前: a = {a = 1, b = 2, }.
@29 比想像中还要少的内部组件; 可能一些人觉得这就像 "电池没有包含在内"一样. 从另一个角度来看, 这成就了它的紧凑而又可移植的核心, 不过同时有一些库可以进行补偿, 如 LuaRocks 和Penlight  (之后可以去看看这个 ).

坏的: ?【得看君如何对看的了】                           
#1, 有限的错误处理支持 (使用pcall 和xpcall),尽管有些人 争论这已经够用了 , 只需要加一些语法糖和特性支持 (如确定性的finalizer). pcall 和error 的组合十分强大, 特别是 error 可以返回任何东西 (例如一个表)而不是仅仅是一个字符串, 但是 catch ... finally 结构在多数情况下可能更加清晰直观.

#2, 默认是全局的作用域 (这么说对 Lua 5.2不公平, 它已经没有全局了). 有一个 strict 模块要求所有全局变量都需要初始化. 虽然我并没有很多问题是由未初始化的全局变量引起的, 但还是把它放到"坏的"分类, 因为有一次我犯了一个错误, 在调用一个"next"变量时没有局部化它, 引起一个问题 就是迭代器覆盖了另一个模块的next 函数.

#3, 没有Unicode 支持 (最起码string.len 和模式识别函数需要识别 Unicode 字符); 不过有一个ICU库的 绑定 实现了Unicode支持. 可以看一下这条 消息 和后续总结的的关于现有的支持和string.* 需要什么样的修改. 有限的模式匹配支持, 尽管已有的也十分强大.在使用了15 年 Perl后, 我非常想念其中的一些正则表达式特性(多数是前向搜索, 可选组 (group )?, 还有组内组), 没有任何一个都是会增加实现复杂度的. 对于需要更强大的正则表达式的人可以使用 LPeg 和它的 re 模块. 没有三目运算符; 有一些替代品. 一般我使用 foo = test and value1 or value2 形式, value2 在test 和value1 都为 false时可以赋值.

#4, 没有内置POSIX函数. 虽然有 luaposix 模块, 但是它需要编译, 这并不是一个好的选择. 尽管对于这个我并没有很强的需求, 但是每当我需要获取/设置一个环境变量时总会直观想到去访问 getenv 和setenv [6/1/2012更新] miko 在评论中提到, 有 os.getenv, 但是没有相应的 os.setenv.
#5, 没有类/对象 finalizer. Lua 通过 __gc metamethod提供finalizer 的功能 , 但它只能用于自定义类型 (不是表), 并且不能跟其它语言的相应功能匹配, 举例来说, Perl中的 DESTROY 和 END方法. [05/27/2012更新] Lua 5.1中有一个没有文档说明的 newproxy特性, 它实现了表的 finalizers; Lua 5.2 移除了这个特性的同时增加了 表的__gc元方法.
#6, 没有Lua和C代码之间的yielding: coroutine.yield ()在跨越 Lua/C 边界调用时会失败 attempt to yield across metamethod/C-call boundary. 我在使用 luasocket和协程进行异步编辑时多次遇到过这个错误, 最后使用 copas 模块解决. 在Lua 5.2中这个问题得到解决.

坑爹的?【得看君如何对看的了】
&1, 表中元素的个数并不是很容易获取, 结果取决于你怎么做 (或你怎么定义"长度"). 这可能不是个意外, 因为Lua提供了强大的表并支持灵活的索引方式 (数字或其它Lua类型, 除了 nil).

&2,  Lua中的表有两部分: "数组" 部分(使用 t = {1, 2, 3}生成) 和 "哈希" 部分(使用t = {a = "foo", ["b"] = 2}生成); 这两者可以灵活地结合在一起. #table 返回最短的"数组"部分长度(没有任何缺口) 而table.maxn(t) 返回最长的 "数组" 部分(Lua 5.2移除了这个函数). "哈希" 部分没有定义长度. 两者都可以使用 pairs 方法进行遍历, 同时允许你对其中的元素进行计数. 然而, print(#{1, 2, nil, 3}) 打印4 却不是想像中的 2 ,print(#{1, 2, nil, 3, nil}) 打印的则是2. 我确信有一个合理的理由解释它, 但是现在说是就是"坑爹"的地方. [11/17/2012更新] FireFly 在评论中提到, Lua 5.2 中表的长度 只定义成 没有洞的.

&3return 必须是语句块中的最后一句; 也就是说,function foo() print(1); return; print(2) end 会触发一个错误 ‘end‘ expected... 或 unexpected symbol near (这取决于在return 之后有没有分号). 没有人会这样写, 除非你在调试, 但我却被它坑了好几次. 原本我想把它放进"与众不同的"分类, 但是我发现它前后矛盾. 在一个不能使用 return的地方却能使用 do return end . [5/19/2012更新] 这同样出现在 break 语句上, 虽然在Lua 5.2中 break 不再必须是语句块的最后一句了. 函数只返回一个值但它并不是列表中的最后一个; 如: 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 这个 return 的行为也受到这条规则约束: return f456() 返回3个值, 但return (f456()) 只返回一个值 (注意多出的括号). 关于这个语言特性有 很好的文档, 但我仍然认为它太坑爹了 (或许在旁人看来它是优点). 总的来说, 到目前为止我很享受这个语言带来的简洁和便利, 尽管有些东西跟我之前的做法有点不一样.

原文链接:  Lua: 好的, 坏的, 和坑爹的

时间: 2024-10-08 16:46:29

Lua: 好的, 坏的, 和坑爹的的相关文章

作为360老员工,谈谈和周鸿祎的接触和印象

最近看前BOSS周鸿祎的新闻不少,才在微博点评"中国首位00后CEO":无知无畏狂妄自大,该点醒他! ,被很多媒体报道和雷军的点评做比较.还有回应360借壳上市传闻,网上议论纷纷.今天还在2017互联网安全大会上透露,将会发布一款"安全车". 其实3年前写过一篇类似的文章,当时是360如日中天的时候,已经被媒体评为互联网巨头3个半中的"半个".但短短两年多的时间过后,很多人都评价360已经开始走下坡路了.只能说移动互联网时代,变幻莫测,一旦没把握

闲话建模

阿里妹导读:以深度学习为代表的人工智能在图像.语音和NLP领域带来了突破性的进展,在信息检索和个性化领域近几年也有不少公开文献,比如wide& deep实现了深度模型和浅层模型的结合,dssm用于计算语义相关性,deepfm增加了特征组合的能力,deep CF用深度学习实现协同过滤,rnn recommender 采用行为序列预估实现个性化推荐等. 工业级的信息检索或个性化系统是一个复杂的系统工程,深度学习的工业级应用需要具备三个条件:强大的系统计算能力,优秀的模型设计能力和合适的应用场景.今天

游戏人生(一),我的lua之旅:那些坑爹的CCBReaderLoad

首先,我们说说这个CCBReaderLoad. 这个脚本是cocos2dx自带的一个lua+cocosbuilder 的工具,详细功能呐,往下看. 先来看下我遇到的一个问题: ----美工给了我一个.ccbi文件.让我放到游戏里去. 然后我们说说这个ccbi.ccbi是cocosbuilder绘制界面导出的文件,有兴趣的同学能够自行去百度. 我们来讨论下怎样用这个ccbi,以及ccbi中的坑. 先来看一段testlua的代码 TestMenusLayer = TestMenusLayer or

《Redis设计与实现》学习笔记-Lua脚本

Redis从2.6开始支持Lua脚本,和事务的功能类似,可以通过Lua脚本原子的执行多个Redis命令.Redis提供了EVAL和EVALSHA命令执行lua脚本. 创建并修改Lua坏境 Redis在服务器内嵌了一个Lua坏境,并进行了一系列的修改,从而确保这个Lua坏境可以满足Redis服务器的需要,通过下列步骤创建并修改Lua坏境: 创建一个基础Lua坏境,通过调用Lua的C API函数lua_open. 载入多个函数库到Lua坏境中,让Lua脚本可以使用这些函数来进行数据操作.包括Lua核

为什么RPP比lua更适合做脚本语言?

1.RPP以静态类型为主,最终的效率肯定比动态类型的lua要高,并且不会引起GC停顿.(目前与luaJIT性能接近) 2.RPP没有GC(自动垃圾回收),与C/C++互相调用简单直接,而且他们共享进程内存空间,RPP变量和C++变量生命周期相同,不会出现像lua一样的这里变量已经GC了那边还在使用. 3.RPP目前兼容50%的C++语法,70%的C语法,因此它天生就更亲近C++系的语法,所以C++程序员几乎无需学习即可使用. 4.RPP支持指针和内联汇编,底层操作更方便. 当然lua已经发展了许

lua table remove元素的问题

当我在工作中使用lua进行开发时,发现在lua中有4种方式遍历一个table,当然,从本质上来说其实都一样,只是形式不同,这四种方式分别是: for key, value in pairs(tbtest) do XXX end for key, value in ipairs(tbtest) do XXX end for i=1, #(tbtest) do XXX end for i=1, table.maxn(tbtest) do XXX end 前两种是泛型遍历,后两种是数值型遍历.当然你还

手动使用C/C++编写Lua扩展插件

最近在研究如何在Windows 下嵌入Lua来完成业务模块编写的时候 发现Lua的一些问题,首先Lua作为一门脚本语言,其灵活性和可扩展性是很高的,要不然Cocos2d-x中也不会嵌入他来编写业务逻辑,但是由于国内资料相当的少,很少有人去正八经研究完了之后 写一篇文章 来分享自己的成果,想要去深入理解应用一些东西得时候,显得很无力,很多Lua扩展都是直接写扩展库来完成,如果不理解原理甚至你都不会灵活运用,这就是本文写作的目的. 第一 我需要Lua嵌入我的应用程序,这一点很容易的做到. 第二 我需

nginx + lua 构建网站防护waf(一)

最近在帮朋友维护一个站点.这个站点是一个Php网站.坑爹的是用IIS做代理.出了无数问题之后忍无可忍,于是要我帮他切换到nginx上面,前期被不断的扫描和CC.最后找到了waf这样一个解决方案缓解一下.话不多说直接开始. waf的作用: 防止sql注入,本地包含,部分溢出,fuzzing测试,xss,SSRF等web攻击 防止svn/备份之类文件泄漏 防止ApacheBench之类压力测试工具的攻击 屏蔽常见的扫描黑客工具,扫描器 屏蔽异常的网络请求 屏蔽图片附件类目录php执行权限 防止web

在JAVA中使用LUA脚本记,javaj调用lua脚本的函数(转)

最近在做一些奇怪的东西,需要Java应用能够接受用户提交的脚本并执行,网络部分我选择了NanoHTTPD提供基本的HTTP服务器支持,并在Java能承载的许多脚本语言中选择了很久,比如Rhino,Jython和JRuby之类,但它们都太过庞大,并且很难实现沙盒保护服务器环境.最后我的目光投向了Lua,那个被称为粘合剂的语言.遇到的第一个难题是选择所使用的库,纯Java实现的Lua解释器有很多,什么LuaJ,LuaJava,kahlua,还有不知名的mochalua,jill等等(好多好多),其中