Lua快速入门教程

文章目录

  • 1、Linux安装Lua
  • 2、语法练习
    • 2.1、变量
    • 2.2、循环
    • 2.3、函数
    • 2.4、数组
    • 2.5、迭代器
    • 2.6、Table操作
    • 2.7、Lua 模块与包
    • 2.8、加载机制
    • 2.9、Lua 元表(Metatable)
  • 3、Lua 协同程序(coroutine)
  • 4、文件IO操作
    • 4.1、简单模式
    • 4.2、完全模式
  • 5、错误处理

内容来源菜鸟教程,主要列举一些重要部分和c语言有歧义的部分
https://www.runoob.com/lua/lua-file-io.html

1、Linux安装Lua

1、下载
wget http://www.lua.org/ftp/lua-5.3.0.tar.gz2、解压
tar zxf lua-5.3.0.tar.gz3、进入目录
cd lua-5.3.04、编译
make linux test5、拷贝到系统位置
make install

2、测试使用
vi test.lua

print(“hello world”)

保存内容

运行lua脚本
lua test.lua

3、首行加入,直接可以运行
#!/usr/local/bin/lua

2、语法练习

1、… 拼接字符串,可以利用nil来删除表中的值

tal = {ke1 = "val1",ke2 = "val2","val3"}
for k,v in pairs(tal) doprint(k .. "-" .. v)
endtal.ke1 = nilfor k,v in pairs(tal) doprint(k,v)
end

2、lua中数字字符串会默认转换成数字进行操作符运算

tal = {}tal["key"] = "val"
key = 10
tal[key]  = 10
tal[key] = tal[key] + 22
for k,y in pairs(tal) doprint(k,y)
end

3、 lua中表中数组下标是从1开始

-- lua中表中数组下标是从1开始
tal = {"key1","key2","key3"}
for key,val in pairs(tal) doprint("key",key)
end

4、在 Lua 中,函数是被看作是"第一类值(First-Class Value)",函数可以存在变量里

function fun(n)if n == 0 thenreturn 1elsereturn n*fun(n-1)end
end
print(fun(5))
fun2 = fun
print(fun2(5))

5、function 可以以匿名函数(anonymous function)的方式通过参数传递:

function fun2(tal,fun)for key,val in pairs(tal) doprint(fun(key,val))end
endtal = {key1 = "val1",key2 = "val2",key3 = "val3"}
fun2(tal,
function(key,val)return key.."="..val
end
)

2.1、变量

1、Lua 变量有三种类型:全局变量、局部变量、表中的域。

a = 5       -- 全局变量
local b = 5 -- 局部变量function joke()c = 5   -- 全局变量local d = 6 -- 局部变量
endjoke()
print(c,d)  --> 5 nildo local a = 6 -- 局部变量b = 6   -- 对局部变量重新赋值print(a,b); --> 6 6
endprint(a,b)  --> 5 6

2、赋值语句

a,b = "hello",111
print(a,b)-- 可以利用这种方式对变量进行交换操作
a,b = 111,222
print(a,b)a,b = b,a
print(a,b)

3、当变量个数和值的个数不一致时,Lua会一直以变量个数为基础采取以下策略:
a. 变量个数 > 值的个数 按变量个数补足nil
b. 变量个数 < 值的个数 多余的值会被忽略

a, b, c = 0, 1
print(a,b,c)    --> 0   1   nila, b = a+1, b+1, b+2    -- value of b+2 is ignored
print(a,b)          --> 1   2a, b, c = 0
print(a,b,c)        --> 0   nil   nil

2.2、循环

这里只是列举出与c语言不一样的地方

重复执行循环,直到 指定的条件为真时为止

--[ 变量定义 --]
a = 10
--[ 执行循环 --]
repeatprint("a的值为:", a)a = a + 1
until( a > 15 )

Lua 语言中的 goto 语句允许将控制流程无条件地转到被标记的语句处。

local i = 0
::s1:: doprint(i)i = i+1
endif i == 1 thengoto s1
end

2.3、函数

可变参数

function add(...)local s = 0  for i, v in ipairs{...} do   --> {...} 表示一个由所有变长参数构成的数组  s = s + vendreturn s
end
print(add(1,2,3,4,5))

