Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
Last revision Both sides next revision
api:non-standard-lua-libs:zh [2022/10/20 23:33]
pointtttt [非标准 Lua 库]
api:non-standard-lua-libs:zh [2023/11/28 10:40]
hfsr [调试]
Line 1: Line 1:
-非标准 Lua 库+非标准Lua库
 ====================== ======================
-大部分 Lua 标准库都是可用的,尽管其中的一些只是部分可用或重新实现的——这意味着表现可能会略有不同。请参阅 [Lua 5.3 手册](http://​www.lua.org/​manual/​5.3/​manual.html#​6) 中关于标准库的文档。+大部分Lua标准库都是可用的,尽管其中的一些只是部分可用或重新实现的——这意味着其行为可能会略有不同。请参阅[Lua 5.3 手册](http://​www.lua.org/​manual/​5.3/​manual.html#​6)(英文)中关于标准库的文档。
  
-对“重新实现”的描述是基于 OpenOS 的,其他自定义的操作系统可能会有不同的细节。+对“重新实现”的描述是基于OpenOS的,其他自定义的操作系统可能会有不同的细节。
  
-本页试列出所有与标准库的差异。+本页将尝试列出所有与标准库的**差异**
  
 基本函数 基本函数
 --------------- ---------------
-库中的[原函数](http://​www.lua.org/​manual/​5.3/​manual.html#​6.1)是可用的,但有以下不同:+库中的[原函数](http://​www.lua.org/​manual/​5.3/​manual.html#​6.1)是可用的,但有以下不同:
  
-- `collectgarbage` **可用. +- `collectgarbage`**可用**
-- `dofile` 和 `loadfile` 被重新实现为从已挂载的文件系统组件内加载文件(使用[[api:​filesystem:​zh|文件系统 API]] 或重新实现过的 `io` 库)。 +- `dofile`和`loadfile` 被重新实现为从已挂载的文件系统组件(使用[[api:​filesystem:​zh|文件系统 API]]或重新实现过的 `io` 库)内加载文件。 
-- `load` 在默认情况下只能用来加载文本,不支持二进制(即编译过的代码。注意,配置中可以启用字节码的加载,但非常不推荐这样做,因为这是一个*重安全风险*。 +- `load`在默认情况下只能用来加载文本,不支持二进制编译过的代码。注意,配置文件中可以启用字节码的加载,但非常不推荐这样做,因为这是一个**重安全风险**。 
-- `print`、`io.write`、`io.stdout:​write` 和 `term.write` 都向stdout(标准输出)推送字符串,这在大多数情况下都指gpu渲染,即打印到屏幕上。+- `print`、`io.write`、`io.stdout:​write`和`term.write`都向stdout(标准输出)推送字符串,这在大多数情况下都指gpu渲染,即输出到屏幕上。
  
 协程操作 协程操作
 ---------------------- ----------------------
-`coroutine` 库内的[原函数](http://​www.lua.org/​manual/​5.3/​manual.html#​6.2)均可用,并没有可见的差异。+`coroutine`库内的[原函数](http://​www.lua.org/​manual/​5.3/​manual.html#​6.2)均可用,并没有可见的差异。
  
-注意,暴露给用户使用的 `coroutine.resume` 和 `coroutine.yield` 是封装过的,它们负责中止在一定时间后还没有 yield 的代码(见配置),还可以区分系统产生的 yield 和用户产生的 yield(系统会产生“bubble”,例如在关闭命令行或调用组件 API 时)。然而这不应该从用户代码层面被注意到。如果被注意到了,那这应该被认为是一个 ​bug。+注意,暴露给用户使用的`coroutine.resume`和`coroutine.yield`实现是封装过的,它们负责中止在一定时间(见配置文件)后还没有yield的代码,还可以区分系统产生的yield和用户产生的yield(系统会产生“bubble”,例如在关闭命令行或调用组件 API 时)。然而这不应该从用户代码层面被注意到。如果被注意到了,应该被认为是bug。
  
 模块 模块
 ------- -------
-模块部分已针对 OpenComputer 重新实现。应该跟[原本的](http://​www.lua.org/​manual/​5.3/​manual.html#​6.3)相同,但它没有以下函数:+模块部分已针对OpenComputer重新实现。效果应该跟[原本的](http://​www.lua.org/​manual/​5.3/​manual.html#​6.3)相同,但它没有以下函数:
  
-- `package.config` ​没有用 +- `package.config`不存在且未使 
-- `package.cpath` ​没有用 +- `package.cpath`不存在且未使 
-- `package.loadlib` 未实现+- `package.loadlib`未实现
  
-后两者不存在是因为OpenComputer不能加载C语言程序+后两者不存在是因为OpenComputer不能加载C语言代码
  
-`require` 是一个在全局范围内可用的方法,也就是说,你不需要加载任何模块就能访问它,你可以在脚本的第一行就使用它(这是很常见的用法)。它的工作依赖于 `package` 库,因此我们就在这里对它进行介绍。 +`require`是在全局范围内可用的方法,也就是说,你不需要加载任何模块就能访问它,你可以在脚本的第一行就使用它(这是很常见的用法)。它的工作依赖于`package`库,因此我们就在这里对它进行介绍。
- +
-  * ''​require(library:​ string): table''​ +
- +
-  返回名字对应的 `library`。首先,如果该库先前已被加载过,那么 `package` 库已经缓存过它了,`require` 会返回该库的缓存版本。如果想要卸载库,参见 `package.loaded`。如果库没有被缓存,就会搜索 `package.path` 直到找到匹配的库。 +
- +
-  * ''​package.path''​ +
-**我们建议用户不要修改默认的 package.path,相反地,用户应该把自定义库放入 /usr/lib/ 中。** +
- +
-  定义了一个供 `require` 遍历寻找库的搜索路径列表。它是一个用分号分隔的路径列表,使用 ‘?’ 作为传递给 `require` 库名的占位符。下面的例子会使它更容易理解 +
- +
-  默认 package.path: +
- +
-  `/​lib/?​.lua;/​usr/​lib/?​.lua;/​home/​lib/?​.lua;​./?​.lua;/​lib/?/​init.lua;/​usr/​lib/?/​init.lua;/​home/​lib/?/​init.lua;​./?/​init.lua` +
- +
-  如果用户尝试导入 "​foobar"​ +
- +
-  `local foobar = require("​foobar"​)` +
- +
-  以下是 `require` 为了执行 require("​foobar"​) 而会去查找的文件的顺序。我们假设当前工作目录是 /​tmp/​,因为这样很 Cool!+
  
 +  * ''​require(library:​ string): table''  ​
 +  返回给定名称对应的`library`。首先,如果该库先前已被加载过,那么`package`库已经缓存它了,`require`会返回该库的缓存版本。如果想要卸载已预缓存的库,参见`package.loaded`。如果库没有被缓存,就会搜索`package.path`直到找到匹配的库。  ​
 +\\
 +  * ''​package.path''  ​
 +**我们建议用户不要修改默认的`package.path`,相反地,用户应该把自定义库放入`/​usr/​lib/​`中。**  ​
 +  定义了一个供 `require` 遍历寻找库的搜索路径列表。它是一个用分号分隔的路径列表,使用`?​`作为传递给`require`库名的占位符。下面的例子会使它更容易理解。  ​
 +\\
 +  默认 package.path:  ​
 +`/​lib/?​.lua;/​usr/​lib/?​.lua;/​home/​lib/?​.lua;​./?​.lua;/​lib/?/​init.lua;/​usr/​lib/?/​init.lua;/​home/​lib/?/​init.lua;​./?/​init.lua`  ​
 +\\
 +  如果用户尝试导入"​foobar"  ​
 +  `local foobar = require("​foobar"​)`  ​
 +\\
 +  以下是`require`为了执行require("​foobar"​) 而会去查找的文件的顺序。为了有趣,我们假设当前工作目录是 /​tmp/​。  ​
     - /​lib/​foobar.lua     - /​lib/​foobar.lua
     - /​usr/​lib/​foobar.lua     - /​usr/​lib/​foobar.lua
Line 61: Line 56:
     - /​home/​lib/​foobar/​init.lua     - /​home/​lib/​foobar/​init.lua
     - /​tmp/​foobar/​init.lua     - /​tmp/​foobar/​init.lua
- +\\ 
-  * ''​package.loaded''​ +  * ''​package.loaded'' ​  
-**修改 package.loaded 的行为是不标准** +**修改package.loaded的行为并非标准操作**   
- +  一个包含已缓存库源码,键为库的名称即传入 `require` 的那个,值是缓存的库本身。将该表中的某个值设为`nil`的效果为从缓存中删除这个库。为了操作系统的正常运行,有些库会假定保持加载状态。
-  一个包含已缓存库的table,键为库的名称 ​(即传入 `require` 的那个),值是缓存的库本身。将该表中的某个值设为 `nil` 从缓存中实实在在地删除这个库。有些库会假定保持加载状态,为了操作系统的正常运行+
  
 字符串操作 字符串操作
 ------------------- -------------------
-`string` 库内的[原函数](http://​www.lua.org/​manual/​5.2/​manual.html#​6.4)均可用,没有任何变化。+`string`库内的[原函数](http://​www.lua.org/​manual/​5.2/​manual.html#​6.4)均可用,没有任何变化。
  
-注意,GPU API 中的函数可以接收 ​UTF-8 字符串,进而 `term.write` 和 `print` 也可以。为了帮助你处理 UTF-8 字符串,我们准备了一个额外的库,即 [[api:​unicode:​zh|Unicode API]]。+注意,GPU API中的函数工作于UTF-8字符串环境下,进而`term.write`和`print`也一样。为了帮助你处理UTF-8字符串,我们准备了一个额外的库,即[[api:​unicode:​zh|Unicode API]]。
  
 表操作 表操作
 ------------------ ------------------
-`table` 库内的[原函数](http://​www.lua.org/​manual/​5.2/​manual.html#​6.5)均可用,没有任何变化。+`table`库内的[原函数](http://​www.lua.org/​manual/​5.2/​manual.html#​6.5)均可用,没有任何变化。
  
 数学函数 数学函数
Line 82: Line 76:
  
  
-- `math.random` 为每个 Lua 状态和计算机使用一个单独的 `java.util.Random` 实例。+- `math.random` 为每个 Lua state和计算机使用一个单独的 `java.util.Random` 实例。
 - `math.randomseed` 也被应用于对应的实例。 - `math.randomseed` 也被应用于对应的实例。
  
 位操作 位操作
 ------------------ ------------------
-`bit32` 库内的[原函数](http://​www.lua.org/​manual/​5.2/​manual.html#​6.7)均可用,没有任何变化。+`bit32`库内的[原函数](http://​www.lua.org/​manual/​5.2/​manual.html#​6.7)均可用,没有任何变化。
  
-输入输出设备+输入输出功能
 --------------------------- ---------------------------
-`io` 库中大部分[原函数](http://​www.lua.org/​manual/​5.2/​manual.html#​6.8)都被重新实现了,它们工作在已挂载的文件系统组件上。标准输入通过 `io.read` (同 `term.read` 和 `io.stdin:​read`)读取。 标准输出通过 `io.write` (同 `term.write`、`io.stdout:​write` 和 `print`)写入。标准错误通过 `io.stderr:​write` 写入。+`io`库中[原函数](http://​www.lua.org/​manual/​5.2/​manual.html#​6.8)都被很大程度上重新实现了,它们工作在已挂载的文件系统组件上。标准输入通过`io.read`(以及`term.read`和`io.stdin:​read`)读取。 标准输出通过`io.write`(以及`term.write`、`io.stdout:​write`和`print`)写入。标准错误通过`io.stderr:​write`写入。
  
-大多数情况下,它们应该与标准 Lua 实现在*功能上*等价。也许它们返回的错误信息与 ​vanilla ​Lua 不同,但大多数情况下vanilla ​Lua使用的也是平台相关的 C 异常库,因此将这些用于程序逻辑无论如何都不是一个好主意。+大多数情况下,它们应该与标准Lua实现在**功能上**等价。也许它们返回的错误信息与原版Lua 不同,但大多数情况下原版Lua使用的也是基于平台的C异常库,因此将这些用于程序逻辑无论如何都不是一个好主意。
  
-- `io.open(path,​ mode)` **支持 `+` 模式,即只支持 `r`、`w`、`a` 和 `rb` (`wb` 和 `ab` 是允许的,但是二进制模式对写入来说没有意义)。 注意, `io.open()` 返回 [[api:​buffer:​zh#​instance_methods|缓冲流]],有比较智能的 ​api,如 `read("​*a"​)` 可以读取整个文件,或者 `io.read("​*l"​)` 可以读取一行。注意,缓冲流(由 `io.open`返回的)可以是二进制模式或文本模式,而原始流(由 `filesystem.open`返回的)是且只能是二进制模式。 流是二进制模式还是文本模式只会影响到 `read`。+- `io.open(path,​ mode)`**支持**`+`模式,即只支持`r`、`w`、`a`和`rb`(允许使用`wb` 和 `ab` 模式,但是二进制模式对写入来说没有意义)。 ​  
 +注意,`io.open()`返回[[api:​buffer:​zh#​实例方法|缓冲IO流]],有比较智能的API,如`read("​*a"​)` 可以读取整个文件,或者`io.read("​*l"​)`可以读取一行。 ​  
 +还需注意,缓冲IO流(由 `io.open`返回的)可以是二进制模式或文本模式,而原始流(由 `filesystem.open`返回的)是且只能是二进制模式。 流是二进制模式还是文本模式只会影响到 `read`。
  
   `path` 是你想打开的文件的相对路径或绝对路径。   `path` 是你想打开的文件的相对路径或绝对路径。
Line 101: Line 97:
   `mode` 可以是 `nil` 或一个字符串, `nil` 默认为“r”。   `mode` 可以是 `nil` 或一个字符串, `nil` 默认为“r”。
  
-  - **二进制模式** +  - **二进制模式** ​  
- +  `io.open(path,​ "​rb"​)`或`filesystem.open(path)`返回的流是二进制格式。`filesystem.open(path,​ "​rb"​)`的写法也可以,但从`filesystem.open`返回的流**永远**都是二进制模式。 ​  
-  `io.open(path,​ "​rb"​)` 或 `filesystem.open(path)` 返回的流是二进制格式。 `filesystem.open(path,​ "​rb"​)` 也可以,但从 `filesystem.open` 返回的流**永远**都是二进制模式。 `stream:​read(1)` ​在二进制模式下读取一个字节。通过 `buffered_stream:​read("​*n"​)` 读取一个数值会数据读成单字节字符。(缓冲流从 `io.open` 返回,并支持从流中解释数值) +  二进制模式下的`stream:​read(1)`读取一个字节。 ​  
- +  ​通过`buffered_stream:​read("​*n"​)`读取一个数值会数据单字节字符的形式读取。(缓冲流从 `io.open` 返回,并支持从流中解释数值) ​  
-  - **文本模式** +\\ 
- +  - **文本模式** ​  
-  只有从 `io.open` 返回的,模式中没有使用 "​b"​ 的流才是文本模式。例如 `io.open(path)` 和 `io.open(path,​ "​r"​)`。`filesystem.open` 返回的任何句柄都不是文本模式下的流。文本模式下的 `stream:​read(1)` ​是一个节码的 char。它可能是单字节的,或者甚至有3个字节——这取决于文本。 通过 `buffered_stream:​read("​*n"​)` ​一个数值读取成 ​unicode 字符。(缓冲流是由 `io,open` 返回的,支持从流中解释数值) +  只有从`io.open`返回的,模式中没有使用"​b"​的流才是文本模式。例如`io.open(path)`和`io.open(path,​ "​r"​)`。`filesystem.open`返回的任何句柄都不是文本模式下的流。 ​  
-  * `io.open(path,​ "​r"​)` 相当于 `io.open(path)`,它以文本只读模式打开一个文件。 +  ​文本模式下的`stream:​read(1)`读取个Unicode。它可能是单字节的,或者甚至有3个字节取决于文本。 ​  
-  * `io.open(path,​ "​rb"​)` 以二进制只读模式打开一个文件。 +  ​通过`buffered_stream:​read("​*n"​)`读取一个数会将数据以unicode字符的形式进行读取。(缓冲IO流是由 `io,open` 返回的,支持从流中解释数值) ​  
-  * `io.open(path,​ "​w"​)` ​截断文件的所有内容,以写模式打开文件。流是二进制模式还是文本模式并不影响写入。因此“w”在功能上等价于“wb”。 + \\ 
-  * `io.open(path,​ "​a"​)` 以写模式打开一个文件并将文件句柄位置放在文件的末尾,这意味着下一次写入会追加到文件末尾。 +  * `io.open(path,​ "​r"​)` 相当于 `io.open(path)`,它以文本只读模式打开一个文件。 ​  
-- `io.stdin` 从模拟的 stdin 中读取数据,它默认为 ​openos ​shell 中的用户输入。注意 `io.read()` 是 `io.stdin:​read()` 的简,它们会被解析为完全相同的操作。+\\ 
 +  * `io.open(path,​ "​rb"​)` 以二进制只读模式打开一个文件。 ​  
 +\\ 
 +  * `io.open(path,​ "​w"​)` ​删除文件的所有内容,以写模式打开文件。流是二进制模式还是文本模式并不影响写入。因此“w”在功能上等价于“wb”。 ​  
 +\\ 
 +  * `io.open(path,​ "​a"​)` 以写模式打开一个文件并将文件句柄位置放在文件的末尾,这意味着下一次写入会追加到文件末尾。 ​  
 +\\ 
 +- `io.stdin`从模拟的stdin中读取数据,它默认为OpenOS ​shell中的用户输入。注意`io.read()`是`io.stdin:​read()`的简,它们会被解析为完全相同的操作。
  
 ```lua ```lua
-io.stdin:​read() -- 从 std in 中读取 +io.stdin:​read() -- 从stdin中读取 
-io.read() -- 也是从 ​std in 中读取 +io.read() -- 也是从stdin中读取 
-term.read() -- 也是从 ​std in 中读取+term.read() -- 也是从stdin中读取
 ``` ```
  
-- `io.stdout` 将数据写到模拟的 stdout,默认在 ​gpu 上渲染。注意 `io.write()` 是 `io.stdout:​write()` 的简称,它们会被解析为完全相同的操作。+- `io.stdout`将数据写到模拟的stdout,默认GPU上渲染。注意`io.write()`是`io.stdout:​write()`的简称,它们会被解析为完全相同的操作。
  
 ```lua ```lua
-io.stdout:​write("​写入 stdout"​) +io.stdout:​write("​写入stdout"​) 
-io.write("​也是写入 stdout"​) +io.write("​也是写入stdout"​) 
-term.write("​也是写入 stdout,但如果字符串超过屏幕分辨率允许的宽度,就会自动换行,就像这样--------------------------------------------------------------------------------------------------------------------------------#​")+term.write("​也是写入stdout,但如果字符串超过屏幕分辨率允许的宽度,就会自动换行"​)
 ``` ```
  
-- `io.stderr` 将数据写到模拟的 stderr,默认在 ​gpu 上渲染,但如果主 GPU 和屏幕支持的话,会尝试用红色渲染。没有像 stdin 和 stdout 那样的使用 stderr 的快捷方式。+- `io.stderr`将数据写到模拟的stderr,默认在GPU上渲染,但如果首选GPU和屏幕支持的话,会尝试用红色渲染。没有像stdin和stdout那样的使用stderr的快捷方式。
  
 ```lua ```lua
-io.stderr:​write("​向 stderr 写入错误信息"​)+io.stderr:​write("​向stderr写入错误信息"​)
 ``` ```
  
-操作系统设备+操作系统功能
 --------------------------- ---------------------------
-`os` 库内的[原函数](http://​www.lua.org/​manual/​5.2/​manual.html#​6.9)已经被部分重新实现。+`os`库内的[原函数](http://​www.lua.org/​manual/​5.2/​manual.html#​6.9)已经被部分重新实现。
  
-- `os.clock` 被重新实现为返回一个近似的 CPU 时间,也就是计算机在一个执行器线程中实际运行的时间。这与计算机运行时间**一样,见 [[api:​computer:​zh|computer.uptime]]。 +- `os.clock`被重新实现为返回近似CPU时间,也就是计算机在一个执行器线程中实际运行的时间。这与计算机运行时间**一样**获取此时间请参见[[api:​computer:​zh|computer.uptime]]。 ​  
-- `os.date` 被重新实现为游戏中的时间,并支持大多数格式。 +\\ 
-- `os.execute` 被重新实现,相当于从已挂载的文件系统中通过 `shell.execute` 启动程序。传入的字符串相当于在 shell 内输入的命令。 +- `os.date`被重新实现为使用游戏时间,并支持大多数格式。 ​  
-- `os.exit` 抛出一个错误并试图终止当前的 ​coroutine。 +\\ 
-- `os.setenv` 添加 Lua shell 的环境变量。 +- `os.execute`被重新实现,相当于从已挂载的文件系统中通过`shell.execute`启动程序。传入的字符串相当于在 shell 内输入的命令。 ​  
-- `os.remove` 是 `filesystem.remove` 的别名。 +\\ 
-- `os.rename` 是 `filesystem.rename` 的别名。 +- `os.exit`抛出一个错误并试图终止当前的协程。   
-- `os.setlocale` **可用。+\\ 
 +- `os.setenv`添加Lua shell的环境变量。 ​  
 +\\ 
 +- `os.remove`是`filesystem.remove`的别名。 ​  
 +\\ 
 +- `os.rename`是`filesystem.rename`的别名。 ​  
 +\\ 
 +- `os.setlocale`**可用**。   
 +\\
 - `os.time` 被重新实现为返回自世界创建以来的游戏时间。 - `os.time` 被重新实现为返回自世界创建以来的游戏时间。
-  注意这个时间是以“游戏内秒”为单位的。要获得世界创建起的时间刻(tick,需要将其乘以 `1000/​60/​60` (因为一天有24000个时间刻) 然后减去 6000。这个 6000 的偏移量不是随便设的,它可以确保早上 6 点确实是那个时候我的世界莫名其妙地认为早上六点是零,可能是因为那是一个新游戏开始的时间……+  注意这个时间是以“游戏内秒”为单位的。要获得世界创建起的tick,需要将其乘以 `1000/​60/​60`(因为一天有24000个时间刻)然后减去6000。这个6000的偏移量不是随便设的,它可以确保早上6点确实是早上6点Minecraft莫名其妙地认为早上六点是零,可能是因为那是新游戏开始的时间…… ​  
 +\\
 - `os.tmpname` 已被重新实现,可以创建一个未使用的名字挂载到 `/tmp` 下。 - `os.tmpname` 已被重新实现,可以创建一个未使用的名字挂载到 `/tmp` 下。
  
 额外增加了一个函数: 额外增加了一个函数:
  
-- `os.sleep(seconds:​ number)` 允许指定时间内暂停脚本。`os.sleep` 消耗事件,但注册的事件处理程序和线程仍接收事件。换句话说,在睡眠状态下,信号仍然会被事件处理程序处理,也就是说你无法在睡眠结束后拉取睡眠期间堆积的信号,因为没有信号会留在队列中(或者说至少不是全部信号)。+- `os.sleep(seconds:​ number)`允许暂停脚本指定时间长度。`os.sleep`消耗事件,但注册的处理函数和线程在休眠时接收事件。换句话说,在睡眠状态下,信号仍然会被处理函数处理,也就是说你无法在睡眠结束后拉取睡眠期间堆积的信号,因为没有信号会留在队列中(或者说至少不是全部信号)。
  
-[[api:​computer:​zh|计算机 ​API]] 提供了一些类似这个类别的函数+此类别的一些新函数可以在[[api:​computer:​zh|computer(电脑) ​API]]中找到
  
 调试 调试
 ----- -----
-只实现了 `debug.traceback` 和 `debug.getinfo` ​(1.5.9 起)后者仅限被动信息。+只实现了`debug.traceback`和`debug.getinfo`1.5.9起,仅限被动信息。
  
 目录 目录
 ----------- -----------
 {{page>​api:​contents:​zh&​noheader&​noeditbutton&​nouser&​nofooter}} {{page>​api:​contents:​zh&​noheader&​noeditbutton&​nouser&​nofooter}}