-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathprogramming-in-lua-notes.html
203 lines (201 loc) · 15.4 KB
/
programming-in-lua-notes.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
<!DOCTYPE html>
<!--[if IE 8]> <html class="no-js lt-ie9" lang="zh-cn"> <![endif]-->
<!--[if gt IE 8]><!--> <html class="no-js" lang="zh-cn"> <!--<![endif]-->
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>道可叨 | Programming in Lua 笔记 </title>
<meta name="viewport" content="width=device-width">
<meta name="author" content="Qiang">
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico">
<link href="http://localhost:8000/feeds/atom.xml" type="application/atom+xml" rel="alternate" title="道可叨 Atom Feed" />
<link rel="stylesheet" href="https://zhuoqiang.github.io/theme/css/app.css">
<script src="https://zhuoqiang.github.io/theme/js/vendor/custom.modernizr.js"></script>
</head>
<body>
<header>
<hgroup>
<a href="https://zhuoqiang.github.io">
<h1 i18n>道可叨</h1>
<h2>Free Will</h2>
</a>
</hgroup>
</header>
<section>
<article>
<header>
<h1><a href="https://zhuoqiang.github.io/programming-in-lua-notes.html">Programming in Lua 笔记</a></h1>
</header> <div>
<section>
<p>最近花了两天读完了《Programming in Lua》。本书不但教授如何使用 Lua 语言,还顺带解释了为什么 Lua 会设计成现在这样子,很有价值。</p>
<div class="contents local topic" id="id1">
<ul class="simple">
<li><a class="reference internal" href="#lua" id="id5">Lua 的特点: 小快灵</a></li>
<li><a class="reference internal" href="#id2" id="id6">Lua 的缺点: 少活简</a></li>
<li><a class="reference internal" href="#id3" id="id7">感想</a></li>
<li><a class="reference internal" href="#id4" id="id8">总结</a></li>
</ul>
</div>
<div class="section" id="lua">
<h2><a class="toc-backref" href="#id5">Lua 的特点: 小快灵</a></h2>
<ul class="simple">
<li>小: 整个 Lua 包括标准库只有 2 万行代码,解释器大小 200 KB。占用的内存也很小</li>
<li>快: 经过手工调优基于寄存器的虚拟机,加上增量式的标志清扫垃圾收集器。Lua 可算是运行速度最快的脚本语言了。另一方面,灵活的语法和数据结构也大大加快了开发速度</li>
<li>灵: 使用方式灵活多样。可做为脚本语言直接使用;也可嵌入宿主程序中做脚本引擎 (如魔兽世界);还可直接调用 C 编写的扩展</li>
</ul>
</div>
<div class="section" id="id2">
<h2><a class="toc-backref" href="#id6">Lua 的缺点: 少活简</a></h2>
<ul class="simple">
<li>少: 标准库的功能太少。自限于标准 C 的框框中,导致很多基本功能缺失,例如正则表达式,系统线程,目录操作等</li>
<li>活: 灵活的语法和数据结构固然能加快开发速度,但往往也使人容易犯下不着边际的错误。非常考验团队的编码纪律。特别是变量使用前不用定义,变量默认全局范围,类型自动协变这些设定</li>
<li>简: Lua 的哲学之一是只提供机制,而不提供设施。导致很多用法是靠着惯例和潜规则来支撑的。对普通应用来说是简约,但对稍复杂的就显得简陋了</li>
</ul>
</div>
<div class="section" id="id3">
<h2><a class="toc-backref" href="#id7">感想</a></h2>
<ul>
<li><p class="first">Lua 太执着于标准 C 了,这种偏执让其保有极端的可移植性与可扩展性。例如 Lua 对库搜索路径的处理用巧妙的方式有意避开了文件路径,仅仅因为标准 C 中没有文件路径这个概念。自困于标准 C 直接导致了 Lua 的 os 库不能使用 POSIX API,几乎毫无用处。但也正是这个决定,让扩展非常方便,第三方很容易就写出 POSIX 的库供 Lua 使用。权衡得失,这个坚持还是相当大胆有效的</p>
</li>
<li><p class="first">由于正则表达式的实现过于庞大 (相比 Lua 其它部分来说),Lua 放弃了对它的支持,转而实现自己的字符串模式匹配方式。这是一个败笔。功能上的不同倒在其次,最重要的是大家要重新学习一套新的匹配规则。原有的经验知识浪费了。</p>
</li>
<li><p class="first">表 (table) 是 Lua 中唯一的数据结构,这也是从“小”来考虑的。带来的结果就是“活”与“简”的缺点。功能上来说,table 顶替 array, stack, map 等数据结构完全没有问题,性能上由于实现的巧妙性,也能接受。可问题在于 table 太灵活了,我们之所以使用某类数据结构,有时正是看中了它的限制。限制有利于开发者的沟通交流协同一致。极端的例子就是把表当数组使用时下标要从 1 开始,这个规矩居然只是惯例而非强制,太考验开发人员的素质了</p>
</li>
<li><p class="first">对于用 <tt class="docutils literal">end</tt> 做结束符我有天生的抵触,Ruby 也是一例。Lua 的语法太过随意。末位的分号可以省略; 代码可以没有任何分割符挤在一行里; table 元素的分割符可用分号也可用逗号; 单参数函数调用可以省略括号 ... 也只有脚本语言能用这么灵活的语法。这方面我很喜欢 Python 的设定,语法严谨简单,没有这么多的例外</p>
</li>
<li><p class="first">Lua 中有两个语法设定是亮点:</p>
<ul class="simple">
<li>table 中末位元素允许可选的分割符</li>
<li>多行字面字符串和多行注释可以自选括号样式</li>
</ul>
<p>前者方便了机器,后者方便了人。Json 规范如果当初规定末位元素可以有可选分割符的话,那 json 的模板生成代码就不用单独对最后的元素做特殊处理了</p>
</li>
<li><p class="first">变量不经定义就能使用,这实在是最大的败笔</p>
</li>
<li><p class="first">命令行参数列表的设定很有意思,可以通过负数下标来往前找 Lua 的启动参数</p>
</li>
<li><p class="first">Lua 是弱类型语言,字符串和数字之间可以隐式转换。这是个错误,好处没有坏处一堆。书的作者对此也持负面看法</p>
</li>
<li><p class="first">与 Python 一样,变量只是名字,绑定的值由运行时管理</p>
</li>
<li><p class="first">只有 <tt class="docutils literal">false</tt> 与 <tt class="docutils literal">nil</tt> 算是假,其它的都是真,包括空表和空字符串。用惯 Python 的人会感到意外</p>
</li>
<li><p class="first">数字只有 <tt class="docutils literal">double</tt> 类型。很漂亮的简化</p>
</li>
<li><p class="first">字符串可存任意数据,包括 <tt class="docutils literal">\0</tt> 。象 Python 一样 Lua 的字符串也是不可变的</p>
</li>
<li><p class="first">不明白为什么要加入特殊的 <tt class="docutils literal">#</tt> 号来取长度。加一个 <tt class="docutils literal">len</tt> 函数不就行了吗</p>
</li>
<li><p class="first"><tt class="docutils literal">a.name()</tt> 不同于 <tt class="docutils literal">a:name()</tt> 而 <tt class="docutils literal">a.name</tt> 也不是 <tt class="docutils literal">a[name]</tt></p>
</li>
<li><p class="first">table 用做 array 时,可能会有洞 (hole),所以用 <tt class="docutils literal">#</tt> 获取元素数量是不准的。因为下标可以跳着定义,所以用 <tt class="docutils literal">table.maxn</tt> 算数量也是不准的。那用什么准 ? 得看你怎么用 table 了,这就是灵活的代价!写的时候,运用之妙存乎一心。等到维护代码的时候,就该骂娘了</p>
</li>
<li><p class="first">Lua 对函数式编程支持的很好。函数是一级值语义,可内部嵌套定义,可绑定 lexical scoping 变量,支持尾递归优化。这比 Python 半生不熟的 lambda 要好太多了</p>
</li>
<li><p class="first">把乘方操作符 <tt class="docutils literal">^</tt> 和取余操作符 <tt class="docutils literal">%</tt> 的操作数都扩展到了小数,有很多有趣实用的应用</p>
</li>
<li><p class="first">虽然字符串和数字之间能隐式转换,但在比较相等时,字符串和数字是绝对不等的。Lua 总算保住了最后的理性</p>
</li>
<li><p class="first">基于逻辑操作符的短路机制,有 <tt class="docutils literal">(x > y) and x or y</tt> 类似 C 中三元操作符 :? 的惯例,这点类似 Python 的 <tt class="docutils literal">(x>y) and x or y</tt></p>
</li>
<li><p class="first">定义局部变量要加 <tt class="docutils literal">local</tt> 限定符。如果不加默认是全局变量。这是鼓励使用全局变量啊,失败!</p>
</li>
<li><p class="first">基于块的局部变量作用域。这点比 Python 只有全局,函数级,和 nonlocal 三种要细致好用</p>
</li>
<li><p class="first"><tt class="docutils literal">switch</tt> 是必须的吗? Lua 和 Python 都不使用 <tt class="docutils literal">switch</tt> 而用 <tt class="docutils literal">if elif</tt> 代替了。编译型语言用 <tt class="docutils literal">switch</tt> 还可以方便编译器做优化,脚本语言确实没必要了</p>
</li>
<li><p class="first"><tt class="docutils literal">for</tt> 和 <tt class="docutils literal">iterator</tt> 结合,简化遍历语法。几种语言都有这种搞法 C++, Java, Python, C#</p>
</li>
<li><p class="first">如果只有一个参数,函数调用可以省略 <tt class="docutils literal">()</tt> 。这种为方便偷懒而定的例外,很难认同</p>
</li>
<li><p class="first">比 Python 更灵活的是,Lua 不检查入参个数是否匹配。检查参数的担子落在了函数调用方和函数实现者身上</p>
</li>
<li><p class="first">象 Python 一样,Lua 函数可以返回多个值。不同的是,在多个返回值的取法上,Lua 有一套复杂规则,Python 只有一个规则。高下立判</p>
</li>
<li><p class="first">Lua 的 <tt class="docutils literal">iterator</tt> 协议中包含了不变状态和控制变量,增强了性能。Lua 可以比作脚本语言中的 C,非常贴近虚拟机</p>
</li>
<li><p class="first">象其他脚本语言一样,Lua 也可以在脚本中直接执行脚本字符串。那怎么控制安全性呢? Lua 又提供了 environment 机制让你实现 sandbox。记得几年前市面上 Python 的虚拟主机非常少,一个重要的原因是 Python 的功能太强大,又没有成熟的 sandbox 机制来控制,很难限制用户</p>
</li>
<li><p class="first">异常机制允许指定是哪层的问题,这个设定很新颖</p>
</li>
<li><p class="first">因为 ANSI C 标准没有线程,Lua 用非对称的协程来补缺。这比 Python 半调子的 <tt class="docutils literal">generator</tt> 要好很多。当然,单个 Lua 没法利用多核优势</p>
</li>
<li><p class="first"><tt class="docutils literal">metatable</tt> 与 <tt class="docutils literal">metamethod</tt> 是个因繁就简的方案。再一次体现了 Lua 通过把机制直接暴露给用户来达到小快灵的设计理念</p>
</li>
<li><p class="first">每个函数都可以设置自己的私有环境,这应该是针对 Lua 变量不用定义,默认全局范围语义的一个修正。早知如此,何必当初!</p>
</li>
<li><p class="first">Lua 的对象模型与 javascript 一样,是基于 prototype 而非 class 的,非常灵活。要模拟 class 对象模型可以有多种方案。如果是编译型语言,基于 prototype 的对象模型实用吗 ?</p>
</li>
<li><p class="first">与 C 交互的接口使用栈来进行,简化了 C 接口模型。不过写起程序来稍微费点事。好在可以用第三方库来简化这些工作。</p>
</li>
<li><p class="first">Lua API 总是需要显示传入一个 <tt class="docutils literal">state</tt> 来指明是哪一个栈环境,而没有依赖任何全局变量,这使得 Lua 的解释器并发无碍</p>
</li>
<li><p class="first">在 5.1 版的 API 中,内存分配策略也可以定制。这里 API 的设计非常合理,包含了一个分配函数和一个用户数据指针。Lua 也提供了缺省的内存分配器</p>
</li>
<li><p class="first">关于垃圾收集器常遇到的 finalize, weak pointer,控制 gc 避免性能波动等问题,Lua 一个都没少。习惯了 C++ RAII 还真是觉得 GC 这套太麻烦了</p>
</li>
</ul>
</div>
<div class="section" id="id4">
<h2><a class="toc-backref" href="#id8">总结</a></h2>
<p>Lua 是一门局限性很大的语言,由于其小快灵的特点,在游戏脚本,编写扩展等领域是非常好的选择。可一旦程序上规模就会捉襟见肘。其极端的灵活性和简陋的设施,并不适合大团队协同开发。这年头语言层出不穷,新语言要想得到市场认可,四平八稳的设计思路是很难出头的。Lua 很多近乎偏质的设计决定,正是它得以流行的关键。</p>
<p>当然,Lua 还远谈不上是一门好语言。我一向认为,编程语言本身需肩负起教育与规劝的责任,应该用精巧的限制和有力的规则来避免码农犯愚蠢的错误。相反,把过多的自由交给无知的键盘,只会带给世界更多的垃圾代码。</p>
</div>
</section>
</div>
<footer>
<p>
<time datetime="2012-05-02T01:20:00+08:00" pubdate>2012-05-02</time><a href="https://zhuoqiang.github.io/category/programming.html"><span class="category" i18n>programming</span></a>
</p>
<ul>
<li><a href="https://zhuoqiang.github.io/tag/design.html"><span class="tag" i18n>design</span></i></a>
<li><a href="https://zhuoqiang.github.io/tag/lua.html"><span class="tag" i18n>lua</span></i></a>
<li><a href="https://zhuoqiang.github.io/tag/review.html"><span class="tag" i18n>review</span></i></a>
</ul>
</footer><link rel="stylesheet" href="https://unpkg.com/gitalk/dist/gitalk.css">
<script src="https://unpkg.com/gitalk@latest/dist/gitalk.min.js"></script>
<div id="gitalk-container"></div>
<script type="text/javascript">
var gitalk = new Gitalk({
clientID: "2f60008869581fd61d19",
clientSecret: "6b303dcf58c04cd35782bae82540921663cbfda8",
repo: "zhuoqiang.github.com",
owner: "zhuoqiang",
admin: ["zhuoqiang"],
id: "programming-in-lua-notes",
title: "Programming in Lua 笔记",
perPage: 30,
distractionFreeMode: true,
pagerDirection: "last",
});
gitalk.render('gitalk-container');
</script></article>
</section>
<footer>
<p>@ <a href="mailto:[email protected]"><span i18n>Qiang</span></a> | <span class="heart"><a href="https://github.com/getpelican/pelican">Pelican</a> & <a href="https://bitbucket.org/zhuoqiang/jing">Jing</a></span></p>
</footer>
<script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.8.3.min.js"></script>
<script>window.jQuery || document.write('<script src="https://zhuoqiang.github.io/theme/js/vendor/jquery-1.8.3.min.js"><\/script>')</script>
<script src="https://zhuoqiang.github.io/theme/js/vendor/moment.min.js"></script>
<script src="https://zhuoqiang.github.io/theme/js/vendor/moment-lang.min.js"></script>
<script src="https://zhuoqiang.github.io/theme/js/vendor/i18next-1.6.0.min.js"></script>
<script src="https://zhuoqiang.github.io/theme/js/main.js"></script>
<script type="text/javascript">
var GoSquared = {};
GoSquared.acct = "GSN-743443-H";
(function(w){
function gs(){
w._gstc_lt = +new Date;
var d = document, g = d.createElement("script");
g.type = "text/javascript";
g.src = "//d1l6p2sc9645hc.cloudfront.net/tracker.js";
var s = d.getElementsByTagName("script")[0];
s.parentNode.insertBefore(g, s);
}
w.addEventListener ?
w.addEventListener("load", gs, false) :
w.attachEvent("onload", gs);
})(window);
</script>
</body>
</html>