Lua 中我们可以将函数作为参数传递给函数

myprint = function (param)print("##",param,"##")
endfunction add(x,y,myp)myp(x+y)
endadd(10,20,myprint)

将可变参数赋值给一个变量
我们也可以通过 select(“#”,…) 来获取可变参数的数量

function average(...)local arg = {...} -- arg为一个表,可变参local result = 0for key,val in ipairs(arg) doresult = result + valendprint("总长度为:",#arg)return result/#arg
endprint(average(1,2,3,4,56))

2.4、数组

多维数组
多维数组即数组中包含数组或一维数组的索引键对应一个数组。
以下是一个三行三列的阵列多维数组:

-- 初始化数组
array = {}
for i=1,3 doarray[i] = {}for j=1,3 doarray[i][j] = i*jend
end-- 访问数组
for i=1,3 dofor j=1,3 doprint(array[i][j])end
end

不同索引键的三行三列阵列多维数组:

-- 初始化数组
array = {}
maxRows = 3
maxColumns = 3
for row=1,maxRows dofor col=1,maxColumns doarray[row*maxColumns +col] = row*colend
end-- 访问数组
for row=1,maxRows dofor col=1,maxColumns doprint(array[row*maxColumns +col])end
end

2.5、迭代器

多状态的迭代器

local tab = {"c","c++"}function element(collection)local index = 0local count = #collection-- 闭包函数return function ()index = index +1if(index<=count) thenreturn collection[index]endend
endfor e in element(tab) doprint(e)
end

2.6、Table操作

Table 连接

fruits = {"banana","orange","apple"}
-- 返回 table 连接后的字符串
print("连接后的字符串 ",table.concat(fruits))-- 指定连接字符
print("连接后的字符串 ",table.concat(fruits,", "))-- 指定索引来连接 table
print("连接后的字符串 ",table.concat(fruits,", ", 2,3))

连接后的字符串 bananaorangeapple
连接后的字符串 banana, orange, apple
连接后的字符串 orange, apple

插入和移除

fruits = {"banana","orange","apple"}-- 在末尾插入
table.insert(fruits,"mango")
print("索引为 4 的元素为 ",fruits[4])-- 在索引为 2 的键处插入
table.insert(fruits,2,"grapes")
print("索引为 2 的元素为 ",fruits[2])print("最后一个元素为 ",fruits[5])
table.remove(fruits)
print("移除后最后一个元素为 ",fruits[5])

索引为 4 的元素为 mango
索引为 2 的元素为 grapes
最后一个元素为 mango
移除后最后一个元素为 nil

Table 排序

fruits = {"banana","orange","apple","grapes"}
print("排序前")
for k,v in ipairs(fruits) doprint(k,v)
endtable.sort(fruits)
print("排序后")
for k,v in ipairs(fruits) doprint(k,v)
end

排序前
1 banana
2 orange
3 apple
4 grapes
排序后
1 apple
2 banana
3 grapes
4 orange

2.7、Lua 模块与包

模块类似一个封装库,从5.1开始,lua中加入了标准的模块管理机制,可以将一个公共的代码,放入到一个文件中,以API的形式提供,lua使用,变量,函数组成的table表,创建一个模块很简单,就是创建一个table表,将需要导入的数据放入到table表中,然后将其返回

module = {}module.constant = "这是一个常量"-- 定义一个函数function module.func1()io.write("这是一个函数")
endlocal function func2()print("这是一个私有函数")
endfunction module.func3()func2()
endreturn module

使用模块
require 函数
Lua提供了一个名为require的函数用来加载模块。要加载一个模块,只需要简单地调用就可以了。例如:
require(“<模块名>”)或者require “<模块名>”

-- 调用模块
-- 执行 require 后会返回一个由模块常量或函数组成的 table,并且还会定义一个包含该 table 的全局变量。
require("module")print(module.constant)
module.func3()

或者给加载的模块定义一个别名变量,方便调用:

-- test_module2.lua 文件
-- module 模块为上文提到到 module.lua
-- 别名变量 m
local m = require("module")print(m.constant)m.func3()

2.8、加载机制

对于自定义的模块,模块文件不是放在哪个文件目录都行,函数 require 有它自己的文件路径加载策略,它会尝试从 Lua 文件或 C 程序库中加载模块。
require 用于搜索 Lua 文件的路径是存放在全局变量 package.path 中,当 Lua 启动后,会以环境变量 LUA_PATH 的值来初始这个环境变量。如果没有找到该环境变量,则使用一个编译时定义的默认路径来初始化。
当然,如果没有 LUA_PATH 这个环境变量,也可以自定义设置,在当前用户根目录下打开 .profile 文件(没有则创建,打开 .bashrc 文件也可以),例如把 “~/lua/” 路径加入 LUA_PATH 环境变量里:

#LUA_PATH
export LUA_PATH="~/lua/?.lua;;"

文件路径以 “;” 号分隔,最后的 2 个 “;;” 表示新加的路径后面加上原来的默认路径。

接着,更新环境变量参数,使之立即生效。

source ~/.profile

这时假设 package.path 的值是:

/Users/dengjoe/lua/?.lua;./?.lua;/usr/local/share/lua/5.1/?.lua;/usr/local/share/lua/5.1/?/init.lua;/usr/local/lib/lua/5.1/?.lua;/usr/local/lib/lua/5.1/?/init.lua

那么调用 require(“module”) 时就会尝试打开以下文件目录去搜索目标。

/Users/dengjoe/lua/module.lua;
./module.lua
/usr/local/share/lua/5.1/module.lua
/usr/local/share/lua/5.1/module/init.lua
/usr/local/lib/lua/5.1/module.lua
/usr/local/lib/lua/5.1/module/init.lua

如果找过目标文件,则会调用 package.loadfile 来加载模块。否则,就会去找 C 程序库。

搜索的文件路径是从全局变量 package.cpath 获取,而这个变量则是通过环境变量 LUA_CPATH 来初始。

搜索的策略跟上面的一样,只不过现在换成搜索的是 so 或 dll 类型的文件。如果找得到,那么 require 就会通过 package.loadlib 来加载它。

2.9、Lua 元表(Metatable)

3、Lua 协同程序(coroutine)

协同程序与线程非常类似,拥有独立的堆栈,和独立的局部变量,独立的指令和指针,又共享全局变量,协同程序可以理解为一种特殊的线程,可以暂停和恢复其执行,从而允许非抢占式的多任务处理。

同程序由 coroutine 模块提供支持。
使用协同程序,你可以在函数中使用 coroutine.create 创建一个新的协同程序对象,并使用 coroutine.resume 启动它的执行。协同程序可以通过调用 coroutine.yield 来主动暂停自己的执行,并将控制权交还给调用者。

function foo()print("协同程序foo开始执行")local value = coroutine.yield("让出foo的执行")print("协同程序 foo 恢复执行,传入的值为: " .. tostring(value))print("协同程序 foo 结束执行")return 1
end-- 创建协同程序
local co = coroutine.create(foo)-- 启动协同程序
local status,result = coroutine.resume(co)
print(result)-- 恢复协同程序的执行,并传入一个值
status, result = coroutine.resume(co, 42)
print(result) -- 输出: 协同程序 foo 恢复执行,传入的值为: 42

以上实例中,我们定义了一个名为 foo 的函数作为协同程序。在函数中,我们使用 coroutine.yield 暂停了协同程序的执行,并返回了一个值
。在主程序中,我们使用 coroutine.create 创建了一个协同程序对象,并使用 coroutine.resume 启动了它的执行。
在第一次调用 coroutine.resume 后,协同程序执行到 coroutine.yield 处暂停,并将值返回给主程序。然后,我们再次调用 coroutine.resume,并传入一个值作为协同程序恢复执行时的参数。执行以上代码输出结果为:

在这里插入图片描述

同程序的状态可以通过 coroutine.status 函数获取,通过检查状态可以确定协同程序的执行情况(如运行中、已挂起、已结束等)。

-- 创建了一个新的协同程序对象 co,其中协同程序函数打印传入的参数 i
co = coroutine.create(function(i)print(i);end
)
-- 使用 coroutine.resume 启动协同程序 co 的执行,并传入参数 1。协同程序开始执行,打印输出为 1
coroutine.resume(co, 1) -- 1-- 通过 coroutine.status 检查协同程序 co 的状态,输出为 dead,表示协同程序已经执行完毕
print(coroutine.status(co)) -- dead
print("----------")-- 使用 coroutine.wrap 创建了一个协同程序包装器,将协同程序函数转换为一个可直接调用的函数对象
co = coroutine.wrap(function(i)print(i);end
)co(1)print("----------")
-- 创建了另一个协同程序对象 co2,其中的协同程序函数通过循环打印数字 1 到 10,在循环到 3 的时候输出当前协同程序的状态和正在运行的线程
co2 = coroutine.create(function()for i=1,10 doprint(i)if i == 3 thenprint(coroutine.status(co2))    --runningprint(coroutine.running())      --thread:XXXXXXendcoroutine.yield()endend
)-- 连续调用 coroutine.resume 启动协同程序 co2 的执行
coroutine.resume(co2) --1
coroutine.resume(co2) --2
coroutine.resume(co2) --3-- 通过 coroutine.status 检查协同程序 co2 的状态,输出为 suspended,表示协同程序暂停执行
print(coroutine.status(co2))    -- suspended
print(coroutine.running())
print("----------")

在这里插入图片描述
更详细的实例:

function foo (a)print("foo 函数输出", a)return coroutine.yield(2 * a) -- 返回  2*a 的值
endco = coroutine.create(function (a , b)
print("第一次协同程序执行输出", a, b) -- co-body 1 10
local r = foo(a + 1)
print("第二次协同程序执行输出", r)
local r, s = coroutine.yield(a + b, a - b) -- a,b的值为第一次调用协同程序时传入
print("第三次协同程序执行输出", r, s)
return b, "结束协同程序" -- b的值为第二次调用协同程序时传入
end)print("main", coroutine.resume(co, 1, 10)) -- true, 4
print("--分割线----")
print("main", coroutine.resume(co, "r")) -- true 11 -9
print("---分割线---")
print("main", coroutine.resume(co, "x", "y")) -- true 10 end
print("---分割线---")
print("main", coroutine.resume(co, "x", "y")) -- cannot resume dead coroutine
print("---分割线---")

生产者消费者模型

-- 生产者消费者模型
-- 1、创建携程
-- 2、调用receive函数,激活协程
-- 2、协程生成一个数据,然后让出协程,将这个数据抛出去
-- 3、receive激活携程这里收到抛出的数据,然后打印出来local newProductorfunction productor()local i = 0while true doi = i + 1send(i)     -- 将生产的物品发送给消费者end
endfunction consumer()while true dolocal i = receive()     -- 从生产者那里得到物品print(i)end
endfunction receive()local status, value = coroutine.resume(newProductor)return value
endfunction send(x)coroutine.yield(x)     -- x表示需要发送的值,值返回以后,就挂起该协同程序
end-- 启动程序
newProductor = coroutine.create(productor)
consumer()

线程和协同程序区别

1、线程与协同程序的主要区别在于,一个具有多个线程的程序可以同时运行几个线程,而协同程序却需要彼此协作的运行。
2、在任一指定时刻只有一个协同程序在运行,并且这个正在运行的协同程序只有在明确的被要求挂起的时候才会被挂起。

4、文件IO操作

4.1、简单模式

简单模式使用标准的 I/O 或使用一个当前输入文件和一个当前输出文件。

-- 以只读方式打开文件
file = io.open("test.lua", "r")-- 设置默认输入文件为 test.lua
io.input(file)-- 输出文件第一行
print(io.read())-- 关闭打开的文件
io.close(file)-- 以附加的方式打开只写文件
file = io.open("test.lua", "a")-- 设置默认输出文件为 test.lua
io.output(file)-- 在文件最后一行添加 Lua 注释
io.write("--  test.lua 文件末尾注释")-- 关闭打开的文件
io.close(file)

在这里插入图片描述

4.2、完全模式

通常我们需要在同一时间处理多个文件。我们需要使用 file:function_name 来代替 io.function_name 方法。以下实例演示了如何同时处理同一个文件

-- 以只读方式打开文件
file = io.open("test.lua", "r")-- 输出文件第一行
print(file:read())-- 关闭打开的文件
file:close()-- 以附加的方式打开只写文件
file = io.open("test.lua", "a")-- 在文件最后一行添加 Lua 注释
file:write("--test")-- 关闭打开的文件
file:close()

5、错误处理

我们可以使用两个函数:assert 和 error 来处理错误。实例如下:

local function add(a,b)assert(type(a) == "number", "a 不是一个数字")assert(type(b) == "number", "b 不是一个数字")return a+b
end
add(10)

error函数

error (message [, level])功能:终止正在执行的函数,并返回message的内容作为错误信息(error函数永远都不会返回)
通常情况下,error会附加一些错误位置的信息到message头部。Level参数指示获得错误的位置:
Level=1[默认]:为调用error位置(文件+行号)
Level=2:指出哪个调用error的函数的函数
Level=0:不添加错误位置信息

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/163545.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

GO 语言的方法??

GO 中的方法是什么&#xff1f; 前面我们有分享到 GO 语言的函数&#xff0c;他是一等公民&#xff0c;那么 GO 语言中的方法和函数有什么区别呢&#xff1f; GO 语言中的方法实际上和函数是类似的&#xff0c;只不过在函数的基础上多了一个参数&#xff0c;这个参数在 GO 语…

AAOS CarMediaService 服务框架

文章目录 前言MediaSessionCarMediaService作用是什么&#xff1f;提供了哪些接口&#xff1f;如何使用&#xff1f;CarMediaService的实现总结 前言 CarMediaService 是AAOS中统一管理媒体播放控制、信息显示和用户交互等功能的服务。这一服务依赖于android MediaSession框架…

Redis入门到实战(四、原理篇)RESP协议

目录 2、Redis内存回收-过期key处理3、Redis内存回收-内存淘汰策略 Redis是一个CS架构的软件&#xff0c;通信一般分两步&#xff08;不包括pipeline和PubSub&#xff09;&#xff1a; 客户端&#xff08;client&#xff09;向服务端&#xff08;server&#xff09;发送一条命令…

linux常见命令-文件目录类

9.4 文件目录类 &#xff08;1&#xff09;pwd 指令:显示当前工作目录的绝对路径 &#xff08;2&#xff09;Is指令:查看当前目录的所有内容信息 基本语法&#xff1a; ls [选项,可选多个] [目录或是文件] 常用选项:-a:显示当前目录所有的文件和目录&#xff0c;包括隐藏的…

ES6 Symbol 数据结构

1. Symbol概念以及引入原因 ES6 引入了的一种新的原始数据类型Symbol&#xff0c;表示独一无二的值。它是 JavaScript 语言的第七种数据类型&#xff0c;前六种是&#xff1a;undefined、null、布尔值&#xff08;Boolean&#xff09;、字符串&#xff08;String&#xff09;、…

Stanford CS224N - word2vec

最近在听Stanford放出来的Stanford CS224N NLP with Deep Learning这门课&#xff0c;弥补一下之前nlp这块基础知识的一些不清楚的地方&#xff0c;顺便巩固一下基础知识&#x1f601; 关于word2vec&#xff1a; 1.为什么要把单词表示成向量 一开始人们造了一个类似于词典表…

Squeeze-and-Attention Networks for Semantic Segmentation

0.摘要 最近&#xff0c;将注意力机制整合到分割网络中可以通过更重视提供更多信息的特征来提高它们的表征能力。然而&#xff0c;这些注意力机制忽视了语义分割的一个隐含子任务&#xff0c;并受到卷积核的网格结构的限制。在本文中&#xff0c;我们提出了一种新颖的squeeze-a…

Linux不间断会话服务

9.2.3远程传输命令 家里有2台电脑&#xff0c;一台是ubuntu虚拟机&#xff0c;另一台是rhel8虚拟机器&#xff0c;两台虚拟机都是通过桥接的方式&#xff08;桥接方式就是虚拟机直接和路由器连接&#xff09;上网。然后现在用ubuntu来使用sshd服务登录rhel8系统。 用ip addr命…

Arcgis聚合工具——实现简单的升尺度

找到Aggregate工具 按如下设置进行操作 注意&#xff1a;如有需要对应的低分辨率影像&#xff0c;必须点开右下角环境Environments选项&#xff0c;进行栅格的捕捉选项设置&#xff0c;以防止升尺度后的影像与需对应的低分辨率影像的栅格单元存在偏移。 点击OK&#xff0c;即可…

算法--排序算法效率比较

《算法设计与分析》课程实验报告 &#xff08; 实验一&#xff09; 实验名称&#xff1a;排序算法效率比较 实验地点&#xff1a; 所使用的开发工具及环境&#xff1a; PC机&#xff0c;DEV 一、实验目的&#xff1a; 比较至少 4 种排序&#xff08;从小到大排&#xff09…

大数据Flink(九十七):EXPLAIN、USE和SHOW 子句

文章目录 EXPLAIN、USE和SHOW 子句 一、EXPLAIN 子句 二、USE 子句

Ruby和面向对象技术

Ruby和许多极为流行的编程语言都是面向对象的。多数的面向对象编程语言&#xff0c;每个对象都是一个样例或者既定类的实例以及独立对象的行为。 一、创建一个通用对象 创建一个通用对象 obj Object.new定义通用对象的行为 def obj.talk puts "I am an object"p…

VMware——VMware17安装WindowServer2012R2环境(图解版)

目录 一、WindowServer2012R2镜像百度云下载二、安装 一、WindowServer2012R2镜像百度云下载 下载链接&#xff1a;https://pan.baidu.com/s/1TWnSRJTk0ruGNn4YinzIgA 提取码&#xff1a;e7u0 二、安装 打开虚拟机&#xff0c;点击【创建新的虚拟机】&#xff0c;如下图&…

直接插入排序

排序——先写单个——再衍生到整体 单个插入排序——在插入前数组里面的数是有序的&#xff0c;然后来了一个数据&#xff0c;就要用这个数组从后往前和这个数比较&#xff0c; 整体的话就是&#xff0c;end从0开始&#xff0c;循环n-1次 void TnsertSort(int* a,int n) {in…

SpringCloud: sentinel链路限流

一、配置文件要增加 spring.cloud.sentinel.webContextUnify: false二、在要限流的业务方法上使用SentinelResource注解 package cn.edu.tju.service;import com.alibaba.csp.sentinel.annotation.SentinelResource; import com.alibaba.csp.sentinel.slots.block.BlockExcept…

【Python】文件操作

一、文件的编码 思考:计算机只能识别:0和1&#xff0c;那么我们丰富的文本文件是如何被计算机识别&#xff0c;并存储在硬盘中呢? 答案:使用编码技术( 密码本)将内容翻译成0和1存入 编码技术即:翻译的规则&#xff0c;记录了如何将内容翻译成二进制&#xff0c;以及如何将二…

nginx平滑升级添加echo模块、localtion配置、rewrite配置

nginx平滑升级添加echo模块、location配置、rewrite配置 文章目录 nginx平滑升级添加echo模块、location配置、rewrite配置1.环境说明&#xff1a;2.nginx平滑升级原理&#xff1a;3.平滑升级nginx&#xff0c;并添加echo模块3.1.查看当前nginx版本以及老版本编译参数信息3.2.下…

【MyBatis】MyBatis日志信息配置

目录 什么是MyBatis相关的日志&#xff1f; 标准日志信息配置&#xff1a; 配置logback日志信息&#xff1a; 什么是MyBatis相关的日志&#xff1f; 首先什么叫做与MyBatis相关的日志呢&#xff1f;就是我们在执行sql语句的时候&#xff0c;如果没有MyBatis相关的日志&…

TX Text Control.NET 32.0 For WPF

TX Text Control 支持VISUAL STUDIO 2022、.NET 5 和 .NET 6 支持 .NET WPF 应用程序的文档处理 将文档编辑、创建和 PDF 生成添加到您的 WPF 应用程序中。 视窗用户界面 功能齐全的文档编辑器 TX Text Control 是一款完全可编程的丰富编辑控件&#xff0c;它在专为 Visual Stu…

基于java的校园论坛系统,ssm+jsp,Mysql数据库,前台用户+后台管理,完美运行,有一万多字论文

目录 演示视频 基本介绍 论文目录 功能架构 系统截图 演示视频 基本介绍 基于java的校园论坛系统&#xff0c;Mysql数据库&#xff0c;系统整体采用ssmjsp设计&#xff0c;前台用户后台管理&#xff0c;完美运行&#xff0c;有一万多字论文。 用户功能&#xff1a; 1.系统…