• lua快速入门~在js基础上,知道Lua 和 Js 的不同即可


    ☺ lua 和 javaScript 差不多的,就是一些语法的细节不同,学过js,再注意一下下面的细节,就能上手了~

    快速入门,可以直接看一下菜鸟教程的lua:https://www.runoob.com/lua/lua-tutorial.html


    Lua 和 Js 的不同

    Lua 概述

    Lua概述

    • Lua是一种轻量小巧的脚本语言,用标准C语言编写并以源代码形式开放, 其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。

    Lua特性

    1.轻量级

    • 使用标准C语言编写并以源代码形式开放,编译后仅仅一百余K,可以很方便的嵌入别的程序里。

    2.可扩展

    • Lua提供了非常易于使用的扩展接口和机制:由宿主语言(通常是C或C++)提供这些功能,Lua可以使用它们,就像是本来就内置的功能一样。

    3.其它特性

    支持面向过程(procedure-oriented)编程和函数式编程(functional programming)

    自动内存管理;只提供了一种通用类型的表(table),用它可以实现数组,哈希表,集合,对象、字典,并且table是变长变量

    语言内置模式匹配;闭包(closure);函数也可以看做一个值;提供多线程(协同进程,并非操作系统所支持的线程)支持

    通过闭包和table可以很方便地支持面向对象编程所需要的一些关键机制,比如数据抽象,虚函数,继承和重载等


    1、注释

    (1) 单行注释:

    --
    

    (2) 多行注释:

    --[[
     多行注释
     多行注释
     --]]
    

    2、变量

    全局变量:

    • 默认情况下,没有被local 修饰的变量都是全局变量,不管它的位置在哪里
    • 访问一个没有初始化的全局变量也不会出错,只不过得到的结果是:nil
    • 如果你想删除一个全局变量,只需要将变量赋值为nil。

    局部变量[私有]

    • local 修饰

    nil (空)

    字符串

    • 字符串由一对双引号单引号来表示

    • 也可以用 2 个方括号 "[[]]" 来表示"一块"字符串

    string1 = "this is string1"
    string2 = 'this is string2'
    
    html = [[
    
    
    
        菜鸟教程
    
    
    ]]
    print(html)
    
    

    ☺ 字符串拼接,使用的是 ..

    -- 举例子:
    > print("a" .. 'b')
    ab
    > print(157 .. 428)
    157428
    

    加号 + ,这和java有区别,会把字符串转成数学的加分运算

    -- 举例子:
    > print("2" + 6)
    8.0
    > print("2" + "6")
    8.0
    

    计算字符串长度,使用#

    > len = "www.runoob.com"
    > print(#len)
    

    3、循环语句、判断语句

    循环语句

    (1) while

    while(condition)
    do
       statements
    end
    

    (2) for

    var 从 exp1 变化到 exp2,每次变化以 exp3 为步长递增 var,并执行一次 "执行体"。exp3 是可选的,如果不指定,默认为1。

    for var=exp1,exp2,exp3 do  
        <执行体>  
    end 
    
    
    -- 举例子:
    tab1 = { key1 = "val1", key2 = "val2", "val3" }
    for k, v in pairs(tab1) do
        print(k .. " - " .. v)
    end
    

    (3) repeate

    repeat
       statements
    until( condition )
    

    判断语句

    --[ 0 为 true ]
    if(0)
    then
        print("0 为 true")
    end
    

    4、函数获取可变参数的长度,也是使用#变量,或者使用select方法

    -- 举例子:
    function average(...)
       result = 0
       local arg={...}    --> arg 为一个表,局部变量
       for i,v in ipairs(arg) do
          result = result + v
       end
       print("总共传入 " .. #arg .. " 个数")
       return result/#arg
    end
    
    print("平均值为",average(10,5,3,4,5,6))
    
    
    -----------------------------------
    function average(...)
       result = 0
       local arg={...}
       for i,v in ipairs(arg) do
          result = result + v
       end
       print("总共传入 " .. select("#",...) .. " 个数")
       return result/select("#",...)
    end
    
    print("平均值为",average(10,5,3,4,5,6))
    

    5、select 方法

    • select('#', …) 返回可变参数的长度。
    • select(n, …) 用于返回从起点 n 开始到结束位置的所有参数列表
      • 如果这时候使用一个变量指向方法 select(n, …),它只会得到第 n 位置的参数值
    function f(...)
        a = select(3,...)  -->从第三个位置开始,变量 a 对应右边变量列表的第一个参数
        print (a)
        print (select(3,...)) -->打印所有列表参数
    end
    
    f(0,1,2,3,4,5)
    -- 结果:
    2
    2       3       4       5
    

    6、lua 的运算符

    • 通用的加减乘除外,还有整除 // , 比如:5//2 输出结果 2

    • 不等于 ~=

    • 逻辑运算符:and、or、not

    • 其他运算符:

    .. 连接两个字符串

    # 一元运算符,返回字符串或表的长度


    7、转义字符

    所有的转义字符和所对应的意义:

    转义字符 意义 ASCII码值(十进制)
    \a 响铃(BEL) 007
    \b 退格(BS) ,将当前位置移到前一列 008
    \f 换页(FF),将当前位置移到下页开头 012
    \n 换行(LF) ,将当前位置移到下一行开头 010
    \r 回车(CR) ,将当前位置移到本行开头 013
    \t 水平制表(HT) (跳到下一个TAB位置) 009
    \v 垂直制表(VT) 011
    \ 代表一个反斜线字符''' 092
    ' 代表一个单引号(撇号)字符 039
    " 代表一个双引号字符 034
    \0 空字符(NULL) 000
    \ddd 1到3位八进制数所代表的任意字符 三位八进制
    \xhh 1到2位十六进制所代表的任意字符 二位十六进制

    8、常用的字符串函数

    Lua 提供了很多的方法来支持字符串的操作:

    序号 方法 & 用途
    1 string.upper(argument): 字符串全部转为大写字母。
    2 string.lower(argument): 字符串全部转为小写字母。
    3 string.gsub(mainString,findString,replaceString,num)在字符串中替换。mainString 为要操作的字符串, findString 为被替换的字符,replaceString 要替换的字符,num 替换次数(可以忽略,则全部替换),如:> string.gsub("aaaa","a","z",3); zzza 3
    4 string.find (str, substr, [init, [plain]]) 在一个指定的目标字符串 str 中搜索指定的内容 substr,如果找到了一个匹配的子串,就会返回这个子串的起始索引和结束索引,不存在则返回 nil。init 指定了搜索的起始位置,默认为 1,可以一个负数,表示从后往前数的字符个数。plain 表示是否使用简单模式,默认为 false,true 只做简单的查找子串的操作,false 表示使用使用正则模式匹配。以下实例查找字符串 "Lua" 的起始索引和结束索引位置:> string.find("Hello Lua user", "Lua", 1) 7 9
    5 string.reverse(arg) 字符串反转> string.reverse("Lua") auL
    6 string.format(...) 返回一个类似printf的格式化字符串> string.format("the value is:%d",4) the value is:4
    7 string.char(arg) 和 string.byte(arg[,int]) char 将整型数字转成字符并连接, byte 转换字符为整数值(可以指定某个字符,默认第一个字符)。> string.char(97,98,99,100) abcd > string.byte("ABCD",4) 68 > string.byte("ABCD") 65 >
    8 string.len(arg) 计算字符串长度。string.len("abc") 3
    9 string.rep(string, n) 返回字符串string的n个拷贝> string.rep("abcd",2) abcdabcd
    10 .. 链接两个字符串> print("www.runoob.".."com") www.runoob.com
    11 string.gmatch(str, pattern) 返回一个迭代器函数,每一次调用这个函数,返回一个在字符串 str 找到的下一个符合 pattern 描述的子串。如果参数 pattern 描述的字符串没有找到,迭代函数返回nil。> for word in string.gmatch("Hello Lua user", "%a+") do print(word) end Hello Lua user
    12 string.match(str, pattern, init) string.match()只寻找源字串str中的第一个配对. 参数init可选, 指定搜寻过程的起点, 默认为1。 在成功配对时, 函数将返回配对表达式中的所有捕获结果; 如果没有设置捕获标记, 则返回整个配对字符串. 当没有成功的配对时, 返回nil。> = string.match("I have 2 questions for you.", "%d+ %a+") 2 questions > = string.format("%d, %q", string.match("I have 2 questions for you.", "(%d+) (%a+)")) 2, "questions"

    字符串替换

    string.gsub(mainString,findString,replaceString,num)在字符串中替换。

    • mainString 为要操作的字符串, findString 为被替换的字符,replaceString 要替换的字符,num 替换次数(可以忽略,则全部替换)
    string.gsub("aaaa","a","z",3)
    
    • 结果:zzza 3

    字符串反转

    string.reverse(arg) 字符串反转

    string.reverse("Lua") 
    > auL
    

    字符串长度

    string.len(arg)

    字符串拷贝

    string.rep(string, n)
    返回字符串string的n个拷贝

    > string.rep("abcd",2)
    abcdabcd
    

    字符串拼接,使用的是 ..

    > print("www.runoob.".."com")
    www.runoob.com
    

    字符串截取

    string.sub(s, i [, j]) 用于截取字符串,原型为:

    参数说明:

    • s:要截取的字符串。
    • i:截取开始位置。
    • j:截取结束位置,默认为 -1,最后一个字符。
    print(string.sub("abcdef", 1, 3))
    > abc
    

    字符串查找

    (1) string.find (str, substr, [init, [plain]])

    在一个指定的目标字符串 str 中搜索指定的内容 substr,如果找到了一个匹配的子串,就会返回这个子串的起始索引和结束索引,不存在则返回 nil。

    init 指定了搜索的起始位置,默认为 1,可以一个负数,表示从后往前数的字符个数。

    plain 表示是否使用简单模式,默认为 false,true 只做简单的查找子串的操作,false 表示使用使用正则模式匹配。

    以下实例查找字符串 "Lua" 的起始索引和结束索引位置:

    > string.find("Hello Lua user", "Lua", 1) 
    7    9
    

    (2) string.gmatch(str, pattern)

    返回一个迭代器函数,每一次调用这个函数,返回一个在字符串 str 找到的下一个符合 pattern 描述的子串。如果参数 pattern 描述的字符串没有找到,迭代函数返回nil。

    > for word in string.gmatch("Hello Lua user", "%a+") do print(word) end
    Hello
    Lua
    user
    

    (3) string.match(str, pattern, init)

    string.match()只寻找源字串str中的第一个配对. 参数init可选, 指定搜寻过程的起点, 默认为1。
    在成功配对时, 函数将返回配对表达式中的所有捕获结果; 如果没有设置捕获标记, 则返回整个配对字符串. 当没有成功的配对时, 返回nil。

    > = string.match("I have 2 questions for you.", "%d+ %a+")
    2 questions
    

    区别:使用format 格式化一下

    > = string.format("%d, %q", string.match("I have 2 questions for you.", "(%d+) (%a+)"))
    2, "questions"
    

    9、正则匹配规则

    和java 不一样,可以到时候,使用lua 正则在线规则,检查是否正确

    字符	含义
    %a	字母a-z,A-Z
    %b	%bxy,以x和y进行成对匹配
    %c	控制字符ASCII码 十进制转义表示为\0 - \31
    %d	数字 0 - 9
    %f	%f[char-set],边界匹配,前一个不在范围内,后一个在
    %g	除了空格以外的可打印字符 十进制转义表示为\33 - \126
    %l	小写字母 a - z
    %u	大写字母 A - Z
    %s	空格 十进制转义表示为\32
    %p	标点符号,即!@#$%^&*()`~-_=+{}:"<>?[];',./| 32个字符
    %w	字母数字 a - z, A - Z, 0 - 9
    %x	十六进制符号 0 - 9, a - f, A - F
    

    模糊匹配的其他匹配情况[单个字符(除 ^$()%.[]*+-? 外): 与该字符自身配对]:https://www.runoob.com/lua/lua-strings.html

    10、字符串格式化

    Lua 提供了 string.format() 函数,以下是它的一些转义码:

    %c - 接受一个数字, 并将其转化为ASCII码表中对应的字符
    %d, %i - 接受一个数字并将其转化为有符号的整数格式
    %o - 接受一个数字并将其转化为八进制数格式
    %u - 接受一个数字并将其转化为无符号整数格式
    %x - 接受一个数字并将其转化为十六进制数格式, 使用小写字母
    %X - 接受一个数字并将其转化为十六进制数格式, 使用大写字母
    %e - 接受一个数字并将其转化为科学记数法格式, 使用小写字母e
    %E - 接受一个数字并将其转化为科学记数法格式, 使用大写字母E
    %f - 接受一个数字并将其转化为浮点数格式
    %g(%G) - 接受一个数字并将其转化为%e(%E, 对应%G)及%f中较短的一种格式
    %q - 接受一个字符串并将其转化为可安全被Lua编译器读入的格式
    %s - 接受一个字符串并按照给定的参数格式化该字符串
    

    11、字符与整数相互转换

    string.byte(字符串) 转换第一个字符

    string.char(数字) 转换为字符


    12、Lua特性:自动内存管理;只提供了一种通用类型的表(table),用它可以实现数组,哈希表,集合,对象、字典,并且table是变长变量

    table 变量注意事项:

    table 的索引不可以是数字,也不可以是字符串,会报错,只能是普通变量

    > tbl ={100 = "100"}
    stdin:1: '}' expected near '='
    > tbl ={"100" = "100"}
    stdin:1: '}' expected near '='
    
    -- 正确写法:
    > tbl = {a = "aa"}
    > print(tbl["a"])
    aa
    > print(tbl.a)
    aa
    

    (1) 数组

    array = {}
    
    for i= -2, 2 do
       array[i] = i *2
    end
    
    for i = -2,2 do
       print(array[i])
    end
    
    • 结果:

      -4
      -2
      0
      2
      4


    (2) Lua 迭代器

    ☺ 泛型迭代

    pairs 和 ipairs异同

    同:都能进行for 循环遍历

    异:ipairs 仅仅遍历值,按照索引升序遍历,索引中断停止遍历。即不能返回 nil,只能返回数字 0,如果遇到 nil 则退出。所以table 变量中的变量是键值对形式的,它会直接忽略,即ipairs 迭代时会略过非数值的索引。

    ​ pairs 能遍历集合的所有元素。即 pairs 可以遍历集合中所有的 key,并且除了迭代器本身以及遍历表本身还可以返回 nil。

    注意:下面两个例子的结果的顺序、特点

    tab = {"Hello","World",a=1,b=2,z=3,x=10,y=20,"Good","Bye"}
    for k,v in pairs(tab) do
        print(k.."  "..v)
    end
    
    • 结果:

    1 Hello
    2 World
    3 Good
    4 Bye
    a 1
    x 10
    b 2
    y 20
    z 3

    tab = {"Hello","World",a=1,b=2,z=3,x=10,y=20,"Good","Bye"}
    for k,v in ipairs(tab) do
        print(k.."  "..v)
    end
    
    • 结果:

    1 Hello
    2 World
    3 Good
    4 Bye

    如上代码输出结果存在一定规律,"Hello"、"World"、"Good"、"Bye"是表中的值,在存储时是按照顺序存储的,并且不同于其他脚本语言,Lua是从1开始排序的,因此,使用pairs遍历打印输出时,会先按照顺序输出表的值,然后再按照键值对的键的哈希值打印。


    ☺ 泛型迭代的例子2:

    for k, v in pairs(t) do
        print(k, v)
    end
    
    
    -------------------------------------------
    array = {"Google", "Runoob"}
    
    for key,value in ipairs(array)
    do
       print(key, value)
    end
    
    • 结果:

    1 Google
    2 Runoob


    ☺ 泛型 for 在迭代的时候每次调用的是闭包函数,迭代函数只是开始的时候调用一次

    function eleiter(t)
        local index = 0
        print('in eleiter function')  --> 每次调用迭代函数都说一句:in eleiter function
        return function()
            print('I am here.')  --> 每次调用闭包函数都说一句:I am here
            index = index + 1
            return t[index]
        end
    end
    
    t = {'one','two','three','four','five'}
    for ele in eleiter(t) do
        print(ele)
    end
    
    • 结果:

    in eleiter function --> 【for 迭代函数内部包含闭包函数的情况,对于函数非闭包内容,只执行一次

    --> 泛型 for 在迭代的时候每次调用的是闭包函数

    I am here.
    one
    I am here.
    two
    I am here.
    three
    I am here.
    four
    I am here.
    five
    I am here.


    (2) 表、字典、对象....

    在lua使用 table 表示了数组、表、字典、对象

    table 特点:

    Lua table 使用关联型数组,你可以用任意类型的值来作数组的索引,但这个值不能是 nil。

    Lua table 是不固定大小的,你可以根据自己需要进行扩容。

    Lua 的table 变量是引用型变量,即地址指向。

    tab = {"Hello","World",a=1,b=2,z=3,x=10,y=20,"Good","Bye"}
    tab[1] = "W两个世界"
    for k,v in ipairs(tab) do
        print(k.."  "..v)
    end
    
    • 结果:

    1 W两个世界
    2 World
    3 Good
    4 Bye


    Lua 的table 变量是引用型变量,即地址指向。

    -- 简单的 table
    mytable = {}
    print("mytable 的类型是 ",type(mytable))
    
    mytable[1]= "Lua"
    mytable["wow"] = "修改前"
    print("mytable 索引为 1 的元素是 ", mytable[1])
    print("mytable 索引为 wow 的元素是 ", mytable["wow"])
    
    -- alternatetable和mytable的是指同一个 table
    alternatetable = mytable
    
    print("alternatetable 索引为 1 的元素是 ", alternatetable[1])
    print("mytable 索引为 wow 的元素是 ", alternatetable["wow"])
    
    alternatetable["wow"] = "修改后"
    
    print("mytable 索引为 wow 的元素是 ", mytable["wow"])
    
    -- 释放变量
    alternatetable = nil
    print("alternatetable 是 ", alternatetable)
    
    -- mytable 仍然可以访问
    print("mytable 索引为 wow 的元素是 ", mytable["wow"])
    
    mytable = nil
    print("mytable 是 ", mytable)
    
    • 结果:

    mytable 的类型是 table
    mytable 索引为 1 的元素是 Lua
    mytable 索引为 wow 的元素是 修改前
    alternatetable 索引为 1 的元素是 Lua
    mytable 索引为 wow 的元素是 修改前
    mytable 索引为 wow 的元素是 修改后
    alternatetable 是 nil
    mytable 索引为 wow 的元素是 修改后
    mytable 是 nil


    (3) table 的增删改查操作

    序号 方法 & 用途
    1 table.concat (table [, sep [, start [, end]]]):concat是concatenate(连锁, 连接)的缩写. table.concat()函数列出参数中指定table的数组部分从start位置到end位置的所有元素, 元素间以指定的分隔符(sep)隔开。
    2 table.insert (table, [pos,] value):在table的数组部分指定位置(pos)插入值为value的一个元素. pos参数可选, 默认为数组部分末尾.
    3 table.maxn (table)指定table中所有正数key值中最大的key值. 如果不存在key值为正数的元素, 则返回0。(Lua5.2之后该方法已经不存在了,本文使用了自定义函数实现)
    4 table.remove (table [, pos])返回table数组部分位于pos位置的元素. 其后的元素会被前移. pos参数可选, 默认为table长度, 即从最后一个元素删起。
    5 table.sort (table [, comp])对给定的table进行升序排序。

    13、协同程序 coroutine

    (1) 基本语法

    方法 描述
    coroutine.create() 创建 coroutine,返回 coroutine, 参数是一个函数,当和 resume 配合使用的时候就唤醒函数调用
    coroutine.resume() 重启 coroutine,和 create 配合使用
    coroutine.yield() 挂起 coroutine,将 coroutine 设置为挂起状态,这个和 resume 配合使用能有很多有用的效果
    coroutine.status() 查看 coroutine 的状态 注:coroutine 的状态有三种:dead,suspended,running,具体什么时候有这样的状态请参考下面的程序
    coroutine.wrap() 创建 coroutine,返回一个函数,一旦你调用这个函数,就进入 coroutine,和 create 功能重复
    coroutine.running() 返回正在跑的 coroutine,一个 coroutine 就是一个线程,当使用running的时候,就是返回一个 coroutine 的线程号

    create一个coroutine的时候就是在新线程中注册了一个事件

    当使用resume触发事件的时候,create的coroutine函数就被执行了,当遇到yield的时候就代表挂起当前线程,等候再次resume触发事件。

    可以把 yield 当成暂时,点击resume 当成开始,并且具有“断点续传” 作用

    -- 注册一个事件
    co = coroutine.create(
        function(i)
    		print(coroutine.status(co)) 
            -- 挂起/暂停
    		coroutine.yield()
            print(i);
        end
    )
     
    -- 执行事件
    coroutine.resume(co, 1)   
    print(coroutine.status(co)) 
    coroutine.resume(co, 3) -- 重新开始,相当于“断点续传”,因为这时候传递参数已经是3,但在原先情况上继续执行,会执行coroutine.resume(co, 1) 的参数1
    
    • 结果:

    running
    suspended

    1


    (2) coroutine.creat方法和coroutine.wrap 的区别

    • 返回值不同:coroutine.creat返回的是一个协同程序,类型为thread,需要使用coroutine.resume进行调用;而coroutine.wrap返回的是一个普通的方法(函数),类型为function,和普通function有同样的使用方法,并且不能使用coroutine.resume进行调用。
    co_creat = coroutine.create(
        function()
            print("co_creat类型是"..type(co_creat))
        end
    )
    
    co_wrap = coroutine.wrap(
        function()
            print("co_wrap类型是"..type(co_wrap))
        end
    )
    
    coroutine.resume(co_creat)
    co_wrap()
    
    • 结果:

    co_creat类型是thread
    co_wrap类型是function


    14、创建对象

    Lua 中使用":"实现面向对象方式的调用。":"只是语法糖,它同时在方法的声明与实现中增加了一个名为 self 的隐藏参数,这个参数就是对象本身

    - --实例:
    Account = {balance = 0};
    
    --生成对象
    function Account:new(o)
        o = o or {}; --如果用户没有提供对象,则创建一个。
        setmetatable(o, self); --将 Account 作为新创建的对象元表
        self.__index = self; --将新对象元表的 __index 指向为 Account(这样新对象就可以通过索引来访问 Account 的值了)
        
        return o; --将新对象返回
    end
    
    --存款
    function Account:deposit(v)
        self.balance = self.balance + v;
    end
    
    --取款
    function Account:withdraw(v)
        self.balance = self.balance - v;
    end
    
    --查询
    function Account:demand()
        print(self.balance);
    end
    
    --创建对象
    myAccount = Account:new();
    --通过索引访问
    print(myAccount.balance);
    --调用函数
    myAccount:deposit(100);
    myAccount:withdraw(50);
    myAccount:demand();
    
    • 结果:

      0
      50



    15、Lua 定义或调用方法时的语法糖-冒号,表示参数self

    这个语法糖是用冒号,表示self,相当于java中的this

    ■ 举例1:

    --定义
    Account = { balance = 0 }
    --withdraw 方法有两个参数,一个self【相当于java中的this】是指向当前table的Account 
    function Account.withdraw(self, v)
             self.balance = self.balance - v
    end
    --等价写法:
    function Account:withdraw(v) --通过冒号,表示定义了第一个参数是self
             self.balance = self.balance - v
    end
    
    --调用
    Account.withdraw(self, 100)
    --等价写法
    Account:withdraw(100)        
    

    ■ 举例2:

    -- 在table的键值对的value---是function的时候,方法的参数是self,并且还将self 参数传递给function方法体的另外一个方法
    -- {} 在lua中表示table
    tbWnd.tbOnClick = {
        btnOk = function(self)
            self:onClickOK() -- 相当于onClickOK(self)
        end,
    }
    



    16、获取table 这个关联数组,使用# 有时候不一定准确

    • 推荐自定义一个计算数组长度方法,即使用pairs 遍历一下
    function CountTB(tbVar)
    	local nCount = 0
    	for _, _ in pairs(tbVar) do
    		nCount	= nCount + 1
    	end
    	return nCount
    end
    
    • 获取table 这个关联数组长度,使用# 有时候不一定准确

    • 推荐使用自定义的CountTB 方法




    如果本文对你有帮助的话记得给一乐点个赞哦,感谢!

  • 相关阅读:
    LeetCode_Array_1004. Max Consecutive Ones III 最大连续1的个数 III【滑动数组】【Java】【中等】
    关于汽车html网页设计完整版,10个以汽车为主题的网页设计与实现
    【牛客 - 剑指offer】JZ4 二维数组中的查找 Java实现
    如何给鸿蒙 APP 签名
    基于SNN脉冲神经网络的Hebbian学习训练过程matlab仿真
    实验1: 交换机MAC地址表学习过程实验
    pytest-基础
    关于“兆易创新杯”中国研究生电子设计竞赛的一点个人小经验
    Kubernetes(k8s)高可用搭建
    闲来无事-树莓派控制风扇启停
  • 原文地址:https://www.cnblogs.com/shan333/p/17288018.html