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