aboutsummaryrefslogtreecommitdiff
path: root/doc
diff options
context:
space:
mode:
authorLi Jin <dragon-fly@qq.com>2023-08-28 15:59:36 +0800
committerLi Jin <dragon-fly@qq.com>2023-08-28 15:59:36 +0800
commit88c62cebf18e714a43781cb43651ffb58351059d (patch)
treed44361124880308d11a6bfbf80ae55d1e8dd15ed /doc
parent15055de0f780d77bdbf2ad7fc85a17de8af15893 (diff)
downloadyuescript-88c62cebf18e714a43781cb43651ffb58351059d.tar.gz
yuescript-88c62cebf18e714a43781cb43651ffb58351059d.tar.bz2
yuescript-88c62cebf18e714a43781cb43651ffb58351059d.zip
update doc.
Diffstat (limited to 'doc')
-rwxr-xr-xdoc/docs/.vuepress/config.js78
-rwxr-xr-xdoc/docs/README.md1
-rwxr-xr-xdoc/docs/doc/README.md37
-rwxr-xr-xdoc/docs/zh/README.md8
-rwxr-xr-xdoc/docs/zh/doc/README.md3473
-rwxr-xr-xdoc/docs/zh/try/README.md6
6 files changed, 3585 insertions, 18 deletions
diff --git a/doc/docs/.vuepress/config.js b/doc/docs/.vuepress/config.js
index bd89428..70ea66a 100755
--- a/doc/docs/.vuepress/config.js
+++ b/doc/docs/.vuepress/config.js
@@ -46,31 +46,75 @@ module.exports = {
46 ['script', { src: '/js/yuescript.js' }], 46 ['script', { src: '/js/yuescript.js' }],
47 ], 47 ],
48 48
49 locales: {
50 '/': {
51 lang: 'en-US',
52 title: 'Yuescript',
53 description: 'A language that compiles to Lua'
54 },
55
56 '/zh/': {
57 lang: 'zh-CN',
58 title: '月之脚本',
59 description: '一门编译到 Lua 的语言'
60 }
61 },
62
49 /** 63 /**
50 * Theme configuration, here is the default theme configuration for VuePress. 64 * Theme configuration, here is the default theme configuration for VuePress.
51 * 65 *
52 * ref:https://v1.vuepress.vuejs.org/theme/default-theme-config.html 66 * ref:https://v1.vuepress.vuejs.org/theme/default-theme-config.html
53 */ 67 */
54 themeConfig: { 68 themeConfig: {
55 repo: '', 69 locales: {
56 editLinks: false, 70 '/': {
57 docsDir: '', 71 selectText: 'Languages',
58 editLinkText: '', 72 label: 'English',
59 lastUpdated: false, 73 repo: '',
60 nav: [ 74 editLinks: false,
61 { 75 docsDir: '',
62 text: 'Document', 76 editLinkText: '',
63 link: '/doc/' 77 lastUpdated: false,
78 nav: [
79 {
80 text: 'Document',
81 link: '/doc/'
82 },
83 {
84 text: 'Try yue!',
85 link: '/try/',
86 },
87 {
88 text: 'Github',
89 link: 'https://github.com/pigpigyyy/Yuescript'
90 }
91 ],
64 }, 92 },
65 { 93
66 text: 'Try yue!', 94 '/zh/': {
67 link: '/try/', 95 selectText: '选择语言',
96 label: '简体中文',
97 repo: '',
98 editLinks: false,
99 docsDir: '',
100 editLinkText: '',
101 lastUpdated: false,
102 nav: [
103 {
104 text: '文档',
105 link: '/zh/doc/'
106 },
107 {
108 text: '试一试!',
109 link: '/zh/try/',
110 },
111 {
112 text: 'Github',
113 link: 'https://github.com/pigpigyyy/Yuescript'
114 }
115 ],
68 }, 116 },
69 { 117 }
70 text: 'Github',
71 link: 'https://github.com/pigpigyyy/Yuescript'
72 }
73 ],
74 }, 118 },
75 119
76 /** 120 /**
diff --git a/doc/docs/README.md b/doc/docs/README.md
index df09a37..f70a1c6 100755
--- a/doc/docs/README.md
+++ b/doc/docs/README.md
@@ -1,7 +1,6 @@
1--- 1---
2home: true 2home: true
3heroImage: ./image/yuescript.svg 3heroImage: ./image/yuescript.svg
4tagline: A language that compiles to Lua
5actionText: Quick Start → 4actionText: Quick Start →
6actionLink: /doc/ 5actionLink: /doc/
7footer: MIT Licensed | Copyright © 2023 Li Jin 6footer: MIT Licensed | Copyright © 2023 Li Jin
diff --git a/doc/docs/doc/README.md b/doc/docs/doc/README.md
index 7372eda..9b47d06 100755
--- a/doc/docs/doc/README.md
+++ b/doc/docs/doc/README.md
@@ -898,6 +898,7 @@ x = y = z = f!
898### Explicit Locals 898### Explicit Locals
899```moonscript 899```moonscript
900do 900do
901 local a = 1
901 local * 902 local *
902 print "forward declare all variables as locals" 903 print "forward declare all variables as locals"
903 x = -> 1 + y + z 904 x = -> 1 + y + z
@@ -905,6 +906,7 @@ do
905 global instance = Item\new! 906 global instance = Item\new!
906 907
907do 908do
909 local X = 1
908 local ^ 910 local ^
909 print "only forward declare upper case variables" 911 print "only forward declare upper case variables"
910 a = 1 912 a = 1
@@ -913,6 +915,7 @@ do
913<YueDisplay> 915<YueDisplay>
914<pre> 916<pre>
915do 917do
918 local a = 1
916 local * 919 local *
917 print "forward declare all variables as locals" 920 print "forward declare all variables as locals"
918 x = -> 1 + y + z 921 x = -> 1 + y + z
@@ -920,6 +923,7 @@ do
920 global instance = Item\new! 923 global instance = Item\new!
921 924
922do 925do
926 local X = 1
923 local ^ 927 local ^
924 print "only forward declare upper case variables" 928 print "only forward declare upper case variables"
925 a = 1 929 a = 1
@@ -930,12 +934,14 @@ do
930### Explicit Globals 934### Explicit Globals
931```moonscript 935```moonscript
932do 936do
937 global a = 1
933 global * 938 global *
934 print "declare all variables as globals" 939 print "declare all variables as globals"
935 x = -> 1 + y + z 940 x = -> 1 + y + z
936 y, z = 2, 3 941 y, z = 2, 3
937 942
938do 943do
944 global X = 1
939 global ^ 945 global ^
940 print "only declare upper case variables as globals" 946 print "only declare upper case variables as globals"
941 a = 1 947 a = 1
@@ -945,12 +951,14 @@ do
945<YueDisplay> 951<YueDisplay>
946<pre> 952<pre>
947do 953do
954 global a = 1
948 global * 955 global *
949 print "declare all variables as globals" 956 print "declare all variables as globals"
950 x = -> 1 + y + z 957 x = -> 1 + y + z
951 y, z = 2, 3 958 y, z = 2, 3
952 959
953do 960do
961 global X = 1
954 global ^ 962 global ^
955 print "only declare upper case variables as globals" 963 print "only declare upper case variables as globals"
956 a = 1 964 a = 1
@@ -2544,6 +2552,35 @@ msg = switch math.random(1, 5)
2544</pre> 2552</pre>
2545</YueDisplay> 2553</YueDisplay>
2546 2554
2555If you want to write code with one less indent when writing a switch statement, you can put the first when clause on the statement start line, and then all other clauses can be written with one less indent.
2556
2557```moonscript
2558switch math.random(1, 5)
2559 when 1
2560 print "you are lucky" -- two indents
2561 else
2562 print "not so lucky"
2563
2564switch math.random(1, 5) when 1
2565 print "you are lucky" -- one indent
2566else
2567 print "not so lucky"
2568```
2569<YueDisplay>
2570<pre>
2571switch math.random(1, 5)
2572 when 1
2573 print "you are lucky" -- two indents
2574 else
2575 print "not so lucky"
2576
2577switch math.random(1, 5) when 1
2578 print "you are lucky" -- one indent
2579else
2580 print "not so lucky"
2581</pre>
2582</YueDisplay>
2583
2547It is worth noting the order of the case comparison expression. The case’s expression is on the left hand side. This can be useful if the case’s expression wants to overwrite how the comparison is done by defining an eq metamethod. 2584It is worth noting the order of the case comparison expression. The case’s expression is on the left hand side. This can be useful if the case’s expression wants to overwrite how the comparison is done by defining an eq metamethod.
2548 2585
2549### Table Matching 2586### Table Matching
diff --git a/doc/docs/zh/README.md b/doc/docs/zh/README.md
new file mode 100755
index 0000000..4f2679e
--- /dev/null
+++ b/doc/docs/zh/README.md
@@ -0,0 +1,8 @@
1---
2home: true
3heroImage: /image/yuescript.svg
4actionText: 快速上手 →
5actionLink: /zh/doc/
6footer: MIT Licensed | Copyright © 2023 Li Jin
7---
8
diff --git a/doc/docs/zh/doc/README.md b/doc/docs/zh/doc/README.md
new file mode 100755
index 0000000..cc7ff00
--- /dev/null
+++ b/doc/docs/zh/doc/README.md
@@ -0,0 +1,3473 @@
1---
2sidebar: auto
3title: 参考手册
4---
5
6# 月之脚本
7
8<img src="/image/yuescript.svg" width="300px" height="300px" alt="logo"/>
9
10## 介绍
11
12月之脚本是一种动态语言,可以编译为Lua。它是[Moonscript](https://github.com/leafo/moonscript)的方言。用月之脚本编写的代码既有表现力又非常简洁。它适合编写一些更易于维护的代码,并在嵌入 Lua 的环境中运行,如游戏或网站服务器。
13
14Yue(月)是中文中“月亮”的名称。
15
16### 月之脚本概览
17```moonscript
18-- 导入语法
19import "yue" as :p, :to_lua
20
21-- 隐式对象
22inventory =
23 equipment:
24 * "sword"
25 * "shield"
26 items:
27 * name: "potion"
28 count: 10
29 * name: "bread"
30 count: 3
31
32-- 管道操作符
33{1, 2, 3}
34 |> map (x)-> x * 2
35 |> filter (x)-> x > 4
36 |> reduce 0, (a, b)-> a + b
37 |> print
38
39-- 元表操作
40apple =
41 size: 15
42 <index>: {color: 0x00ffff}
43p apple.color, apple.<index> if apple.<>?
44
45-- 类似js的导出语法
46export 🌛 = "月之脚本"
47```
48<YueDisplay>
49<pre>
50-- 导入语法
51import "yue" as :p, :to_lua
52
53-- 隐式对象
54inventory =
55 equipment:
56 * "sword"
57 * "shield"
58 items:
59 * name: "potion"
60 count: 10
61 * name: "bread"
62 count: 3
63
64-- 管道操作符
65{1, 2, 3}
66 |> map (x)-> x * 2
67 |> filter (x)-> x > 4
68 |> reduce 0, (a, b)-> a + b
69 |> print
70
71-- 元表操作
72apple =
73 size: 15
74 &lt;index&gt;: {color: 0x00ffff}
75p apple.color, apple.&lt;index&gt; if apple.&lt;&gt;?
76
77-- 类似js的导出语法
78export 🌛 = "月之脚本"
79</pre>
80</YueDisplay>
81
82## 安装
83
84* **Lua 模块**
85
86&emsp;安装 [luarocks](https://luarocks.org),一个Lua模块的包管理器。然后作为Lua模块和可执行文件安装它:
87
88```
89> luarocks install yuescript
90```
91
92&emsp;或者你可以自己构建 `yue.so` 文件:
93
94```
95> make shared LUAI=/usr/local/include/lua LUAL=/usr/local/lib/lua
96```
97
98&emsp;然后从路径 **bin/shared/yue.so** 获取二进制文件。
99
100* **二进制工具**
101
102&emsp;克隆项目仓库,然后构建并安装可执行文件:
103```
104> make install
105```
106
107&emsp;构建不带宏功能的月之脚本编译工具:
108```
109> make install NO_MACRO=true
110```
111
112&emsp;构建不带内置Lua二进制文件的月之脚本编译工具:
113```
114> make install NO_LUA=true
115```
116
117## 使用方法
118
119### Lua 模块
120
121在Lua中使用月之脚本模块:
122
123* **用法 1**
124在Lua中引入 "你的脚本入口文件.yue"。
125```Lua
126require("yue")("你的脚本入口文件")
127```
128当你在同一路径下把 "你的脚本入口文件.yue" 编译成了 "你的脚本入口文件.lua" 时,仍然可以使用这个代码加载 .lua 代码文件。在其余的月之脚本文件中,只需正常使用 **require** 或 **import**进行脚本引用即可。错误消息中的代码行号也会被正确处理。
129
130* **用法 2**
131手动引入月之脚本模块并重写消息。
132```lua
133local yue = require("yue")
134local success, result = xpcall(function()
135 yue.require("yuescript_module_name")
136end, function(err)
137 return yue.traceback(err)
138end)
139```
140
141* **用法 3**
142在Lua中使用月之脚本编译器功能。
143```lua
144local yue = require("yue")
145local codes, err, globals = yue.to_lua([[
146f = ->
147 print "hello world"
148f!
149]],{
150 implicit_return_root = true,
151 reserve_line_number = true,
152 lint_global = true,
153 space_over_tab = false,
154 options = {
155 target = "5.4",
156 path = "/script"
157 }
158})
159```
160
161### 月之脚本编译工具
162
163使用月之脚本编译工具:
164```
165使用命令: yue [选项|文件|目录] ...
166
167 -h 打印此消息
168 -e str 执行文件或原始代码
169 -m 生成压缩代码
170 -r 重写输出以匹配原始行号
171 -t path 指定放置编译文件的位置
172 -o file 将输出写入文件
173 -s 在生成的代码中使用空格代替制表符
174 -p 将输出写入标准输出
175 -b 转储编译时间(不写输出)
176 -g 转储在NAME LINE COLUMN中使用的全局变量
177 -l 从源代码写入行号
178 -c 从源代码保留语句前的注释
179 -w path 观察更改并编译目录下的每个文件
180 -v 打印版本
181 -- 从标准输入读取,打印到标准输出
182 (必须是第一个且唯一的参数)
183
184 --target=version 指定编译器将生成的Lua版本代码
185 (版本只能是 5.1, 5.2, 5.3 或 5.4)
186 --path=path_str 将额外的Lua搜索路径字符串追加到package.path
187
188 不带选项执行以进入REPL,在单行输入符号 '$' 后
189 开始或是停止多行输入模式
190```
191&emsp;&emsp;使用案例:
192&emsp;&emsp;递归编译当前路径下扩展名为 **.yue** 的每个月之脚本文件: **yue .**
193&emsp;&emsp;编译并将结果保存到目标路径: **yue -t /target/path/ .**
194&emsp;&emsp;编译并保留调试信息: **yue -l .**
195&emsp;&emsp;编译并生成压缩代码: **yue -m .**
196&emsp;&emsp;直接执行代码: **yue -e 'print 123'**
197&emsp;&emsp;执行一个月之脚本文件: **yue -e main.yue**
198
199## 宏
200
201### 常见用法
202
203宏函数用于在编译时执行一段代码来生成新的代码,并将生成的代码插入到最终编译结果中。
204
205```moonscript
206macro PI2 = -> math.pi * 2
207area = $PI2 * 5
208
209macro HELLO = -> "'你好 世界'"
210print $HELLO
211
212macro config = (debugging)->
213 global debugMode = debugging == "true"
214 ""
215
216macro asserts = (cond)->
217 debugMode and "assert #{cond}" or ""
218
219macro assert = (cond)->
220 debugMode and "assert #{cond}" or "#{cond}"
221
222$config true
223$asserts item ~= nil
224
225$config false
226value = $assert item
227
228-- 传递的表达式被视为字符串
229macro and = (...)-> "#{ table.concat {...}, ' and ' }"
230if $and f1!, f2!, f3!
231 print "OK"
232```
233<YueDisplay>
234<pre>
235macro PI2 = -> math.pi * 2
236area = $PI2 * 5
237
238macro HELLO = -> "'你好 世界'"
239print $HELLO
240
241macro config = (debugging)->
242 global debugMode = debugging == "true"
243 ""
244
245macro asserts = (cond)->
246 debugMode and "assert #{cond}" or ""
247
248macro assert = (cond)->
249 debugMode and "assert #{cond}" or "#{cond}"
250
251$config true
252$asserts item ~= nil
253
254$config false
255value = $assert item
256
257-- 传递的表达式被视为字符串
258macro and = (...)-> "#{ table.concat {...}, ' and ' }"
259if $and f1!, f2!, f3!
260 print "OK"
261</pre>
262</YueDisplay>
263
264### 直接插入代码
265
266宏函数可以返回一个包含月之脚本代码的字符串,或是一个包含Lua代码字符串的配置表。
267```moonscript
268macro yueFunc = (var)-> "local #{var} = ->"
269$yueFunc funcA
270funcA = -> "访问月之脚本定义的变量"
271
272-- 让月之脚本知道你在Lua代码中声明的局部变量
273macro luaFunc = (var)-> {
274 code: "local function #{var}() end"
275 type: "lua"
276 locals: {var}
277}
278$luaFunc funcB
279funcB = -> "访问Lua代码里定义的变量"
280
281macro lua = (code)-> {
282 :code
283 type: "lua"
284}
285
286-- raw字符串的开始和结束符号会自动被去除了再传入宏函数
287$lua[==[
288-- 插入原始Lua代码
289if cond then
290 print("输出")
291end
292]==]
293```
294<YueDisplay>
295<pre>
296macro yueFunc = (var)-> "local #{var} = ->"
297$yueFunc funcA
298funcA = -> "访问月之脚本定义的变量"
299
300-- 让月之脚本知道你在Lua代码中声明的局部变量
301macro luaFunc = (var)-> {
302 code: "local function #{var}() end"
303 type: "lua"
304 locals: {var}
305}
306$luaFunc funcB
307funcB = -> "访问Lua代码里定义的变量"
308
309macro lua = (code)-> {
310 :code
311 type: "lua"
312}
313
314-- raw字符串的开始和结束符号会自动被去除了再传入宏函数
315$lua[==[
316-- 插入原始Lua代码
317if cond then
318 print("输出")
319end
320]==]
321</pre>
322</YueDisplay>
323
324### 导出宏
325
326宏函数可以从一个模块中导出,并在另一个模块中导入。您必须将导出的宏函数放在一个单独的文件中使用,而且只有宏定义、宏导入和宏展开可以放入这个宏导出模块中。
327```moonscript
328-- 文件: utils.yue
329export macro map = (items, action)-> "[#{action} for _ in *#{items}]"
330export macro filter = (items, action)-> "[_ for _ in *#{items} when #{action}]"
331export macro foreach = (items, action)-> "for _ in *#{items}
332 #{action}"
333
334-- 文件 main.yue
335import "utils" as {
336 $, -- 表示导入所有宏的符号
337 $foreach: $each -- 重命名宏 $foreach 为 $each
338}
339{1, 2, 3} |> $map(_ * 2) |> $filter(_ > 4) |> $each print _
340```
341<YueDisplay>
342<pre>
343-- 文件: utils.yue
344export macro map = (items, action)-> "[#{action} for _ in *#{items}]"
345export macro filter = (items, action)-> "[_ for _ in *#{items} when #{action}]"
346export macro foreach = (items, action)-> "for _ in *#{items}
347 #{action}"
348-- 文件 main.yue
349-- 在浏览器中不支持import函数,请在真实环境中尝试
350--[[
351import "utils" as {
352 $, -- 表示导入所有宏的符号
353 $foreach: $each -- 重命名宏 $foreach 为 $each
354}
355{1, 2, 3} |> $map(_ * 2) |> $filter(_ > 4) |> $each print _
356]]
357</pre>
358</YueDisplay>
359
360### 内置宏
361
362月之脚本中有一些内置可以直接使用的宏,但你可以通过声明相同名称的宏来覆盖它们。
363```moonscript
364print $FILE -- 获取当前模块名称的字符串
365print $LINE -- 获取当前代码行数:2
366```
367<YueDisplay>
368<pre>
369print $FILE -- 获取当前模块名称的字符串
370print $LINE -- 获取当前代码行数:2
371</pre>
372</YueDisplay>
373
374## 操作符
375
376Lua的所有二元和一元操作符在月之脚本中都是可用的。此外,**!=** 符号是 **~=** 的别名,而 **\\** 或 **::** 均可用于编写链式函数调用,如写作 `tb\func!` 或 `tb::func!`。此外月之脚本还提供了一些其他特殊的操作符,以编写更具表达力的代码。
377
378```moonscript
379tb\func! if tb ~= nil
380tb::func! if tb != nil
381```
382<YueDisplay>
383<pre>
384tb\func! if tb ~= nil
385tb::func! if tb != nil
386</pre>
387</YueDisplay>
388
389### 表追加
390
391**[] =** 操作符用于向Lua表的最后插入值。
392
393```moonscript
394tab = {}
395tab[] = "Value"
396```
397<YueDisplay>
398<pre>
399tab = {}
400tab[] = "Value"
401</pre>
402</YueDisplay>
403
404### 表扩展
405
406您可以使用前置 `...` 操作符在Lua表中插入数组表或哈希表。
407
408```moonscript
409parts =
410 * "shoulders"
411 * "knees"
412lyrics =
413 * "head"
414 * ...parts
415 * "and"
416 * "toes"
417
418copy = {...other}
419
420a = {1, 2, 3, x: 1}
421b = {4, 5, y: 1}
422merge = {...a, ...b}
423```
424<YueDisplay>
425<pre>
426parts =
427 * "shoulders"
428 * "knees"
429lyrics =
430 * "head"
431 * ...parts
432 * "and"
433 * "toes"
434
435copy = {...other}
436
437a = {1, 2, 3, x: 1}
438b = {4, 5, y: 1}
439merge = {...a, ...b}
440</pre>
441</YueDisplay>
442
443### 元表
444
445**<>** 操作符可提供元表操作的快捷方式。
446
447* **元表创建**
448使用空括号 **<>** 或被 **<>** 包围的元方法键创建普通的Lua表。
449
450```moonscript
451mt = {}
452add = (right)=> <>: mt, value: @value + right.value
453mt.__add = add
454
455a = <>: mt, value: 1
456-- 使用与临时变量名相同的字段名,将临时变量赋值给元表
457b = :<add>, value: 2
458c = <add>: mt.__add, value: 3
459
460d = a + b + c
461print d.value
462
463close _ = <close>: -> print "超出范围"
464```
465<YueDisplay>
466<pre>
467mt = {}
468add = (right)=> &lt;&gt;: mt, value: @value + right.value
469mt.__add = add
470
471a = &lt;&gt;: mt, value: 1
472-- 使用与临时变量名相同的字段名,将临时变量赋值给元表
473b = :&lt;add&gt;, value: 2
474c = &lt;add&gt;: mt.__add, value: 3
475
476d = a + b + c
477print d.value
478
479close _ = &lt;close&gt;: -> print "超出范围"
480</pre>
481</YueDisplay>
482
483* **元表访问**
484使用 **<>** 或被 **<>** 包围的元方法名或在 **<>** 中编写某些表达式来访问元表。
485
486```moonscript
487-- 使用包含字段 "value" 的元表创建
488tb = <"value">: 123
489tb.<index> = tb.<>
490print tb.value
491
492tb.<> = __index: {item: "hello"}
493print tb.item
494```
495<YueDisplay>
496
497<pre>
498-- 使用包含字段 "value" 的元表创建
499tb = &lt;"value"&gt;: 123
500tb.&lt;index&gt; = tb.&lt;&gt;
501print tb.value
502tb.&lt;&gt; = __index: {item: "hello"}
503print tb.item
504</pre>
505</YueDisplay>
506
507* **元表解构**
508使用被 **<>** 包围的元方法键解构元表。
509
510```moonscript
511{item, :new, :<close>, <index>: getter} = tb
512print item, new, close, getter
513```
514<YueDisplay>
515<pre>
516{item, :new, :&lt;close&gt;, &lt;index&gt;: getter} = tb
517print item, new, close, getter
518</pre>
519</YueDisplay>
520
521### 存在性
522
523**?** 运算符可以在多种上下文中用来检查存在性。
524
525```moonscript
526func?!
527print abc?["你好 世界"]?.xyz
528
529x = tab?.value
530len = utf8?.len or string?.len or (o)-> #o
531
532if print and x?
533 print x
534
535with? io.open "test.txt", "w"
536 \write "你好"
537 \close!
538```
539<YueDisplay>
540<pre>
541func?!
542print abc?["你好 世界"]?.xyz
543
544x = tab?.value
545len = utf8?.len or string?.len or (o)-> #o
546
547if print and x?
548 print x
549
550with? io.open "test.txt", "w"
551 \write "你好"
552 \close!
553</pre>
554</YueDisplay>
555
556### 管道
557
558与其使用一系列嵌套的函数调用,您还可以考虑使用运算符 **|>** 来传递值。
559
560```moonscript
561"你好" |> print
5621 |> print 2 -- 将管道项作为第一个参数插入
5632 |> print 1, _, 3 -- 带有占位符的管道
564
565-- 多行的管道表达式
566readFile "example.txt"
567 |> extract language, {}
568 |> parse language
569 |> emit
570 |> render
571 |> print
572```
573<YueDisplay>
574<pre>
575"你好" |> print
5761 |> print 2 -- 将管道项作为第一个参数插入
5772 |> print 1, _, 3 -- 带有占位符的管道
578-- 多行的管道表达式
579readFile "example.txt"
580 |> extract language, {}
581 |> parse language
582 |> emit
583 |> render
584 |> print
585</pre>
586</YueDisplay>
587
588### 空值合并
589
590如果其左操作数不是**nil**,则nil合并运算符 **??** 返回其左操作数的值;否则,它将计算右操作数并返回其结果。如果左操作数计算结果为非nil的值,**??** 运算符将不再计算其右操作数。
591```moonscript
592local a, b, c, d
593a = b ?? c ?? d
594func a ?? {}
595
596a ??= false
597```
598<YueDisplay>
599<pre>
600local a, b, c, d
601a = b ?? c ?? d
602func a ?? {}
603a ??= false
604</pre>
605</YueDisplay>
606
607### 隐式对象
608
609您可以在表格块内使用符号 **\*** 开始编写一系列隐式结构。如果您正在创建隐式对象,对象的字段必须具有相同的缩进。
610```moonscript
611list =
612 * 1
613 * 2
614 * 3
615
616func
617 * 1
618 * 2
619 * 3
620
621tb =
622 name: "abc"
623
624 values:
625 * "a"
626 * "b"
627 * "c"
628
629 objects:
630 * name: "a"
631 value: 1
632 func: => @value + 1
633 tb:
634 fieldA: 1
635
636 * name: "b"
637 value: 2
638 func: => @value + 2
639 tb: { }
640
641```
642<YueDisplay>
643<pre>
644list =
645 * 1
646 * 2
647 * 3
648
649func
650 * 1
651 * 2
652 * 3
653
654tb =
655 name: "abc"
656
657 values:
658 * "a"
659 * "b"
660 * "c"
661
662 objects:
663 * name: "a"
664 value: 1
665 func: => @value + 1
666 tb:
667 fieldA: 1
668
669 * name: "b"
670 value: 2
671 func: => @value + 2
672 tb: { }
673</pre>
674</YueDisplay>
675
676## 模块
677
678### 导入
679
680导入语句是一个语法糖,用于需要引入一个模块或者从已导入的模块中提取子项目。从模块导入的变量默认为不可修改的常量。
681
682```moonscript
683-- 用作表解构
684do
685 import insert, concat from table
686 -- 当给 insert, concat 变量赋值时,编译器会报告错误
687 import C, Ct, Cmt from require "lpeg"
688
689-- 快捷地导入一个模块
690do
691 import 'module'
692 import 'module_x'
693 import "d-a-s-h-e-s"
694 import "module.part"
695
696-- 导入模块后起一个别名使用,或是进行导入模块表的解构
697do
698 import "player" as PlayerModule
699 import "lpeg" as :C, :Ct, :Cmt
700 import "export" as {one, two, Something:{umm:{ch}}}
701```
702<YueDisplay>
703<pre>
704-- 用作表解构
705do
706 import insert, concat from table
707 -- 当给 insert, concat 变量赋值时,编译器会报告错误
708 import C, Ct, Cmt from require "lpeg"
709
710-- 快捷地导入一个模块
711do
712 import 'module'
713 import 'module_x'
714 import "d-a-s-h-e-s"
715 import "module.part"
716
717-- 导入模块后起一个别名使用,或是进行导入模块表的解构
718do
719 import "player" as PlayerModule
720 import "lpeg" as :C, :Ct, :Cmt
721 import "export" as {one, two, Something:{umm:{ch}}}
722</pre>
723</YueDisplay>
724
725### 导出
726
727导出语句提供了一种简洁的方式来定义当前的模块。
728
729* **命名导出**
730带命名的导出将定义一个局部变量,并在导出的表中添加一个同名的字段。
731
732```moonscript
733export a, b, c = 1, 2, 3
734export cool = "cat"
735
736export What = if this
737 "abc"
738else
739 "def"
740
741export y = ->
742 hallo = 3434
743
744export class Something
745 umm: "cool"
746```
747<YueDisplay>
748<pre>
749export a, b, c = 1, 2, 3
750export cool = "cat"
751
752export What = if this
753 "abc"
754else
755 "def"
756
757export y = ->
758 hallo = 3434
759
760export class Something
761 umm: "cool"
762</pre>
763</YueDisplay>
764
765使用解构进行命名导出。
766
767```moonscript
768export :loadstring, to_lua: tolua = yue
769export {itemA: {:fieldA = '默认值'}} = tb
770```
771<YueDisplay>
772<pre>
773export :loadstring, to_lua: tolua = yue
774export {itemA: {:fieldA = '默认值'}} = tb
775</pre>
776</YueDisplay>
777
778从模块导出命名项目时,可以不用创建局部变量。
779
780```moonscript
781export.itemA = tb
782export.<index> = items
783export["a-b-c"] = 123
784```
785<YueDisplay>
786<pre>
787export.itemA = tb
788export.&lt;index&gt; = items
789export["a-b-c"] = 123
790</pre>
791</YueDisplay>
792
793* **未命名导出**
794未命名导出会将要导出的目标项目添加到导出表的数组部分。
795
796```moonscript
797d, e, f = 3, 2, 1
798export d, e, f
799
800export if this
801 123
802else
803 456
804
805export with tmp
806 j = 2000
807```
808<YueDisplay>
809<pre>
810d, e, f = 3, 2, 1
811export d, e, f
812
813export if this
814 123
815else
816 456
817
818export with tmp
819 j = 2000
820</pre>
821</YueDisplay>
822
823* **默认导出**
824在导出语句中使用 **default** 关键字,来替换导出的表为一个目标的对象。
825
826```moonscript
827export default ->
828 print "你好"
829 123
830```
831<YueDisplay>
832<pre>
833export default ->
834 print "你好"
835 123
836</pre>
837</YueDisplay>
838
839## 赋值
840
841月之脚本中定义的变量是动态类型的,并默认为局部变量。但你可以通过**local**和**global**声明来改变声明变量的作用范围。
842
843```moonscript
844hello = "world"
845a, b, c = 1, 2, 3
846hello = 123 -- 访问现有的变量
847```
848<YueDisplay>
849<pre>
850hello = "world"
851a, b, c = 1, 2, 3
852hello = 123 -- 访问现有的变量
853</pre>
854</YueDisplay>
855
856### 执行更新
857
858你可以使用各式二进制运算符执行更新赋值。
859```moonscript
860x = 1
861x += 1
862x -= 1
863x *= 10
864x /= 10
865x %= 10
866s ..= "world" -- 如果执行更新的局部变量不存在,将新建一个局部变量
867arg or= "默认值"
868```
869<YueDisplay>
870<pre>
871x = 1
872x += 1
873x -= 1
874x *= 10
875x /= 10
876x %= 10
877s ..= "world" -- 如果执行更新的局部变量不存在,将新建一个局部变量
878arg or= "默认值"
879</pre>
880</YueDisplay>
881
882### 链式赋值
883
884你可以进行链式赋值,将多个项目赋予相同的值。
885```moonscript
886a = b = c = d = e = 0
887x = y = z = f!
888```
889<YueDisplay>
890<pre>
891a = b = c = d = e = 0
892x = y = z = f!
893</pre>
894</YueDisplay>
895
896### 显式声明局部变量
897```moonscript
898do
899 local a = 1
900 local *
901 print "预先声明后续所有变量为局部变量"
902 x = -> 1 + y + z
903 y, z = 2, 3
904 global instance = Item\new!
905
906do
907 local X = 1
908 local ^
909 print "只预先声明后续大写的变量为局部变量"
910 a = 1
911 B = 2
912```
913<YueDisplay>
914<pre>
915do
916 local a = 1
917 local *
918 print "预先声明后续所有变量为局部变量"
919 x = -> 1 + y + z
920 y, z = 2, 3
921 global instance = Item\new!
922
923do
924 local X = 1
925 local ^
926 print "只预先声明后续大写的变量为局部变量"
927 a = 1
928 B = 2
929</pre>
930</YueDisplay>
931
932### 显式声明全局变量
933```moonscript
934do
935 global a = 1
936 global *
937 print "预先声明所有变量为全局变量"
938 x = -> 1 + y + z
939 y, z = 2, 3
940
941do
942 global x = 1
943 global ^
944 print "只预先声明大写的变量为全局变量"
945 a = 1
946 B = 2
947 local Temp = "一个局部值"
948```
949<YueDisplay>
950<pre>
951do
952 global a = 1
953 global *
954 print "预先声明所有变量为全局变量"
955 x = -> 1 + y + z
956 y, z = 2, 3
957
958do
959 global x = 1
960 global ^
961 print "只预先声明大写的变量为全局变量"
962 a = 1
963 B = 2
964 local Temp = "一个局部值"
965</pre>
966</YueDisplay>
967
968## 解构赋值
969
970解构赋值是一种快速从Lua表中按名称或基于数组中的位置提取值的方法。
971
972通常当你看到一个字面量的Lua表,比如{1,2,3},它位于赋值的右侧,因为它是一个值。解构赋值语句的写法就是交换了字面量Lua表的角色,并将其放在赋值语句的左侧。
973
974最好是通过示例来解释。以下是如何从表格中解包前两个值的方法:
975
976```moonscript
977thing = {1, 2}
978
979{a, b} = thing
980print a, b
981```
982<YueDisplay>
983
984<pre>
985thing = {1, 2}
986
987{a, b} = thing
988print a, b
989</pre>
990</YueDisplay>
991
992在解构表格字面量中,键代表从右侧读取的键,值代表读取的值将被赋予的名称。
993
994```moonscript
995obj = {
996 hello: "world"
997 day: "tuesday"
998 length: 20
999}
1000
1001{hello: hello, day: the_day} = obj
1002print hello, the_day
1003
1004:day = obj -- 可以不带大括号进行简单的解构
1005```
1006<YueDisplay>
1007<pre>
1008obj = {
1009 hello: "world"
1010 day: "tuesday"
1011 length: 20
1012}
1013
1014{hello: hello, day: the_day} = obj
1015print hello, the_day
1016
1017:day = obj -- 可以不带大括号进行简单的解构
1018</pre>
1019</YueDisplay>
1020
1021这也适用于嵌套的数据结构:
1022
1023```moonscript
1024obj2 = {
1025 numbers: {1,2,3,4}
1026 properties: {
1027 color: "green"
1028 height: 13.5
1029 }
1030}
1031
1032{numbers: {first, second}} = obj2
1033print first, second, color
1034```
1035<YueDisplay>
1036<pre>
1037obj2 = {
1038 numbers: {1,2,3,4}
1039 properties: {
1040 color: "green"
1041 height: 13.5
1042 }
1043}
1044
1045{numbers: {first, second}} = obj2
1046print first, second, color
1047</pre>
1048</YueDisplay>
1049
1050如果解构语句很复杂,也可以任意将其分散在几行中。稍微复杂一些的示例:
1051
1052```moonscript
1053{
1054 numbers: {first, second}
1055 properties: {
1056 color: color
1057 }
1058} = obj2
1059```
1060<YueDisplay>
1061<pre>
1062{
1063 numbers: {first, second}
1064 properties: {
1065 color: color
1066 }
1067} = obj2
1068</pre>
1069</YueDisplay>
1070
1071有时候我们会需要从Lua表中提取值并将它们赋给与键同名的局部变量。为了避免编写重复代码,我们可以使用**:**前缀操作符:
1072
1073```moonscript
1074{:concat, :insert} = table
1075```
1076<YueDisplay>
1077<pre>
1078{:concat, :insert} = table
1079</pre>
1080</YueDisplay>
1081
1082这样的用法与导入语法有些相似。但我们可以通过混合语法重命名我们想要提取的字段:
1083
1084```moonscript
1085{:mix, :max, random: rand} = math
1086```
1087<YueDisplay>
1088<pre>
1089{:mix, :max, random: rand} = math
1090</pre>
1091</YueDisplay>
1092
1093在进行解构时,您可以指定默认值,如:
1094
1095```moonscript
1096{:name = "nameless", :job = "jobless"} = person
1097```
1098<YueDisplay>
1099<pre>
1100{:name = "nameless", :job = "jobless"} = person
1101</pre>
1102</YueDisplay>
1103
1104在进行列表解构时,您可以使用`_`作为占位符:
1105
1106```moonscript
1107{_, two, _, four} = items
1108```
1109<YueDisplay>
1110<pre>
1111{_, two, _, four} = items
1112</pre>
1113</YueDisplay>
1114
1115### 在其它地方的解构
1116
1117解构也可以出现在其它隐式进行赋值的地方。一个例子是用在for循环:
1118
1119```moonscript
1120tuples = {
1121 {"hello", "world"}
1122 {"egg", "head"}
1123}
1124
1125for {left, right} in *tuples
1126 print left, right
1127```
1128<YueDisplay>
1129<pre>
1130tuples = {
1131 {"hello", "world"}
1132 {"egg", "head"}
1133}
1134
1135for {left, right} in *tuples
1136 print left, right
1137</pre>
1138</YueDisplay>
1139
1140我们知道数组表中的每个元素都是一个两项的元组,所以我们可以直接在for语句的名称子句中使用解构来解包它。
1141
1142## If 赋值
1143
1144`if` 和 `elseif` 代码块可以在条件表达式的位置进行赋值。在代码执行到要计算条件时,会首先进行赋值计算,并使用赋与的值作为分支判断的条件。赋值的变量仅在条件分支的代码块内有效,这意味着如果值不是真值,那么它就不会被用到。
1145
1146```moonscript
1147if user = database.find_user "moon"
1148 print user.name
1149```
1150<YueDisplay>
1151<pre>
1152if user = database.find_user "moon"
1153 print user.name
1154</pre>
1155</YueDisplay>
1156
1157```moonscript
1158if hello = os.getenv "hello"
1159 print "你有 hello", hello
1160elseif world = os.getenv "world"
1161 print "你有 world", world
1162else
1163 print "什么都没有 :("
1164```
1165<YueDisplay>
1166<pre>
1167if hello = os.getenv "hello"
1168 print "你有 hello", hello
1169elseif world = os.getenv "world"
1170 print "你有 world", world
1171else
1172 print "什么都没有 :("
1173</pre>
1174</YueDisplay>
1175
1176使用多个返回值的 If 赋值。只有第一个值会被检查,其他值都有同样的作用域。
1177```moonscript
1178if success, result = pcall -> "无报错地获取结果"
1179 print result -- 变量 result 是有作用域的
1180print "好的"
1181```
1182<YueDisplay>
1183<pre>
1184if success, result = pcall -> "无报错地获取结果"
1185 print result -- 变量 result 是有作用域的
1186print "好的"
1187</pre>
1188</YueDisplay>
1189
1190## 可变参数赋值
1191
1192您可以将函数返回的结果赋值给一个可变参数符号 `...`。然后使用Lua的方式访问其内容。
1193```moonscript
1194list = {1, 2, 3, 4, 5}
1195fn = (ok) -> ok, table.unpack list
1196ok, ... = fn true
1197count = select '#', ...
1198first = select 1, ...
1199print ok, count, first
1200```
1201<YueDisplay>
1202<pre>
1203list = {1, 2, 3, 4, 5}
1204fn = (ok) -> ok, table.unpack list
1205ok, ... = fn true
1206count = select '#', ...
1207first = select 1, ...
1208print ok, count, first
1209</pre>
1210</YueDisplay>
1211
1212## 空白
1213
1214月之脚本是一个对空白敏感的语言。您必须在相同的缩进中使用空格 **' '** 或制表符 **'\t'** 来编写一些代码块,如函数体、值列表和一些控制块。包含不同空白的表达式可能意味着不同的事情。制表符被视为4个空格,但最好不要混合使用空格和制表符。
1215
1216### 多行链式调用
1217
1218你可以使用相同的缩进来编写多行链式函数调用。
1219```moonscript
1220Rx.Observable
1221 .fromRange 1, 8
1222 \filter (x)-> x % 2 == 0
1223 \concat Rx.Observable.of 'who do we appreciate'
1224 \map (value)-> value .. '!'
1225 \subscribe print
1226```
1227<YueDisplay>
1228<pre>
1229Rx.Observable
1230 .fromRange 1, 8
1231 \filter (x)-> x % 2 == 0
1232 \concat Rx.Observable.of 'who do we appreciate'
1233 \map (value)-> value .. '!'
1234 \subscribe print
1235</pre>
1236</YueDisplay>
1237
1238## 注释
1239
1240```moonscript
1241-- 我是一个注释
1242
1243str = --[[
1244这是一个多行注释。
1245没问题。
1246]] strA \ -- 注释 1
1247 .. strB \ -- 注释 2
1248 .. strC
1249
1250func --[[端口]] 3000, --[[ip]] "192.168.1.1"
1251```
1252<YueDisplay>
1253<pre>
1254-- 我是一个注释
1255
1256str = --[[
1257这是一个多行注释。
1258没问题。
1259]] strA \ -- 注释 1
1260 .. strB \ -- 注释 2
1261 .. strC
1262
1263func --[[端口]] 3000, --[[ip]] "192.168.1.1"
1264</pre>
1265</YueDisplay>
1266
1267## 错误处理
1268
1269用于统一进行Lua错误处理的便捷语法。
1270
1271```moonscript
1272try
1273 func 1, 2, 3
1274catch err
1275 print yue.traceback err
1276
1277success, result = try
1278 func 1, 2, 3
1279catch err
1280 yue.traceback err
1281
1282try func 1, 2, 3
1283catch err
1284 print yue.traceback err
1285
1286success, result = try func 1, 2, 3
1287
1288try
1289 print "尝试中"
1290 func 1, 2, 3
1291
1292-- 使用if赋值模式
1293if success, result = try func 1, 2, 3
1294catch err
1295 print yue.traceback err
1296 print result
1297```
1298<YueDisplay>
1299<pre>
1300try
1301 func 1, 2, 3
1302catch err
1303 print yue.traceback err
1304
1305success, result = try
1306 func 1, 2, 3
1307catch err
1308 yue.traceback err
1309
1310try func 1, 2, 3
1311catch err
1312 print yue.traceback err
1313
1314success, result = try func 1, 2, 3
1315
1316try
1317 print "尝试中"
1318 func 1, 2, 3
1319
1320-- 使用if赋值模式
1321if success, result = try func 1, 2, 3
1322catch err
1323 print yue.traceback err
1324 print result
1325</pre>
1326</YueDisplay>
1327
1328## 属性
1329
1330月之脚本现在提供了Lua 5.4新增的叫做属性的语法支持。在月之脚本编译到的Lua目标版本低于5.4时,你仍然可以同时使用`const`和`close`的属性声明语法,并获得常量检查和作用域回调的功能。
1331
1332```moonscript
1333const a = 123
1334close _ = <close>: -> print "超出范围。"
1335```
1336<YueDisplay>
1337<pre>
1338const a = 123
1339close _ = &lt;close&gt;: -> print "超出范围。"
1340</pre>
1341</YueDisplay>
1342
1343你可以对进行解构得到的变量标记为常量。
1344
1345```moonscript
1346const {:a, :b, c, d} = tb
1347-- a = 1
1348```
1349<YueDisplay>
1350<pre>
1351const {:a, :b, c, d} = tb
1352-- a = 1
1353</pre>
1354</YueDisplay>
1355
1356## 字面量
1357
1358Lua中的所有基本字面量都可以在月之脚本中使用。包括数字、字符串、布尔值和**nil**。
1359
1360但与Lua不同的是,单引号和双引号字符串内部允许有换行:
1361
1362```moonscript
1363some_string = "这是一个字符串
1364 并包括一个换行。"
1365
1366-- 使用#{}语法可以将表达式插入到字符串字面量中。
1367-- 字符串插值只在双引号字符串中可用。
1368print "我有#{math.random! * 100}%的把握。"
1369```
1370<YueDisplay>
1371<pre>
1372some_string = "这是一个字符串
1373 并包括一个换行。"
1374
1375-- 使用#{}语法可以将表达式插入到字符串字面量中。
1376-- 字符串插值只在双引号字符串中可用。
1377print "我有#{math.random! * 100}%的把握。"
1378</pre>
1379</YueDisplay>
1380
1381### 数字字面量
1382
1383您可以在数字字面量中使用下划线来增加可读性。
1384
1385```moonscript
1386integer = 1_000_000
1387hex = 0xEF_BB_BF
1388```
1389<YueDisplay>
1390
1391<pre>
1392integer = 1_000_000
1393hex = 0xEF_BB_BF
1394</pre>
1395</YueDisplay>
1396
1397## 函数字面量
1398
1399所有函数都是使用月之脚本的函数表达式创建的。一个简单的函数可以用箭头表示为:**->**。
1400
1401```moonscript
1402my_function = ->
1403my_function() -- 调用空函数
1404```
1405<YueDisplay>
1406<pre>
1407my_function = ->
1408my_function() -- 调用空函数
1409</pre>
1410</YueDisplay>
1411
1412函数体可以是紧跟在箭头后的一个语句,或者是在后面的行上使用同样缩进的一系列语句:
1413
1414```moonscript
1415func_a = -> print "你好,世界"
1416
1417func_b = ->
1418 value = 100
1419 print "这个值是:", value
1420```
1421<YueDisplay>
1422<pre>
1423func_a = -> print "你好,世界"
1424
1425func_b = ->
1426 value = 100
1427 print "这个值是:", value
1428</pre>
1429</YueDisplay>
1430
1431如果一个函数没有参数,可以使用**!**操作符调用它,而不是空括号。使用**!**调用没有参数的函数是推荐的写法。
1432
1433```moonscript
1434func_a!
1435func_b()
1436```
1437<YueDisplay>
1438<pre>
1439func_a!
1440func_b()
1441</pre>
1442</YueDisplay>
1443
1444带有参数的函数可以通过在箭头前加上括号中的参数名列表来进行创建:
1445
1446```moonscript
1447sum = (x, y)-> print "数字的和", x + y
1448```
1449<YueDisplay>
1450<pre>
1451sum = (x, y)-> print "数字的和", x + y
1452</pre>
1453</YueDisplay>
1454
1455函数可以通过在函数名后列出参数来调用。当对函数做嵌套的调用时,后面列出的参数会应用于左侧最近的函数。
1456
1457```moonscript
1458sum 10, 20
1459print sum 10, 20
1460
1461a b c "a", "b", "c"
1462```
1463<YueDisplay>
1464<pre>
1465sum 10, 20
1466print sum 10, 20
1467
1468a b c "a", "b", "c"
1469</pre>
1470</YueDisplay>
1471
1472为了避免在调用函数时产生歧义,也可以使用括号将参数括起来。比如在以下的例子中是必需的,这样才能确保参数被传入到正确的函数。
1473
1474```moonscript
1475print "x:", sum(10, 20), "y:", sum(30, 40)
1476```
1477<YueDisplay>
1478<pre>
1479print "x:", sum(10, 20), "y:", sum(30, 40)
1480</pre>
1481</YueDisplay>
1482
1483注意:函数名与开始括号之间不能有任何空格。
1484
1485函数会将函数体中的最后一个语句强制转换为返回语句,这被称作隐式返回:
1486
1487```moonscript
1488sum = (x, y)-> x + y
1489print "数字的和是", sum 10, 20
1490```
1491<YueDisplay>
1492<pre>
1493sum = (x, y) -> x + y
1494print "数字的和是", sum 10, 20
1495</pre>
1496</YueDisplay>
1497
1498如果您需要做显式返回,可以使用return关键字:
1499
1500```moonscript
1501sum = (x, y)-> return x + y
1502```
1503<YueDisplay>
1504<pre>
1505sum = (x, y)-> return x + y
1506</pre>
1507</YueDisplay>
1508
1509就像在Lua中一样,函数可以返回多个值。最后一个语句必须是由逗号分隔的值列表:
1510
1511```moonscript
1512mystery = (x, y)-> x + y, x - y
1513a, b = mystery 10, 20
1514```
1515<YueDisplay>
1516<pre>
1517mystery = (x, y)-> x + y, x - y
1518a, b = mystery 10, 20
1519</pre>
1520</YueDisplay>
1521
1522### 粗箭头
1523
1524因为在Lua中调用方法时,经常习惯将对象作为第一个参数传入,所以月之脚本提供了一种特殊的语法来创建自动包含self参数的函数。
1525
1526```moonscript
1527func = (num)=> @value + num
1528```
1529<YueDisplay>
1530<pre>
1531func = (num)=> @value + num
1532</pre>
1533</YueDisplay>
1534
1535### 参数默认值
1536
1537可以为函数的参数提供默认值。如果参数的值为nil,则确定该参数为空。任何具有默认值的nil参数在函数体运行之前都会被替换。
1538
1539```moonscript
1540my_function = (name = "某物", height = 100)->
1541 print "你好,我是", name
1542 print "我的高度是", height
1543```
1544<YueDisplay>
1545<pre>
1546my_function = (name = "某物", height = 100)->
1547 print "你好,我是", name
1548 print "我的高度是", height
1549</pre>
1550</YueDisplay>
1551
1552函数参数的默认值表达式在函数体中会按参数声明的顺序进行计算。因此,在默认值的表达式中可以访问先前声明的参数。
1553
1554```moonscript
1555some_args = (x = 100, y = x + 1000)->
1556 print x + y
1557```
1558<YueDisplay>
1559<pre>
1560some_args = (x = 100, y = x + 1000)->
1561 print x + y
1562</pre>
1563</YueDisplay>
1564
1565### 多行参数
1566
1567当调用接收大量参数的函数时,将参数列表分成多行是很方便的。由于月之脚本语言对空白字符的敏感性,做参数列表的分割时务必要小心。
1568
1569如果要将参数列表写到下一行,那么当前行必须以逗号结束。并且下一行的缩进必须比当前的缩进多。一旦做了参数的缩进,所有其他参数列表的行必须保持相同的缩进级别,以成为参数列表的一部分。
1570
1571```moonscript
1572my_func 5, 4, 3,
1573 8, 9, 10
1574
1575cool_func 1, 2,
1576 3, 4,
1577 5, 6,
1578 7, 8
1579```
1580<YueDisplay>
1581<pre>
1582my_func 5, 4, 3,
1583 8, 9, 10
1584
1585cool_func 1, 2,
1586 3, 4,
1587 5, 6,
1588 7, 8
1589</pre>
1590</YueDisplay>
1591
1592这种调用方式可以做嵌套。并通过缩进级别来确定参数属于哪一个函数。
1593
1594```moonscript
1595my_func 5, 6, 7,
1596 6, another_func 6, 7, 8,
1597 9, 1, 2,
1598 5, 4
1599```
1600<YueDisplay>
1601<pre>
1602my_func 5, 6, 7,
1603 6, another_func 6, 7, 8,
1604 9, 1, 2,
1605 5, 4
1606</pre>
1607</YueDisplay>
1608
1609因为Lua表也使用逗号作为分隔符,这种缩进语法有助于让值成为参数列表的一部分,而不是Lua表的一部分。
1610
1611```moonscript
1612x = {
1613 1, 2, 3, 4, a_func 4, 5,
1614 5, 6,
1615 8, 9, 10
1616}
1617```
1618<YueDisplay>
1619<pre>
1620x = {
1621 1, 2, 3, 4, a_func 4, 5,
1622 5, 6,
1623 8, 9, 10
1624}
1625</pre>
1626</YueDisplay>
1627
1628有个不常见的写法可以注意一下,如果我们将在后面使用较低的缩进,我们可以为函数参数提供更深的缩进来区分列表的归属。
1629
1630```moonscript
1631y = { my_func 1, 2, 3,
1632 4, 5,
1633 5, 6, 7
1634}
1635```
1636<YueDisplay>
1637<pre>
1638y = { my_func 1, 2, 3,
1639 4, 5,
1640 5, 6, 7
1641}
1642</pre>
1643</YueDisplay>
1644
1645对于其它有代码块跟随的语句,比如条件语句,也可以通过小心安排缩进来做类似的事。比如我们可以通过调整缩进级别来控制一些值归属于哪个语句:
1646
1647```moonscript
1648if func 1, 2, 3,
1649 "你好",
1650 "世界"
1651 print "你好"
1652 print "我在if内部"
1653
1654if func 1, 2, 3,
1655 "你好",
1656 "世界"
1657 print "hello"
1658 print "我在if内部"
1659```
1660<YueDisplay>
1661<pre>
1662if func 1, 2, 3,
1663 "你好",
1664 "世界"
1665 print "你好"
1666 print "我在if内部"
1667
1668if func 1, 2, 3,
1669 "你好",
1670 "世界"
1671 print "你好"
1672 print "我在if内部"
1673</pre>
1674</YueDisplay>
1675
1676## 反向回调
1677
1678反向回调用于减少函数回调的嵌套。它们使用指向左侧的箭头,并且默认会被定义为传入后续函数调用的最后一个参数。它的语法大部分与常规箭头函数相同,只是它指向另一方向,并且后续的函数体不需要进行缩进。
1679
1680```moonscript
1681<- f
1682print "hello"
1683```
1684<YueDisplay>
1685<pre>
1686<- f
1687print "hello"
1688</pre>
1689</YueDisplay>
1690
1691月之脚本也提供了粗箭头反向回调函数。
1692
1693```moonscript
1694<= f
1695print @value
1696```
1697<YueDisplay>
1698<pre>
1699<= f
1700print @value
1701</pre>
1702</YueDisplay>
1703
1704您可以通过一个占位符指定回调函数的传参位置。
1705
1706```moonscript
1707(x) <- map _, {1, 2, 3}
1708x * 2
1709```
1710<YueDisplay>
1711<pre>
1712(x) <- map _, {1, 2, 3}
1713x * 2
1714</pre>
1715</YueDisplay>
1716
1717如果您希望在反向回调处理后继续编写更多其它的代码,您可以使用do语句将不归属反向回调的代码分开。
1718
1719```moonscript
1720result, msg = do
1721 (data) <- readAsync "文件名.txt"
1722 print data
1723 (info) <- processAsync data
1724 check info
1725print result, msg
1726```
1727<YueDisplay>
1728<pre>
1729result, msg = do
1730 (data) <- readAsync "文件名.txt"
1731 print data
1732 (info) <- processAsync data
1733 check info
1734print result, msg
1735</pre>
1736</YueDisplay>
1737
1738## 表格字面量
1739
1740和Lua一样,表格可以通过花括号进行定义。
1741
1742```moonscript
1743some_values = { 1, 2, 3, 4 }
1744```
1745<YueDisplay>
1746<pre>
1747some_values = { 1, 2, 3, 4 }
1748</pre>
1749</YueDisplay>
1750
1751但与Lua不同的是,给表格中的键赋值是用 **:**(而不是 **=**)。
1752
1753```moonscript
1754some_values = {
1755 name: "Bill",
1756 age: 200,
1757 ["favorite food"]: "rice"
1758}
1759```
1760<YueDisplay>
1761<pre>
1762some_values = {
1763 name: "Bill",
1764 age: 200,
1765 ["favorite food"]: "rice"
1766}
1767</pre>
1768</YueDisplay>
1769
1770如果只分配一个键值对的表格,可以省略花括号。
1771
1772```moonscript
1773profile =
1774 height: "4英尺",
1775 shoe_size: 13,
1776 favorite_foods: {"冰淇淋", "甜甜圈"}
1777```
1778<YueDisplay>
1779<pre>
1780profile =
1781 height: "4英尺",
1782 shoe_size: 13,
1783 favorite_foods: {"冰淇淋", "甜甜圈"}
1784</pre>
1785</YueDisplay>
1786
1787可以使用换行符而不使用逗号(或两者都用)来分隔表格中的值:
1788
1789```moonscript
1790values = {
1791 1, 2, 3, 4
1792 5, 6, 7, 8
1793 name: "超人"
1794 occupation: "打击犯罪"
1795}
1796```
1797<YueDisplay>
1798<pre>
1799values = {
1800 1, 2, 3, 4
1801 5, 6, 7, 8
1802 name: "超人"
1803 occupation: "打击犯罪"
1804}
1805</pre>
1806</YueDisplay>
1807
1808创建单行表格字面量时,也可以省略花括号:
1809
1810```moonscript
1811my_function dance: "探戈", partner: "无"
1812
1813y = type: "狗", legs: 4, tails: 1
1814```
1815<YueDisplay>
1816<pre>
1817my_function dance: "探戈", partner: "无"
1818
1819y = type: "狗", legs: 4, tails: 1
1820</pre>
1821</YueDisplay>
1822
1823表格字面量的键可以使用Lua语言的关键字,而无需转义:
1824
1825```moonscript
1826tbl = {
1827 do: "某事"
1828 end: "饥饿"
1829}
1830```
1831<YueDisplay>
1832<pre>
1833tbl = {
1834 do: "某事"
1835 end: "饥饿"
1836}
1837</pre>
1838</YueDisplay>
1839
1840如果你要构造一个由变量组成的表,并希望键与变量名相同,那么可以使用 **:** 前缀操作符:
1841
1842```moonscript
1843hair = "金色"
1844height = 200
1845person = { :hair, :height, shoe_size: 40 }
1846
1847print_table :hair, :height
1848```
1849<YueDisplay>
1850<pre>
1851hair = "金色"
1852height = 200
1853person = { :hair, :height, shoe_size: 40 }
1854
1855print_table :hair, :height
1856</pre>
1857</YueDisplay>
1858
1859如果你希望表中字段的键是某个表达式的结果,那么可以用 **[ ]** 包裹它,就像在Lua中一样。如果键中有任何特殊字符,也可以直接使用字符串字面量作为键,省略方括号。
1860
1861```moonscript
1862t = {
1863 [1 + 2]: "你好"
1864 "你好 世界": true
1865}
1866```
1867<YueDisplay>
1868<pre>
1869t = {
1870 [1 + 2]: "你好"
1871 "你好 世界": true
1872}
1873</pre>
1874</YueDisplay>
1875
1876## 推导式
1877
1878推导式为我们提供了一种便捷的语法,通过遍历现有对象并对其值应用表达式来构造出新的表格。月之脚本有两种推导式:列表推导式和表格推导式。它们最终都是产生Lua表格;列表推导式将值累积到类似数组的表格中,而表格推导式允许您在每次遍历时设置新表格的键和值。
1879
1880### 列表推导式
1881
1882以下操作创建了一个items表的副本,但所有包含的值都翻倍了。
1883
1884```moonscript
1885items = { 1, 2, 3, 4 }
1886doubled = [item * 2 for i, item in ipairs items]
1887```
1888<YueDisplay>
1889<pre>
1890items = { 1, 2, 3, 4 }
1891doubled = [item * 2 for i, item in ipairs items]
1892</pre>
1893</YueDisplay>
1894
1895可以使用when子句筛选新表中包含的项目:
1896
1897```moonscript
1898iter = ipairs items
1899slice = [item for i, item in iter when i > 1 and i < 3]
1900```
1901<YueDisplay>
1902<pre>
1903iter = ipairs items
1904slice = [item for i, item in iter when i > 1 and i < 3]
1905</pre>
1906</YueDisplay>
1907
1908因为我们常常需要迭代数值索引表的值,所以引入了**\***操作符来做语法简化。doubled示例可以重写为:
1909
1910```moonscript
1911doubled = [item * 2 for item in *items]
1912```
1913<YueDisplay>
1914<pre>
1915doubled = [item * 2 for item in *items]
1916</pre>
1917</YueDisplay>
1918
1919for和when子句可以根据需要进行链式操作。唯一的要求是推导式中至少要有一个for子句。
1920
1921使用多个for子句与使用多重循环的效果相同:
1922
1923```moonscript
1924x_coords = {4, 5, 6, 7}
1925y_coords = {9, 2, 3}
1926
1927points = [{x, y} for x in *x_coords \
1928for y in *y_coords]
1929```
1930<YueDisplay>
1931<pre>
1932x_coords = {4, 5, 6, 7}
1933y_coords = {9, 2, 3}
1934
1935points = [{x, y} for x in *x_coords \
1936for y in *y_coords]
1937</pre>
1938</YueDisplay>
1939
1940在推导式中也可以使用简单的数值for循环:
1941
1942```moonscript
1943evens = [i for i = 1, 100 when i % 2 == 0]
1944```
1945<YueDisplay>
1946<pre>
1947evens = [i for i = 1, 100 when i % 2 == 0]
1948</pre>
1949</YueDisplay>
1950
1951### 表格推导式
1952
1953表格推导式和列表推导式的语法非常相似,只是要使用 **{** 和 **}** 并从每次迭代中取两个值。
1954
1955以下示例生成了表格thing的副本:
1956
1957```moonscript
1958thing = {
1959 color: "red"
1960 name: "fast"
1961 width: 123
1962}
1963
1964thing_copy = {k, v for k, v in pairs thing}
1965```
1966<YueDisplay>
1967<pre>
1968thing = {
1969 color: "red"
1970 name: "fast"
1971 width: 123
1972}
1973
1974thing_copy = {k, v for k, v in pairs thing}
1975</pre>
1976</YueDisplay>
1977
1978```moonscript
1979no_color = {k, v for k, v in pairs thing when k != "color"}
1980```
1981<YueDisplay>
1982<pre>
1983no_color = {k, v for k, v in pairs thing when k != "color"}
1984</pre>
1985</YueDisplay>
1986
1987**\***操作符在表格推导式中能使用。在下面的例子里,我们为几个数字创建了一个平方根查找表。
1988
1989```moonscript
1990numbers = {1, 2, 3, 4}
1991sqrts = {i, math.sqrt i for i in *numbers}
1992```
1993<YueDisplay>
1994<pre>
1995numbers = {1, 2, 3, 4}
1996sqrts = {i, math.sqrt i for i in *numbers}
1997</pre>
1998</YueDisplay>
1999
2000表格推导式中的键值元组也可以来自单个表达式,在这种情况下,表达式在计算后应返回两个值。第一个用作键,第二个用作值:
2001
2002在下面的示例中,我们将一些数组转换为一个表,其中每个数组里的第一项是键,第二项是值。
2003
2004```moonscript
2005tuples = {{"hello", "world"}, {"foo", "bar"}}
2006tbl = {unpack tuple for tuple in *tuples}
2007```
2008<YueDisplay>
2009<pre>
2010tuples = { {"hello", "world"}, {"foo", "bar"} }
2011tbl = {unpack tuple for tuple in *tuples}
2012</pre>
2013</YueDisplay>
2014
2015### 切片
2016
2017当使用 **\*** 操作符时,月之脚本还提供了一种特殊的语法来限制要遍历的列表范围。这个语法也相当于在for循环中设置迭代边界和步长。
2018
2019下面的案例中,我们在切片中设置最小和最大边界,取索引在1到5之间(包括1和5)的所有项目:
2020
2021```moonscript
2022slice = [item for item in *items[1, 5]]
2023```
2024<YueDisplay>
2025<pre>
2026slice = [item for item in *items[1, 5]]
2027</pre>
2028</YueDisplay>
2029
2030切片的任意参数都可以省略,并会使用默认值。在如下示例中,如果省略了最大索引边界,它默认为表的长度。使下面的代码取除第一个元素之外的所有元素:
2031
2032```moonscript
2033slice = [item for item in *items[2,]]
2034```
2035<YueDisplay>
2036<pre>
2037slice = [item for item in *items[2,]]
2038</pre>
2039</YueDisplay>
2040
2041如果省略了最小边界,便默认会设置为1。这里我们只提供一个步长,并留下其他边界为空。这样会使得代码取出所有奇数索引的项目:(1, 3, 5, …)
2042
2043```moonscript
2044slice = [item for item in *items[,,2]]
2045```
2046<YueDisplay>
2047<pre>
2048slice = [item for item in *items[,,2]]
2049</pre>
2050</YueDisplay>
2051
2052## For 循环
2053
2054Lua中有两种for循环形式,数字型和通用型:
2055
2056```moonscript
2057for i = 10, 20
2058 print i
2059
2060for k = 1, 15, 2 -- 提供了一个遍历的步长
2061 print k
2062
2063for key, value in pairs object
2064 print key, value
2065```
2066<YueDisplay>
2067<pre>
2068for i = 10, 20
2069 print i
2070
2071for k = 1, 15, 2 -- 提供了一个遍历的步长
2072 print k
2073
2074for key, value in pairs object
2075 print key, value
2076</pre>
2077</YueDisplay>
2078
2079可以使用切片和**\***操作符,就像在列表推导中一样:
2080
2081```moonscript
2082for item in *items[2, 4]
2083 print item
2084```
2085<YueDisplay>
2086<pre>
2087for item in *items[2, 4]
2088 print item
2089</pre>
2090</YueDisplay>
2091
2092当代码语句只有一行时,循环语句也都可以写作更短的语法:
2093
2094```moonscript
2095for item in *items do print item
2096
2097for j = 1, 10, 3 do print j
2098```
2099<YueDisplay>
2100<pre>
2101for item in *items do print item
2102
2103for j = 1, 10, 3 do print j
2104</pre>
2105</YueDisplay>
2106
2107for循环也可以用作表达式。for循环主体中的最后一条语句会被强制转换为一个返回值的表达式,并会将表达式计算结果的值追加到一个作为结果的数组表中。
2108
2109将每个偶数加倍:
2110
2111```moonscript
2112doubled_evens = for i = 1, 20
2113 if i % 2 == 0
2114 i * 2
2115 else
2116 i
2117```
2118<YueDisplay>
2119<pre>
2120doubled_evens = for i = 1, 20
2121 if i % 2 == 0
2122 i * 2
2123 else
2124 i
2125</pre>
2126</YueDisplay>
2127
2128您还可以结合for循环表达式与continue语句来过滤值。
2129
2130注意出现在函数体末尾的for循环,不会被当作是一个表达式,并将循环结果累积到一个列表中作为返回值(相反,函数将返回nil)。如果要函数末尾的循环转换为列表表达式,可以使用返回语句加for循环表达式。
2131
2132```moonscript
2133func_a = -> for i = 1, 10 do print i
2134func_b = -> return for i = 1, 10 do i
2135
2136print func_a! -- 打印 nil
2137print func_b! -- 打印 table 对象
2138```
2139<YueDisplay>
2140<pre>
2141func_a = -> for i = 1, 10 do print i
2142func_b = -> return for i = 1, 10 do i
2143
2144print func_a! -- 打印 nil
2145print func_b! -- 打印 table 对象
2146</pre>
2147</YueDisplay>
2148
2149这样做是为了避免在不需要返回循环结果的函数,创建无效的返回值表格。
2150
2151## Repeat 循环
2152
2153repeat循环是从Lua语言中搬过来的相似语法:
2154
2155```moonscript
2156i = 10
2157repeat
2158 print i
2159 i -= 1
2160until i == 0
2161```
2162<YueDisplay>
2163<pre>
2164i = 10
2165repeat
2166 print i
2167 i -= 1
2168until i == 0
2169</pre>
2170</YueDisplay>
2171
2172## While 循环
2173
2174在月之脚本中的while循环有四种写法:
2175
2176```moonscript
2177i = 10
2178while i > 0
2179 print i
2180 i -= 1
2181
2182while running == true do my_function!
2183```
2184<YueDisplay>
2185<pre>
2186i = 10
2187while i > 0
2188 print i
2189 i -= 1
2190
2191while running == true do my_function!
2192</pre>
2193</YueDisplay>
2194
2195```moonscript
2196i = 10
2197until i == 0
2198 print i
2199 i -= 1
2200
2201until running == false do my_function!
2202```
2203<YueDisplay>
2204<pre>
2205i = 10
2206until i == 0
2207 print i
2208 i -= 1
2209until running == false do my_function!
2210</pre>
2211</YueDisplay>
2212
2213像for循环的语法一样,while循环也可以作为一个表达式使用。为了使函数返回while循环的累积列表值,必须明确使用返回语句返回while循环表达式。
2214
2215## 继续
2216
2217继续语句可以用来跳出当前的循环迭代。
2218
2219```moonscript
2220i = 0
2221while i < 10
2222 i += 1
2223 continue if i % 2 == 0
2224 print i
2225```
2226<YueDisplay>
2227<pre>
2228i = 0
2229while i < 10
2230 i += 1
2231 continue if i % 2 == 0
2232 print i
2233</pre>
2234</YueDisplay>
2235
2236继续语句也可以与各种循环表达式一起使用,以防止当前的循环迭代结果累积到结果列表中。以下示例将数组表过滤为仅包含偶数的数组:
2237
2238```moonscript
2239my_numbers = {1, 2, 3, 4, 5, 6}
2240odds = for x in *my_numbers
2241 continue if x % 2 == 1
2242 x
2243```
2244<YueDisplay>
2245<pre>
2246my_numbers = {1, 2, 3, 4, 5, 6}
2247odds = for x in *my_numbers
2248 continue if x % 2 == 1
2249 x
2250</pre>
2251</YueDisplay>
2252
2253## 条件语句
2254
2255```moonscript
2256have_coins = false
2257if have_coins
2258 print "有硬币"
2259else
2260 print "没有硬币"
2261```
2262<YueDisplay>
2263<pre>
2264have_coins = false
2265if have_coins
2266 print "有硬币"
2267else
2268 print "没有硬币"
2269</pre>
2270</YueDisplay>
2271
2272对于简单的语句,也可以使用简短的语法:
2273
2274```moonscript
2275have_coins = false
2276if have_coins then print "有硬币" else print "没有硬币"
2277```
2278<YueDisplay>
2279<pre>
2280have_coins = false
2281if have_coins then print "有硬币" else print "没有硬币"
2282</pre>
2283</YueDisplay>
2284
2285因为if语句可以用作表达式,所以也可以这样写:
2286
2287```moonscript
2288have_coins = false
2289print if have_coins then "有硬币" else "没有硬币"
2290```
2291<YueDisplay>
2292<pre>
2293have_coins = false
2294print if have_coins then "有硬币" else "没有硬币"
2295</pre>
2296</YueDisplay>
2297
2298条件语句也可以作为表达式用在返回语句和赋值语句中:
2299
2300```moonscript
2301is_tall = (name) ->
2302 if name == "Rob"
2303 true
2304 else
2305 false
2306
2307message = if is_tall "Rob"
2308 "我很高"
2309else
2310 "我不是很高"
2311
2312print message -- 打印: 我很高
2313```
2314<YueDisplay>
2315<pre>
2316is_tall = (name) ->
2317 if name == "Rob"
2318 true
2319 else
2320 false
2321
2322message = if is_tall "Rob"
2323 "我很高"
2324else
2325 "我不是很高"
2326
2327print message -- 打印: 我很高
2328</pre>
2329</YueDisplay>
2330
2331if的反义词是unless(相当于if not,如果 vs 除非):
2332
2333```moonscript
2334unless os.date("%A") == "Monday"
2335 print "今天不是星期一!"
2336```
2337<YueDisplay>
2338<pre>
2339unless os.date("%A") == "Monday"
2340 print "今天不是星期一!"
2341</pre>
2342</YueDisplay>
2343
2344```moonscript
2345print "你真幸运!" unless math.random! > 0.1
2346```
2347<YueDisplay>
2348<pre>
2349print "你真幸运!" unless math.random! > 0.1
2350</pre>
2351</YueDisplay>
2352
2353### 范围表达式
2354
2355您可以使用范围表达式来编写进行边界范围检查的代码。
2356
2357```moonscript
2358a = 5
2359
2360if a in [1, 10]
2361 print "a在1到10的范围内"
2362
2363if a not in [1, 10]
2364 print "a不在1到10的范围内"
2365
2366if a in (0, 11)
2367 print "a在0到11的开放区间内"
2368
2369if a in {1, 3, 5, 7}
2370 print "检查离散值的相等性"
2371
2372if a in list
2373 print "检查`a`是否在列表中"
2374```
2375<YueDisplay>
2376<pre>
2377a = 5
2378
2379if a in [1, 10]
2380 print "a在1到10的范围内"
2381
2382if a not in [1, 10]
2383 print "a不在1到10的范围内"
2384
2385if a in (0, 11)
2386 print "a在0到11的开放区间内"
2387
2388if a in {1, 3, 5, 7}
2389 print "检查离散值的相等性"
2390
2391if a in list
2392 print "检查`a`是否在列表中"
2393</pre>
2394</YueDisplay>
2395
2396```moonscript
2397print "你很幸运!" unless math.random! > 0.1
2398```
2399<YueDisplay>
2400<pre>
2401print "你很幸运!" unless math.random! > 0.1
2402</pre>
2403</YueDisplay>
2404
2405## 代码行修饰符
2406
2407为了方便编写代码,循环语句和if语句可以应用于单行代码语句的末尾:
2408
2409```moonscript
2410print "你好,世界" if name == "Rob"
2411```
2412<YueDisplay>
2413<pre>
2414print "你好,世界" if name == "Rob"
2415</pre>
2416</YueDisplay>
2417
2418修饰for循环的示例:
2419
2420```moonscript
2421print "项目: ", item for item in *items
2422```
2423<YueDisplay>
2424<pre>
2425print "项目: ", item for item in *items
2426</pre>
2427</YueDisplay>
2428
2429修饰while循环的示例:
2430
2431```moonscript
2432game\update! while game\isRunning!
2433
2434reader\parse_line! until reader\eof!
2435```
2436<YueDisplay>
2437<pre>
2438game\update! while game\isRunning!
2439
2440reader\parse_line! until reader\eof!
2441</pre>
2442</YueDisplay>
2443
2444## Switch 语句
2445
2446switch语句是为了简化检查一系列相同值的if语句而提供的简写语法。要注意用于比较检查的目标值只会计算一次。和if语句一样,switch语句在最后可以接一个else代码块来处理没有匹配的情况。在生成的Lua代码中,进行比较是使用==操作符完成的。
2447
2448```moonscript
2449name = "Dan"
2450switch name
2451 when "Robert"
2452 print "你是Robert"
2453 when "Dan", "Daniel"
2454 print "你的名字是Dan"
2455 else
2456 print "我不知道你的名字"
2457```
2458<YueDisplay>
2459<pre>
2460name = "Dan"
2461switch name
2462 when "Robert"
2463 print "你是Robert"
2464 when "Dan", "Daniel"
2465 print "你的名字是Dan"
2466 else
2467 print "我不知道你的名字"
2468</pre>
2469</YueDisplay>
2470
2471switch语句的when子句中可以通过使用逗号分隔的列表来匹配多个值。
2472
2473switch语句也可以作为表达式使用,下面我们可以将switch语句返回的结果分配给一个变量:
2474
2475```moonscript
2476b = 1
2477next_number = switch b
2478 when 1
2479 2
2480 when 2
2481 3
2482 else
2483 error "数字数得太大了!"
2484```
2485<YueDisplay>
2486<pre>
2487b = 1
2488next_number = switch b
2489 when 1
2490 2
2491 when 2
2492 3
2493 else
2494 error "数字数得太大了!"
2495</pre>
2496</YueDisplay>
2497
2498我们可以使用then关键字在when子句的同一行上编写处理代码。else代码块的后续代码中要写在同一行上不需要额外的关键字。
2499
2500```moonscript
2501msg = switch math.random(1, 5)
2502 when 1 then "你很幸运"
2503 when 2 then "你差点很幸运"
2504 else "不太幸运"
2505```
2506<YueDisplay>
2507<pre>
2508msg = switch math.random(1, 5)
2509 when 1 then "你很幸运"
2510 when 2 then "你差点很幸运"
2511 else "不太幸运"
2512</pre>
2513</YueDisplay>
2514
2515如果在编写switch语句时希望少写一个缩进,那么你可以把第一个when子句放在switch开始语句的第一行,然后后续的子语句就都可以都少写一个缩进。
2516
2517```moonscript
2518switch math.random(1, 5)
2519 when 1
2520 print "你很幸运" -- 两个缩进级别
2521 else
2522 print "不太幸运"
2523
2524switch math.random(1, 5) when 1
2525 print "你很幸运" -- 一个缩进级别
2526else
2527 print "不太幸运"
2528```
2529<YueDisplay>
2530<pre>
2531switch math.random(1, 5)
2532 when 1
2533 print "你很幸运" -- 两个缩进级别
2534 else
2535 print "不太幸运"
2536
2537switch math.random(1, 5) when 1
2538 print "你很幸运" -- 一个缩进级别
2539else
2540 print "不太幸运"
2541</pre>
2542</YueDisplay>
2543
2544值得注意的是,在生成Lua代码时,我们要做检查的目标变量会放在==表达式的右侧。当您希望给when子句的比较对象定义一个\_\_eq元方法来重载判断逻辑时,可能会有用。
2545
2546### 表格匹配
2547
2548在switch的when子句中,如果期待检查目标是一个表格,且可以通过特定的结构进行解构并获得非nil值,那么你可以尝试使用表格匹配的语法。
2549
2550```moonscript
2551items =
2552 * x: 100
2553 y: 200
2554 * width: 300
2555 height: 400
2556
2557for item in *items
2558 switch item
2559 when :x, :y
2560 print "Vec2 #{x}, #{y}"
2561 when :width, :height
2562 print "尺寸 #{width}, #{height}"
2563```
2564<YueDisplay>
2565<pre>
2566items =
2567 * x: 100
2568 y: 200
2569 * width: 300
2570 height: 400
2571
2572for item in *items
2573 switch item
2574 when :x, :y
2575 print "Vec2 #{x}, #{y}"
2576 when :width, :height
2577 print "尺寸 #{width}, #{height}"
2578</pre>
2579</YueDisplay>
2580
2581你可以使用默认值来选择性地解构表格的某些字段。
2582
2583```moonscript
2584item = {}
2585
2586{pos: {:x = 50, :y = 200}} = item -- 获取错误:尝试索引nil值(字段'pos')
2587
2588switch item
2589 when {pos: {:x = 50, :y = 200}}
2590 print "Vec2 #{x}, #{y}" -- 表格解构仍然会通过
2591```
2592<YueDisplay>
2593<pre>
2594item = {}
2595
2596{pos: {:x = 50, :y = 200}} = item -- 获取错误:尝试索引nil值(字段'pos')
2597
2598switch item
2599 when {pos: {:x = 50, :y = 200}}
2600 print "Vec2 #{x}, #{y}" -- 表格解构仍然会通过
2601</pre>
2602</YueDisplay>
2603
2604### 范围匹配
2605
2606使用`in`范围匹配表达式,你可以在switch的when子句中进行范围匹配的检查处理。
2607
2608```moonscript
2609value = 5
2610
2611switch item
2612 -- 使用闭区间进行范围检查
2613 when in [1, 3]
2614 print "1 <= value <= 3"
2615
2616 -- 使用开闭区间进行范围检查
2617 when in (6, 8]
2618 print "6 < value <= 8"
2619
2620 -- 检查不在范围内
2621 when not in [1, 10)
2622 print "不是 (1 <= value < 10)"
2623
2624 -- 检查离散值
2625 when in {11, 21, 99}
2626 print "值是 11, 21 或 99"
2627```
2628<YueDisplay>
2629<pre>
2630value = 5
2631
2632switch item
2633 -- 使用闭区间进行范围检查
2634 when in [1, 3]
2635 print "1 <= value <= 3"
2636
2637 -- 使用开闭区间进行范围检查
2638 when in (6, 8]
2639 print "6 < value <= 8"
2640
2641 -- 检查不在范围内
2642 when not in [1, 10)
2643 print "不是 (1 <= value < 10)"
2644
2645 -- 检查离散值
2646 when in {11, 21, 99}
2647 print "值是 11, 21 或 99"
2648</pre>
2649</YueDisplay>
2650
2651## 面向对象编程
2652
2653在以下的示例中,月之脚本生成的Lua代码可能看起来会很复杂。所以最好主要关注月之脚本代码层面的意义,然后如果您想知道关于面向对象功能的实现细节,再查看Lua代码。
2654
2655一个简单的类:
2656
2657```moonscript
2658class Inventory
2659 new: =>
2660 @items = {}
2661
2662 add_item: (name)=>
2663 if @items[name]
2664 @items[name] += 1
2665 else
2666 @items[name] = 1
2667```
2668<YueDisplay>
2669<pre>
2670class Inventory
2671 new: =>
2672 @items = {}
2673
2674 add_item: (name)=>
2675 if @items[name]
2676 @items[name] += 1
2677 else
2678 @items[name] = 1
2679</pre>
2680</YueDisplay>
2681
2682月之脚本面向对象的写法是使用类的声明语句,然后跟一个Lua表格字面量的声明来声明一个类,并在表格定义中列出所有的方法和属性。
2683
2684键名为new的成员在这里很特殊,因为它会被用作构造函数。
2685
2686注意类中的所有方法都使用了粗箭头函数语法。在调用类的实例上的方法时,实例本身会作为第一个参数传入。所以粗箭头的函数负责创建一个self参数。
2687
2688变量名上的@前缀是self的简写。@items会变为self.items。
2689
2690在创建类的实例时,可以通过把类的名称作为函数进行调用,并获得新的实例。
2691
2692```moonscript
2693inv = Inventory!
2694inv\add_item "t-shirt"
2695inv\add_item "pants"
2696```
2697<YueDisplay>
2698<pre>
2699inv = Inventory!
2700inv\add_item "t-shirt"
2701inv\add_item "pants"
2702</pre>
2703</YueDisplay>
2704
2705因为需要将类的实例传入到它们被调用的方法,所以使用了\操作符。
2706
2707类的所有属性在实例之间是共享的。对于函数来说一般没有问题,但是对于其他类型的属性,可能会出现不希望的结果。
2708
2709比如下面的例子,clothes属性在所有实例之间是共享的,所以在一个实例中对它的修改会影响到另一个实例:
2710
2711```moonscript
2712class Person
2713 clothes: {}
2714 give_item: (name)=>
2715 table.insert @clothes, name
2716
2717a = Person!
2718b = Person!
2719
2720a\give_item "pants"
2721b\give_item "shirt"
2722
2723-- 会同时打印出裤子和衬衫
2724print item for item in *a.clothes
2725```
2726<YueDisplay>
2727<pre>
2728class Person
2729 clothes: {}
2730 give_item: (name)=>
2731 table.insert @clothes, name
2732
2733a = Person!
2734b = Person!
2735
2736a\give_item "pants"
2737b\give_item "shirt"
2738
2739-- 会同时打印出裤子和衬衫
2740print item for item in *a.clothes
2741</pre>
2742</YueDisplay>
2743
2744避免这个问题的正确方法是在构造函数中创建对象的可变状态:
2745
2746```moonscript
2747class Person
2748 new: =>
2749 @clothes = {}
2750```
2751<YueDisplay>
2752<pre>
2753class Person
2754 new: =>
2755 @clothes = {}
2756</pre>
2757</YueDisplay>
2758
2759### 继承
2760
2761`extends`关键字可以在类声明中使用,以继承另一个类的属性和方法。
2762
2763```moonscript
2764class BackPack extends Inventory
2765 size: 10
2766 add_item: (name)=>
2767 if #@items > size then error "背包已满"
2768 super name
2769```
2770<YueDisplay>
2771<pre>
2772class BackPack extends Inventory
2773 size: 10
2774 add_item: (name)=>
2775 if #@items > size then error "背包已满"
2776 super name
2777</pre>
2778</YueDisplay>
2779
2780这里我们扩展了Inventory类,并限制了它可以携带的物品数量。
2781
2782在这个例子中,我们没有在子类上定义构造函数,所以当我们创建一个新实例时,会调用父类的构造函数。如果我们定义了构造函数,那么我们可以使用super方法来调用父类的构造函数。
2783
2784每当一个类从另一个类继承时,它会通过调用父类上的`__inherited`方法(如果存在)向父类发送消息。该函数接收两个参数,被继承的类和子类。
2785
2786```moonscript
2787class Shelf
2788 @__inherited: (child)=>
2789 print @__name, "被", child.__name, "继承"
2790
2791-- 将打印: Shelf 被 Cupboard 继承
2792class Cupboard extends Shelf
2793```
2794<YueDisplay>
2795<pre>
2796class Shelf
2797 @__inherited: (child)=>
2798 print @__name, "被", child.__name, "继承"
2799
2800-- 将打印: Shelf 被 Cupboard 继承
2801class Cupboard extends Shelf
2802</pre>
2803</YueDisplay>
2804
2805### Super 关键字
2806
2807**super** 是一个特殊的关键字,可以用两种不同的方式使用:它可以被视为一个对象,或者可以像函数一样被调用。它只在类的内部出现时有特殊功能。
2808
2809当作为函数调用时,它会调用父类中同名的函数。当前的 self 会自动作为第一个参数进行传递。(如上面的继承示例所示)
2810
2811当 super 被用作普通值时,它是对父类对象的引用。
2812
2813它可以像访问任何普通对象一样来检索父类中可能被子类覆盖的值。
2814
2815当使用 \ 调用操作符与 super 一起使用时,self 会被插入为第一个参数,而不是 super 本身的值。当使用 . 来检索函数时,会返回父类中的原始函数。
2816
2817以下是使用 super 的几个不同方式的示例:
2818
2819```moonscript
2820class MyClass extends ParentClass
2821 a_method: =>
2822 -- 以下效果相同:
2823 super "你好", "世界"
2824 super\a_method "你好", "世界"
2825 super.a_method self, "你好", "世界"
2826
2827 -- super 作为值等于父类:
2828 assert super == ParentClass
2829```
2830<YueDisplay>
2831<pre>
2832class MyClass extends ParentClass
2833 a_method: =>
2834 -- 以下效果相同:
2835 super "你好", "世界"
2836 super\a_method "你好", "世界"
2837 super.a_method self, "你好", "世界"
2838
2839 -- super 作为值等于父类:
2840 assert super == ParentClass
2841</pre>
2842</YueDisplay>
2843
2844**super** 也可以用在函数存根的左侧。唯一的主要区别是,生成的函数不是绑定到 super 的值,而是绑定到 self。
2845
2846### 类型
2847
2848每个类的实例都带有它的类型。这存储在特殊的 \_\_class 属性中。此属性会保存类对象。类对象是我们用来构建新实例的对象。我们还可以索引类对象以检索类方法和属性。
2849
2850```moonscript
2851b = BackPack!
2852assert b.__class == BackPack
2853
2854print BackPack.size -- 打印 10
2855```
2856<YueDisplay>
2857<pre>
2858b = BackPack!
2859assert b.__class == BackPack
2860
2861print BackPack.size -- 打印 10
2862</pre>
2863</YueDisplay>
2864
2865### 类对象
2866
2867当我们使用类的定义语句时,我们创建的是类对象。类对象存储在与类同名的变量中。
2868
2869类对象可以像函数一样被调用,以创建新的实例。这就是我们在上面的例子中如何创建类的实例的。
2870
2871一个类由两个表组成。类表本身和基表。基表用作所有实例的元表。在类声明中列出的所有属性都会被放在基表中。
2872
2873如果类对象的元表中不存在属性,它会从基表中读取属性。这意味着我们可以直接从类中访问函数和属性。
2874
2875重要的是要注意,赋值给类对象并不会赋值给基表,所以这不是向实例添加新方法的有效方式。相反,必须明确地更改基表。参见下面的 \_\_base 字段。
2876
2877类对象有几个特殊的属性:
2878
2879当它被声明时,类的名称存储为类对象的 \_\_name 字段中的字符串。
2880
2881```moonscript
2882print BackPack.__name -- 打印 Backpack
2883```
2884<YueDisplay>
2885<pre>
2886print BackPack.__name -- 打印 Backpack
2887</pre>
2888</YueDisplay>
2889
2890基对象存储在 \_\_base 中。我们可以修改这个表,为已经创建的实例和尚未创建的实例添加功能。
2891
2892如果类从其他类扩展,父类对象存储在 \_\_parent 中。
2893
2894### 类变量
2895
2896我们可以直接在类对象中创建变量,而不是在类的基对象中,通过在类声明中的属性名前使用 @。
2897
2898```moonscript
2899class Things
2900 @some_func: => print "Hello from", @__name
2901
2902Things\some_func!
2903
2904-- 类变量在实例中不可见
2905assert Things().some_func == nil
2906```
2907<YueDisplay>
2908<pre>
2909class Things
2910 @some_func: => print "Hello from", @__name
2911
2912Things\some_func!
2913
2914-- 类变量在实例中不可见
2915assert Things().some_func == nil
2916</pre>
2917</YueDisplay>
2918
2919在表达式中,我们可以使用 @@ 来访问存储在 self.__class 中的值。因此,@@hello 是 self.__class.hello 的简写。
2920
2921```moonscript
2922class Counter
2923 @count: 0
2924
2925 new: =>
2926 @@count += 1
2927
2928Counter!
2929Counter!
2930
2931print Counter.count -- 输出 2
2932```
2933<YueDisplay>
2934<pre>
2935class Counter
2936 @count: 0
2937
2938 new: =>
2939 @@count += 1
2940
2941Counter!
2942Counter!
2943
2944print Counter.count -- 输出 2
2945</pre>
2946</YueDisplay>
2947
2948@@ 的调用语义与 @ 类似。调用 @@ 时,会使用 Lua 的冒号语法将类作为第一个参数传入。
2949
2950```moonscript
2951@@hello 1,2,3,4
2952```
2953<YueDisplay>
2954<pre>
2955@@hello 1,2,3,4
2956</pre>
2957</YueDisplay>
2958
2959### 类声明语句
2960
2961在类声明的主体中,除了键/值对外,我们还可以编写普通的表达式。在这种类声明体中的普通代码的上下文中,self等于类对象,而不是实例对象。
2962
2963以下是创建类变量的另一种方法:
2964
2965```moonscript
2966class Things
2967 @class_var = "hello world"
2968```
2969<YueDisplay>
2970<pre>
2971class Things
2972 @class_var = "hello world"
2973</pre>
2974</YueDisplay>
2975
2976这些表达式会在所有属性被添加到类的基对象后执行。
2977
2978在类的主体中声明的所有变量都会限制作用域只在类声明的范围。这对于放置只有类方法可以访问的私有值或辅助函数很方便:
2979
2980```moonscript
2981class MoreThings
2982 secret = 123
2983 log = (msg)-> print "LOG:", msg
2984
2985 some_method: =>
2986 log "hello world: " .. secret
2987```
2988<YueDisplay>
2989<pre>
2990class MoreThings
2991 secret = 123
2992 log = (msg)-> print "LOG:", msg
2993
2994 some_method: =>
2995 log "hello world: " .. secret
2996</pre>
2997</YueDisplay>
2998
2999### @ 和 @@ 值
3000
3001当@和@@前缀在一个名字前时,它们分别代表在self和self.\_\_class中访问的那个名字。
3002
3003如果它们单独使用,它们是self和self.\_\_class的别名。
3004
3005```moonscript
3006assert @ == self
3007assert @@ == self.__class
3008```
3009<YueDisplay>
3010<pre>
3011assert @ == self
3012assert @@ == self.__class
3013</pre>
3014</YueDisplay>
3015
3016例如,使用@@从实例方法快速创建同一类的新实例的方法:
3017
3018```moonscript
3019some_instance_method = (...)=> @@ ...
3020```
3021<YueDisplay>
3022<pre>
3023some_instance_method = (...)=> @@ ...
3024</pre>
3025</YueDisplay>
3026
3027### 构造属性提升
3028
3029为了减少编写简单值对象定义的代码。你可以这样简单写一个类:
3030
3031```moonscript
3032class Something
3033 new: (@foo, @bar, @@biz, @@baz) =>
3034
3035-- 这是以下声明的简写形式
3036
3037class Something
3038 new: (foo, bar, biz, baz) =>
3039 @foo = foo
3040 @bar = bar
3041 @@biz = biz
3042 @@baz = baz
3043```
3044<YueDisplay>
3045<pre>
3046class Something
3047 new: (@foo, @bar, @@biz, @@baz) =>
3048
3049-- 这是以下声明的简写形式
3050
3051class Something
3052 new: (foo, bar, biz, baz) =>
3053 @foo = foo
3054 @bar = bar
3055 @@biz = biz
3056 @@baz = baz
3057</pre>
3058</YueDisplay>
3059
3060你也可以使用这种语法为一个函数初始化传入对象的字段。
3061
3062```moonscript
3063new = (@fieldA, @fieldB)=> @
3064obj = new {}, 123, "abc"
3065print obj
3066```
3067<YueDisplay>
3068<pre>
3069new = (@fieldA, @fieldB)=> @
3070obj = new {}, 123, "abc"
3071print obj
3072</pre>
3073</YueDisplay>
3074
3075### 类表达式
3076
3077类声明的语法也可以作为一个表达式使用,可以赋值给一个变量或者被返回语句返回。
3078
3079```moonscript
3080x = class Bucket
3081 drops: 0
3082 add_drop: => @drops += 1
3083```
3084<YueDisplay>
3085<pre>
3086x = class Bucket
3087 drops: 0
3088 add_drop: => @drops += 1
3089</pre>
3090</YueDisplay>
3091
3092### 匿名类
3093
3094声明类时可以省略名称。如果类的表达式不在赋值语句中,\_\_name属性将为nil。如果出现在赋值语句中,赋值操作左侧的名称将代替nil。
3095
3096```moonscript
3097BigBucket = class extends Bucket
3098 add_drop: => @drops += 10
3099
3100assert Bucket.__name == "BigBucket"
3101```
3102<YueDisplay>
3103<pre>
3104BigBucket = class extends Bucket
3105 add_drop: => @drops += 10
3106
3107assert Bucket.__name == "BigBucket"
3108</pre>
3109</YueDisplay>
3110
3111你甚至可以省略掉主体,这意味着你可以这样写一个空白的匿名类:
3112
3113```moonscript
3114x = class
3115```
3116<YueDisplay>
3117<pre>
3118x = class
3119</pre>
3120</YueDisplay>
3121
3122### 类混合
3123
3124您可以使用关键字 `using` 进行类混合,从一个普通表或预定义的类对象中复制函数到您的新类中。当使用普通表进行混合时,您可以将比如类的索引方法(元方法 `__index`)重写为您的自定义实现。当混合使用的目标是一个类对象时,类对象的元方法不会被复制到新的类中。
3125
3126```moonscript
3127MyIndex = __index: var: 1
3128
3129class X using MyIndex
3130 func: =>
3131 print 123
3132
3133x = X!
3134print x.var
3135
3136class Y using X
3137
3138y = Y!
3139y\func!
3140
3141assert y.__class.__parent ~= X -- X 不是 Y 的父类
3142```
3143<YueDisplay>
3144<pre>
3145MyIndex = __index: var: 1
3146
3147class X using MyIndex
3148 func: =>
3149 print 123
3150
3151x = X!
3152print x.var
3153
3154class Y using X
3155
3156y = Y!
3157y\func!
3158
3159assert y.__class.__parent ~= X -- X 不是 Y 的父类
3160</pre>
3161</YueDisplay>
3162
3163## With 语句
3164
3165在编写Lua代码时,我们在创建对象后的常见操作是立即调用这个对象一系列操作函数并设置一系列属性。
3166
3167这导致在代码中多次重复引用对象的名称,增加了不必要的文本噪音。一个常见的解决方案是在创建对象时,在构造函数传入一个表,该表包含要覆盖设置的键和值的集合。这样做的缺点是该对象的构造函数必须支持这种初始化形式。
3168
3169with块有助于简化编写这样的代码。在with块内,我们可以使用以.或\开头的特殊语句,这些语句代表我们正在使用的对象的操作。
3170
3171例如,我们可以这样处理一个新创建的对象:
3172
3173```moonscript
3174with Person!
3175 .name = "Oswald"
3176 \add_relative my_dad
3177 \save!
3178 print .name
3179```
3180<YueDisplay>
3181<pre>
3182with Person!
3183 .name = "Oswald"
3184 \add_relative my_dad
3185 \save!
3186 print .name
3187</pre>
3188</YueDisplay>
3189
3190with语句也可以用作一个表达式,并返回它的代码块正在处理的对象。
3191
3192```moonscript
3193file = with File "favorite_foods.txt"
3194 \set_encoding "utf8"
3195```
3196<YueDisplay>
3197<pre>
3198file = with File "favorite_foods.txt"
3199 \set_encoding "utf8"
3200</pre>
3201</YueDisplay>
3202
3203或者…
3204
3205```moonscript
3206create_person = (name, relatives)->
3207 with Person!
3208 .name = name
3209 \add_relative relative for relative in *relatives
3210
3211me = create_person "Leaf", {dad, mother, sister}
3212```
3213<YueDisplay>
3214<pre>
3215create_person = (name, relatives)->
3216 with Person!
3217 .name = name
3218 \add_relative relative for relative in *relatives
3219
3220me = create_person "Leaf", {dad, mother, sister}
3221</pre>
3222</YueDisplay>
3223
3224在此用法中,with可以被视为K组合子(k-combinator)的一种特殊形式。
3225
3226如果您想给表达式另外起一个名称的话,with语句中的表达式也可以是一个赋值语句。
3227
3228```moonscript
3229with str = "你好"
3230 print "原始:", str
3231 print "大写:", \upper!
3232```
3233<YueDisplay>
3234<pre>
3235with str = "你好"
3236 print "原始:", str
3237 print "大写:", \upper!
3238</pre>
3239</YueDisplay>
3240
3241在with语句中可以使用`[]`访问特殊键。
3242
3243```moonscript
3244with tb
3245 [1] = 1
3246 print [2]
3247 with [abc]
3248 [3] = [2]\func!
3249 ["key-name"] = value
3250 [] = "abc" -- 追加到 "tb"
3251```
3252<YueDisplay>
3253<pre>
3254with tb
3255 [1] = 1
3256 print [2]
3257 with [abc]
3258 [3] = [2]\func!
3259 ["key-name"] = value
3260 [] = "abc" -- 追加到 "tb"
3261</pre>
3262</YueDisplay>
3263
3264## do 语句
3265
3266当用作语句时,do语句的作用就像在Lua中差不多。
3267
3268```moonscript
3269do
3270 var = "hello"
3271 print var
3272print var -- 这里是nil
3273```
3274<YueDisplay>
3275<pre>
3276do
3277 var = "hello"
3278 print var
3279print var -- 这里是nil
3280</pre>
3281</YueDisplay>
3282
3283月之脚本的 **do** 也可以用作表达式。允许您将多行代码的处理合并为一个表达式,并将do语句代码块的最后一个语句作为表达式返回的结果。
3284
3285```moonscript
3286counter = do
3287 i = 0
3288 ->
3289 i += 1
3290 i
3291
3292print counter!
3293print counter!
3294```
3295<YueDisplay>
3296<pre>
3297counter = do
3298 i = 0
3299 ->
3300 i += 1
3301 i
3302
3303print counter!
3304print counter!
3305</pre>
3306</YueDisplay>
3307
3308```moonscript
3309tbl = {
3310 key: do
3311 print "分配键值!"
3312 1234
3313}
3314```
3315<YueDisplay>
3316<pre>
3317tbl = {
3318 key: do
3319 print "分配键值!"
3320 1234
3321}
3322</pre>
3323</YueDisplay>
3324
3325## 函数存根
3326
3327将对象的函数作为值传递是很常见的,例如,将实例方法传递给函数作为回调。如果函数期望它操作的对象作为第一个参数,那么您必须以某种方式将该对象与函数捆绑在一起,以便可以正确地调用它。
3328
3329函数存根语法是创建一个新的闭包函数的简写,该函数将对象和函数都捆绑在一起。这个新函数在对象的正确上下文中调用被包装的函数。
3330
3331其语法与使用\操作符调用实例方法相同,但不用在后面接着提供参数列表。
3332
3333```moonscript
3334my_object = {
3335 value: 1000
3336 write: => print "值为:", @value
3337}
3338
3339run_callback = (func)->
3340 print "运行回调..."
3341 func!
3342
3343-- 这样写不起作用:
3344-- 函数没有引用my_object
3345run_callback my_object.write
3346
3347-- 函数存根语法
3348-- 让我们把对象捆绑到一个新函数中
3349run_callback my_object\write
3350```
3351<YueDisplay>
3352<pre>
3353my_object = {
3354 value: 1000
3355 write: => print "值为:", @value
3356}
3357
3358run_callback = (func)->
3359 print "运行回调..."
3360 func!
3361
3362-- 这样写不起作用:
3363-- 函数没有引用my_object
3364run_callback my_object.write
3365
3366-- 函数存根语法
3367-- 让我们把对象捆绑到一个新函数中
3368run_callback my_object\write
3369</pre>
3370</YueDisplay>
3371
3372## Using 语句:避免有破坏性的赋值
3373
3374虽然Lua的变量作用域可以在减少我们编写的代码的复杂性上提供很大的帮助,但随着代码量的增加,事情可能会变得难以管理。考虑以下代码片段:
3375
3376```moonscript
3377i = 100
3378
3379-- 许多代码行...
3380
3381my_func = ->
3382 i = 10
3383 while i > 0
3384 print i
3385 i -= 1
3386
3387my_func!
3388
3389print i -- 将打印 0
3390```
3391<YueDisplay>
3392<pre>
3393i = 100
3394
3395-- 许多代码行...
3396
3397my_func = ->
3398 i = 10
3399 while i > 0
3400 print i
3401 i -= 1
3402
3403my_func!
3404
3405print i -- 将打印 0
3406</pre>
3407</YueDisplay>
3408
3409在my_func中,我们错误地覆盖了i的值。在例子中的这个问题很明显,但考虑一个大的或外部的代码库,会很容易不清楚之前已经声明了哪些名称。
3410
3411如果能指定我们只打算更改的封闭作用域中的变量,并防止我们意外地更改其他作用域的同名变量,那将会很有帮助。
3412
3413using语句让我们能够做到这一点。using nil确保在一个函数里的赋值中不会覆盖外部的变量。我们只要把using子句放在函数的参数列表之后,或者当函数没有参数,就直接放在括号里。
3414
3415```moonscript
3416i = 100
3417
3418my_func = (using nil)->
3419 i = "hello" -- 这里创建了一个新的局部变量
3420
3421my_func!
3422print i -- 打印 100,i 没有受到影响
3423```
3424<YueDisplay>
3425<pre>
3426i = 100
3427
3428my_func = (using nil)->
3429 i = "hello" -- 这里创建了一个新的局部变量
3430
3431my_func!
3432print i -- 打印 100,i 没有受到影响
3433</pre>
3434</YueDisplay>
3435
3436using子句中可以填写多个用逗号分隔名称。指定可以访问和修改的外部变量的名称:
3437
3438```moonscript
3439tmp = 1213
3440i, k = 100, 50
3441
3442my_func = (add using k, i)->
3443 tmp = tmp + add -- 创建了一个新的局部tmp
3444 i += tmp
3445 k += tmp
3446
3447my_func(22)
3448print i, k -- 这些已经被更新
3449```
3450<YueDisplay>
3451<pre>
3452tmp = 1213
3453i, k = 100, 50
3454
3455my_func = (add using k, i)->
3456 tmp = tmp + add -- 创建了一个新的局部tmp
3457 i += tmp
3458 k += tmp
3459
3460my_func(22)
3461print i, k -- 这些已经被更新
3462</pre>
3463</YueDisplay>
3464
3465## MIT 许可证
3466
3467版权 (c) 2023 李瑾
3468
3469特此免费授予任何获得本软件副本和相关文档文件(下称“软件”)的人不受限制地处置该软件的权利,包括不受限制地使用、复制、修改、合并、发布、分发、转授许可和/或出售该软件副本,以及再授权被配发了本软件的人如上的权利,须在下列条件下:
3470上述版权声明和本许可声明应包含在该软件的所有副本或实质成分中。
3471本软件是“如此”提供的,没有任何形式的明示或暗示的保证,包括但不限于对适销性、特定用途的适用性和不侵权的保证。在任何情况下,作者或版权持有人都不对任何索赔、损害或其他责任负责,无论这些追责来自合同、侵权或其它行为中,还是产生于、源于或有关于本软件以及本软件的使用或其它处置。
3472
3473<CompilerModal />
diff --git a/doc/docs/zh/try/README.md b/doc/docs/zh/try/README.md
new file mode 100755
index 0000000..c42e5a9
--- /dev/null
+++ b/doc/docs/zh/try/README.md
@@ -0,0 +1,6 @@
1# 月之脚本的在线编译器
2---
3
4在这里试试 WASM 版的 Yuescript 吧。
5
6<YueCompiler />