02Lua入门
前言:
语言学起来其实相似点很多,简单整理的知识点
目录:
1.使用控制台
2.Lua基础
3.变量
4.运算符
5.控制结构
1.使用控制台
Lua脚本是包含一系列Lua命令的简单脚本(扩展名为.lua的文本文件)。Lua不关注格式,制表符,换行符或者其他让脚本文件更具可读性的转换符号(不过Lua还是需要命令之间的空格符号,用来区别变量和函数调用)
简单的Lua脚本:
for indx = 1,4 do
print("line:",indx )
end
也可以写成:for indx = 1,4 do print("line:",indx ) end
注:
格式化后的脚本更容易阅读,但不改变结果
可以把脚本放在本地文件夹通过dofile命令来在控制台执行脚本,参数是字符串,指定文件的名字和路径。使用dofile命令可以在命令行中运行脚本,如果脚本有什么错误的化,命令行会给出错误信息以便调试。
语法格式:
dofile<"c:\\file_name\\lua_scripts_name.lua">
2.Lua基础
优势:整合C++的模块来扩展自身的功能。
语言定义
不以数字为标识符开头(同C),避免下划线接大写字母(_NAME),因为这种格式是Lua自身保留的(保留字),如_Start。
Lua保留关键字
and local break nil do
not else or elseif repeat
end return false then for
true function until if while
in
变量,常量,函数名的格式和命名规则:
常用全大写和下划线(MY_NAME)
变量第一个字母小写(myVariable)
全局变量第一个字母用小写(gMyGlobal)
函数名第一个字母大写(function MyFunction)
--单行注释
--[[
statements 块注释
--]]
使用技巧:--[[ 或者 --]] 加一个 - 可以重新使用这段代码 ---[[ ---]]
3.变量
在Lua中,变量不需要在使用前声明,这个稍微有些争议。因为不需要声明,所以可以才任何地方引入需要的变量。由于不是显式声明,所以很难追踪变量(不造),或者在另一个函数中用了相同名字的变量而造成数据的混乱。因此,必须小心的使用变量,要保证变量是可追踪的。同样不需要指定变量类型,变量类型取决于给变量赋的值,灵活性较高。
变量赋值:
1.赋值改变
2.改变表域
多个变量同时赋值时赋值变量列表和值列表的各个元素用逗号分开,依次赋值。
a,b=1,2
遇到赋值语句Lua会先计算右边所有的值然后在执行赋值操作,所以我们可以这样进行交换变量的值。
x,y=y,x
当变量个数与值个数不一致时,Lua会以变量个数为基础,采取不足补nil,多的忽略。
例:
a,b,c=0,1 --->0,1,nil
a,b,c=0,1,2,3 --->0,1,2
多值赋值常用来交换变量,或将函数调用返回给变量。
例:
a, b = f()
f()返回两个值,第一个赋给a,第二个赋给b。
应该尽可能的使用局部变量,有两个好处:
1. 避免命名冲突。
2. 访问局部变量的速度比全局变量更快。
索引
对 table 的索引使用方括号 []。Lua 也提供了 . 操作。
t[i]
t.i -- 当索引为字符串类型时的一种简化写法
gettable_event(t,i) -- 采用索引访问本质上是一个类似这样的函数调用
例如:
> site = {}
> site["key"] = "www.w3cschool.cc"
> print(site["key"])
www.w3cschool.cc
> print(site.key)
www.w3cschool.cc
变量类型:
注:可以使用Type函数测试变量的类型。
3.1 nil
nil 表示没赋值的变量,实际上可以表示删除变量。
3.2Boolean
Boolean类型的变量只有两种值 :true和false.
3.3string
3.3.1用两个方括号也可以表示一块字符串
特殊字符串:
\a 响铃 \v 垂直指标
\b 退格 \\ 反斜杠
\f 换页符 \" 双引号
\n 换行符 \' 单引号
\r 换行符 \[ 左方括号
\t 制表符 \] 右方括号
\o 空字符 \ddd 八进制
\xhh 十六进制
3.3.2在对一个数字字符串进行操作时,Lua会将这个数字字符串转换成一个数字。
例子:
print(2+6)--8
print("2"+6)--8
print("2"+"6")--8
print("2+6")--2+6
print("abc"+6)--报错,字符串连接不是用+而是两点 ..
print("2"..6)--26
注:..(连接字符串)
#(计算字符串长度,置于字符串前或者变量名前)
3.4Number
双精度浮点数,无整数类型
myNumber=7
myNumber=0.765
myNumber=7.65e8(7.65x10^8,765 000 000)
myNumber=7.65e-2(7.65x10^-2,0.0765)
3.5table
3.5.1实质是关联数组(associative arrays)索引为数字或字符串,初始索引为1.
3.5.2table的创建
3.5.2.1空表
local tab1={}
3.5.2.2初始表
local tab2={"apple","pear"}
注:table不会固定长度大小,有新数据添加时,table会自动增长,没有初始值都为nil.
3.6局部变量和全局变量
Lua变量默认是全局的,也就是说,变量的值在整个会话中是保持不变的,除非脚本改变了它。
当使用全局变量时,变量名前加一个g字母会更加明确,便于调试。
局部变量可以给设定一个初始值,也可以不用赋值。
语法:
local 变量名
变量的有效范围取决于声明变量的位置。
4.运算符
4.1算术运算符
+-*/%^-
4.2关系运算符
==
~=
>
<
>=
<=
注:如果使用关系运算符比较两个table,只有两个table是同一个对象的时候才能得到预期的结果,因为变量只是table对象的引用(像指针一样),不能直接比较存在于table中的值。
实例:
tableA={1,2,3}
tableB={1,2,3}
if tableA=tableB then
print("相等")
else
print("不相等")
end
-->不相等(结构不同)
tableA={1,2,3}
tableB=tableA
if tableA=tableB then
print("相等")
else
print("不相等")
end
-->相等(tableB指向了同样的对象tableA)
4.3逻辑运算符
and(比较两个参数时,第一个false则返回flase,否则返回第二个参数的值)
or
not
注:官方解释的是一坨屎,就和C一样理解就行。
4.4其他运算符
.. 连接字符串
#一元运算符,返回字符串或表的长度
4.5运算符的优先级
从高到低的顺序:
^
not - (unary)
* /
+ -
..
< > <= >= ~= ==
and
or
注:
除了^和..外所有的二元运算符都是左连接的。
a+i < b/2+1 <--> (a+i) < ((b/2)+1)
5+x^2*8 <--> 5+((x^2)*8)
a < y and y <= z <--> (a < y) and (y <= z)
-x^2 <--> -(x^2)
x^y^z <--> x^(y^z)
实例
我们可以通过以下实例来更加透彻的了解 Lua 语言运算符的优先级:
a = 20
b = 10
c = 15
d = 5
e = (a + b) * c / d;-- ( 30 * 15 ) / 5
print("(a + b) * c / d 运算值为 :",e )
e = ((a + b) * c) / d; -- (30 * 15 ) / 5
print("((a + b) * c) / d 运算值为 :",e )
e = (a + b) * (c / d);-- (30) * (15/5)
print("(a + b) * (c / d) 运算值为 :",e )
e = a + (b * c) / d; -- 20 + (150/5)
print("a + (b * c) / d 运算值为 :",e )
以上程序执行结果为:
(a + b) * c / d 运算值为 : 90.0
((a + b) * c) / d 运算值为 : 90.0
(a + b) * (c / d) 运算值为 : 90.0
a + (b * c) / d 运算值为 : 50.0
注:除了^和..外所有二元运算符都是左连接的。
笔记:
1.在Lua中实现C/C++中的三目运算。
condition? result1:result2
|
if condition then
return result1
else
return result2
end
三目运算
熟悉 C/C++ 的老司机都知道三目运算 a ? b : c,这种写法替换简单的条件判断语句可以在不增加阅读难度的情况下,使代码尽量保持简洁。
int a, b, ret;
//if-else
if (a > b)
ret = a;
else
ret = b;
//三目运算符
ret = a > b ? a : b;
Lua 中的三目运算
Lua 原生的语义并没有实现三目运算,一般是通过逻辑运算符 and 和 or 来模拟三目运算符的。
Lua 中 and 和 or 都使用"短路求值(short_cur evaluation)",也就是说,它们只会在需要时才去评估第二个操作数。(《Lua程序设计》)
local a, b, ret;
ret = a > b and a or b
穷举所有可能性:
a > b 时:
a > b and a –> true
a or b –> a
a <= b时:
a > b and a –> false
a or b –> b
完美!
Lua 中的三目运算符陷阱
按照从特殊到一般的原则:
三目运算的一般形式a ? b : c
a = true,结果为b
a = false,结果为c
对应Lua中的a and b or c
b = true
a = true
a and b –> true
b or c –> b
a = false
a and b –> false
b or c –> c
b = false
a = true
a and b –> false
b or c –> c
a = false
a and b –> false
b or c –> c
可以看到当 b = false 时,Lua模拟的 a and b or c 始终返回 c 并不能还原三目运算符的原貌。
《Lua程序设计》也建议这种情况使用 if-else 来避免。
一般化的 a and b or c
那么有没有办法可以解决 b = false 失效的问题呢?
由此我联想到 C 语言有一道常规的考题:请使用宏定义写一个返回两个值中较小值的方法。
在校时第一次看到本题,第一反应如下:
#define MIN(A,B) A < B ? A : B
然而这种写法在很多嵌套的使用环境中都不能返回正确的结果。比如:2 * MIN(3, 4)展开为 2 * 3 < 4 ? 3 : 4 = 6 < 4 ? 3 : 4,结果为4。
更"工程师"的写法是:
#define MIN(A,B) ((A) < (B) ? (A) : (B))
还有好奇心的同学可以参考:宏定义的正确写法,三目运算的宏定义
从这个示例中,我就在想如何能保证 a and b or c 中 b 为真或者 b 不产生歧义呢?
and的运算优先级高于or,简单的改变运算顺序并没有用。
这时就想到了lua中万能的table,能不能把a,b,c都放到table中来改变b的存在呢?要注意{nil}也是一个为true的对象。
a,b,c都替换为table:{a} and {b} or {c}。
三目运算中a是条件,结果是b或者c。其实a并不需要放入table中,否则{a}就始终为true了,失去了条件的意义。而{b} or
{c}的结果也必然是一个table,该table只有一个元素。那么通过[1]即可访问。
综上所述,更一般化的Lua三目运算为:(a and {b} or {c})[1]
2.其他运算符 —— #
在获取表的长度时,根据的是表的最大索引的值:
tab1 = {"1","2"}
print("tab1长度"..#tab1)
tab2 = {key1="1","2"}
print("tab2长度"..#tab2)
tab3 = {}
tab3[1]="1"
tab3[2]="2"
tab3[4]="4"
print("tab3长度"..#tab3)
输出:
tab1长度2
tab2长度1
tab3长度4
3. 下标越过 1 位以上,长度还是为 2:
tab3={}
tab3[1]="1"
tab3[2]="2"
tab3[5]="5"
print("tab3的长度",#tab3)
输出:
tab3的长度 2
4.取长度操作符写作一元操作 #
table t 的长度被定义成一个整数下标 n 。 它满足 t[n] 不是 nil 而 t[n+1] 为 nil; 此外,如果 t[1] 为 nil ,n 就可能是零。 对于常规的数组,里面从 1 到 n 放着一些非空的值的时候, 它的长度就精确的为 n,即最后一个值的下标。 如果数组有一个“空洞” (就是说,nil 值被夹在非空值之间), 那么 #t 可能是指向任何一个是 nil 值的前一个位置的下标 (就是说,任何一个nil 值都有可能被当成数组的结束)。
tab4 = {}
tab4[1] = "1"
tab4[2] = nil
tab4[3] = "2"
tab4[4] = nil
print("tab4的长度", #tab4)
--tab4的长度 1
tab5 = {}
tab5[1] = "1"
tab5[2] = nil
tab5[3] = "2"
tab5[4] = "4"
print("tab5的长度", #tab5)
--tab5的长度 4
tab6 = {1, nil, 3}
print("tab6的长度", #tab6)
--tab6的长度 3
tab6 = {1, nil, 3, nil}
print("tab6的长度", #tab6)
--tab6的长度 1
5.控制结构
注:
控制结构的条件表达式结果可以是任何值,Lua认为false和nil为假,true和非nil为真。
要注意的是Lua中 0 为 true:
5.1if 语句
if 语句 由一个布尔表达式作为条件判断,其后紧跟其他语句组成。
Lua if 语句语法格式如下:
if(布尔表达式)
then
--[ 在布尔表达式为 true 时执行的语句 --]
end
在布尔表达式为 true 时会if中的代码块会被执行,在布尔表达式为 false 时,紧跟在 if 语句 end 之后的代码会被执行。
Lua认为false和nil为假,true 和非nil为真。要注意的是Lua中 0 为 true。
5.2if...else 语句
if 语句 可以与 else 语句搭配使用, 在 if 条件表达式为 false 时执行 else 语句代码。
if...else 语句
Lua if 语句可以与 else 语句搭配使用, 在 if 条件表达式为 false 时执行 else 语句代码块。
Lua if...else 语句语法格式如下:
if(布尔表达式)
then
--[ 布尔表达式为 true 时执行该语句块 --]
else
--[ 布尔表达式为 false 时执行该语句块 --]
end
在布尔表达式为 true 时会if中的代码块会被执行,在布尔表达式为 false 时,else 的代码块会被执行。
Lua认为false和nil为假,true 和非nil为真。要注意的是Lua中 0 为 true。
if...elseif...else 语句
Lua if 语句可以与 elseif...else 语句搭配使用, 在 if 条件表达式为 false 时执行 elseif...else 语句代码块,用于检测多个条件语句。
Lua if...elseif...else 语句语法格式如下:
if( 布尔表达式 1)
then
--[ 在布尔表达式 1 为 true 时执行该语句块 --]
elseif( 布尔表达式 2)
then
--[ 在布尔表达式 2 为 true 时执行该语句块 --]
elseif( 布尔表达式 3)
then
--[ 在布尔表达式 3 为 true 时执行该语句块 --]
else
--[ 如果以上布尔表达式都不为 true 则执行该语句块 --]
end
5.3if 嵌套语句
你可以在if 或 else if中使用一个或多个 if 或 else if 语句 。
if...else 语句
Lua if 语句允许嵌套, 这就意味着你可以在一个 if 或 else if 语句中插入其他的 if 或 else if 语句。
Lua if 嵌套语句语法格式如下:
if( 布尔表达式 1)
then
--[ 布尔表达式 1 为 true 时执行该语句块 --]
if(布尔表达式 2)
then
--[ 布尔表达式 2 为 true 时执行该语句块 --]
end
end
你可以用同样的方式嵌套 else if...else 语句。
注:Lua不支持case语句因此可以用elseif语句
5.4循环
Lua 语言提供了以下几种循环处理方式:
while 循环
在条件为 true 时,让程序重复地执行某些语句。执行语句前会先检查条件是否为 true。
for 循环
重复执行指定语句,重复次数可在 for 语句中控制。
repeat...until
重复执行循环,直到 指定的条件为真时为止
循环嵌套
可以在循环内嵌套一个或多个循环语句(while do ... end;for ... do ... end;repeat ... until;)
5.4.1while
注:
while和repeat 控制结构非常相似,它们都可以循环执行一段脚本直到满足某个条件。
Lua 编程语言中 while 循环语句在判断条件为 true 时会重复执行循环体语句。
语法
Lua 编程语言中 while 循环语法:
while(condition)
do
statements
end
5.4.2repeat
repeat...until 循环的条件语句在当前循环结束后判断。
语法
Lua 编程语言中 repeat...until 循环语法格式:
repeat
statements
until( condition )
5.4.3for
Lua 编程语言中 for 循环语句可以重复执行指定语句,重复次数可在 for 语句中控制。
Lua 编程语言中 for语句有两大类:
数值for循环
泛型for循环
5.4.3.1数值for循环
Lua 编程语言中数值for循环语法格式:
for var=exp1,exp2,exp3 do
<执行体>
end
var从exp1变化到exp2,每次变化以exp3为步长递增var,并执行一次"执行体"。exp3是可选的,如果不指定,默认为1。
示例:
for i=10,1,-1 do
print(i)
end
从10变到1,每次减1
结果:10,9,8,7,6,5,4,3,2,1
5.4.3.2泛型for循环
泛型for循环通过一个迭代器函数来遍历所有值,类似java中的foreach语句。
Lua 编程语言中泛型for循环语法格式:
--打印数组a的所有值
for i,v in ipairs(a)
do print(v)
end
i是数组索引值,v是对应索引的数组元素值。ipairs是Lua提供的一个迭代器函数,用来迭代数组。
days ={"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"}
for i,v in ipairs(days) do
print(v)
end
结果略(ipairs(为表达式列表),后面泛型for里有提到)
注:
1.循环次数只是在第一次执行时确定,因此就算用户更改了参数的值也不会影响最终的循环次数。
2.循环结构中的变量是局部变量,一旦循环结束就会被清除。如果想保存它们的值
,那么必须使用全局变量或者更高级的局部变量。
笔记:
1.
在lua中pairs与ipairs两个迭代器的用法相近,但有一点是不一样的:
pairs可以遍历表中所有的key,并且除了迭代器本身以及遍历表本身还可以返回nil;
但是ipairs则不能返回nil,只能返回数字0,如果遇到nil则退出。它只能遍历到表中出现的第一个不是整数的key
2.pairs遍历表中全部key,value
ipairs从下标为1开始遍历,然后下标累加1,如果某个下标元素不存在就终止遍历。这就导致如果下标不连续或者不是从1开始的表就会中断或者遍历不到元素。
3.
首先,ipairs 这个迭代器只能遍历所有数组下标的值,这是前提,也是和 pairs 的最根本区别,也就是说如果 ipairs 在迭代过程中是会直接跳过所有手动设定key值的变量。
特别注意一点,和其他多数语言不同的地方是,迭代的下标是从1开始的。
3.1
例如:
tab = {1,2,a= nil,"d"}
for i,v in ipairs(tab) do
print(i,v)
end
输出结果为:
1 1
2 2
3 d
这里是直接跳过了a=nil这个变量
3.2
ipairs在迭代过程中如果遇到nil时会直接停止。
例如:
tab = {1,2,a= nil,nil,"d"}
for i,v in ipairs(tab) do
print(i,v)
end
输出结果为:
1 1
2 2
4.
for 循环中,循环的索引 i 为外部索引,修改循环语句中的内部索引 i,不会影响循环次数:
for i=1,10 do
i = 10
print("one time,i:"..i)
end
仍然循环 10 次,只是 i 的值被修改了。
5.5嵌套循环
语法
Lua 编程语言中 for 循环嵌套语法格式:
for init,max/min value, increment
do
for init,max/min value, increment
do
statements
end
statements
end
Lua 编程语言中 while 循环嵌套语法格式:
while(condition)
do
while(condition)
do
statements
end
statements
end
Lua 编程语言中 repeat...until 循环嵌套语法格式:
repeat
statements
repeat
statements
until( condition )
until( condition )
除了以上同类型循环嵌套外,我们还可以使用不同的循环类型来嵌套,如 for 循环体中嵌套 while 循环。
实例
以下实例使用了for循环嵌套:
j =2
for i=2,10 do
for j=2,(i/j) , 2 do
if(not(i%j))
then
break
end
if(j > (i/j))then
print("i 的值为:",i)
end
end
end
以上代码执行结果为:
i 的值为: 8
i 的值为: 9
i 的值为: 10
5.6循环控制语句
循环控制语句用于控制程序的流程, 以实现程序的各种结构方式。
Lua 支持以下循环控制语句:
break 语句
退出当前循环或语句,并开始脚本执行紧接着的语句。
5.7无限循环
在循环体中如果条件永远为 true 循环语句就会永远执行下去,以下以 while 循环为例:
while( true )
do
print("循环将永远执行下去")
end
02Lua入门的更多相关文章
- Angular2入门系列教程7-HTTP(一)-使用Angular2自带的http进行网络请求
上一篇:Angular2入门系列教程6-路由(二)-使用多层级路由并在在路由中传递复杂参数 感觉这篇不是很好写,因为涉及到网络请求,如果采用真实的网络请求,这个例子大家拿到手估计还要自己写一个web ...
- ABP入门系列(1)——学习Abp框架之实操演练
作为.Net工地搬砖长工一名,一直致力于挖坑(Bug)填坑(Debug),但技术却不见长进.也曾热情于新技术的学习,憧憬过成为技术大拿.从前端到后端,从bootstrap到javascript,从py ...
- Oracle分析函数入门
一.Oracle分析函数入门 分析函数是什么?分析函数是Oracle专门用于解决复杂报表统计需求的功能强大的函数,它可以在数据中进行分组然后计算基于组的某种统计值,并且每一组的每一行都可以返回一个统计 ...
- Angular2入门系列教程6-路由(二)-使用多层级路由并在在路由中传递复杂参数
上一篇:Angular2入门系列教程5-路由(一)-使用简单的路由并在在路由中传递参数 之前介绍了简单的路由以及传参,这篇文章我们将要学习复杂一些的路由以及传递其他附加参数.一个好的路由系统可以使我们 ...
- Angular2入门系列教程5-路由(一)-使用简单的路由并在在路由中传递参数
上一篇:Angular2入门系列教程-服务 上一篇文章我们将Angular2的数据服务分离出来,学习了Angular2的依赖注入,这篇文章我们将要学习Angualr2的路由 为了编写样式方便,我们这篇 ...
- Angular2入门系列教程4-服务
上一篇文章 Angular2入门系列教程-多个组件,主从关系 在编程中,我们通常会将数据提供单独分离出来,以免在编写程序的过程中反复复制粘贴数据请求的代码 Angular2中提供了依赖注入的概念,使得 ...
- wepack+sass+vue 入门教程(三)
十一.安装sass文件转换为css需要的相关依赖包 npm install --save-dev sass-loader style-loader css-loader loader的作用是辅助web ...
- wepack+sass+vue 入门教程(二)
六.新建webpack配置文件 webpack.config.js 文件整体框架内容如下,后续会详细说明每个配置项的配置 webpack.config.js直接放在项目demo目录下 module.e ...
- wepack+sass+vue 入门教程(一)
一.安装node.js node.js是基础,必须先安装.而且最新版的node.js,已经集成了npm. 下载地址 node安装,一路按默认即可. 二.全局安装webpack npm install ...
随机推荐
- mysql特殊查询----分组后排序
使用的示例表 学生表----student 表结构 数据 查询方法 一.第一种方法 我认为这是比较传统,比较容易理解的一种方式,使用自连接,并在连接条件中作比较,之后再对查询条件分组统计,排序. se ...
- gitbook 入门教程之实用插件(新增3个插件)
插件没有什么逻辑顺序,大家可以按照目录大纲直接定位到感兴趣的插件部分阅读即可. 更多插件正在陆续更新中,敬请期待... 最新更新插件 tbfed-pagefooter 版权页脚插件 gitalk 评论 ...
- SCSS 在项目中的运用
最后一段时间一直在做一些网站或是CMS的项目,想用bootstrap,但是,设计那哥们说了,用什么都行,就不能用bootstrap,我去了个..... 无语中,逼着自己写.说实话,就是用bootstr ...
- python爬虫实战:利用scrapy,短短50行代码下载整站短视频
近日,有朋友向我求助一件小事儿,他在一个短视频app上看到一个好玩儿的段子,想下载下来,可死活找不到下载的方法.这忙我得帮,少不得就抓包分析了一下这个app,找到了视频的下载链接,帮他解决了这个小问题 ...
- mysql免安装版初次使用
在自己电脑上安装一个mysql数据库并启动,碰到一些问题,总结一下 1.下载免安装版mysql数据库,百度下载了了5.7.25版本 2.在bin文件夹下找到my-defaults.ini文件,我这没有 ...
- Win10 - MySQL 10061 错误
Win10 - MySQL 10061 错误 报错内容为: Can't connect to MySQL server on localhost (10061) 参考 : MySQL问题记录--Can ...
- python离线安装包
一.用download命令离线下载包 *.whl , 这个方法好像python3.7以上才能用 那么我的requirement.txt内容就是: django==1.8.11 simplejson= ...
- linux用户身份和文件权限
1.用户身份与能力 root管理员是linux 的超级用户,他拥有系统的所有权,能够管理系统的各项功能,如添加/删除用户,启动/关闭服务进程,开启/禁用硬件设备…… "Linux系统中的管理 ...
- [LeetCode] 5. 最长回文子串
题目链接:https://leetcode-cn.com/problems/longest-palindromic-substring/ 题目描述: 给定一个字符串 s,找到 s 中最长的回文子串.你 ...
- 深入理解Fabric环境搭建的详细过程
博主之前的文章都是教大家怎么快速的搭建一个Fabric的环境,但是其中大量的工作都隐藏到了官方的脚本中,并不方便大家深入理解其中的过程,所以博主这里就将其中的过程一步步分解,方便大家! 前面的准备工作 ...