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
api:non-standard-lua-libs:zh [2023/10/16 11:47]
hfsr [协程操作]
api:non-standard-lua-libs:zh [2023/12/10 14:52] (current)
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}}