Lua 编程学习笔记
文章目录
- Lua 编程学习笔记
- 一、环境安装
- 二、Lua 基本语法
- 1. 注释
- 2. 标识符
- 3. 变量
- 4. 数据类型
- 5. Lua 运算符
- 三、循环与流程控制
- 1. 循环
- 2. 流程控制
- 四、函数
- 1. 基本定义
- 2. 可变参数
- 3. 函数作为参数传递
- 五、表与数组
- 1. 基本定义
- 2. 表操作的常用方法
- 2.1 插入数据
- 2.2 删除数据
- 2.3 拼接表数据
- 2.4 排序
- 六、模块与包
- 1. 模块定义
- 2. 包的引入
- 七、元表
- 1. 基本定义
- 2. 元方法
- 2.1 __index 元方法
- 总结
- 2.2 __newIndex 元方法
- 2.3 __tostring 元方法
- 八、协同程序
- 1. 常用方法:
- 2. 经典生产消费问题
- 九、文件IO
- 1. 简单模式
- 2. 完全模式
- 十、面向对象
- 十一、饥荒源码Lua学习
Lua 编程学习笔记
Lua编程语言给人的感觉是小巧,简洁,解释性语言,弱类型,主要用于游戏开发,嵌入式脚本开发。
此次学习源于写饥荒脚本,用饥荒学习Lua绝对是不错的一个实战。
一、环境安装
首先在官网下载
Lua下载是一个压缩文件,解压即可用,但是需要先编译一下。
默认是没有可执行程序的,需要使用命令制作一下。
在当前src
目录下打开终端窗口,执行命令
make
make
之后生成lua
和luac
两个可执行程序
这样我们就可以用Lua
这个可执行程序执行我们编写的.lua文件了。
我使用的是IDE是Idea,Idea需要编写Lua需要下载一个插件:EmmyLua
使用这个插件可以高亮显示还有方法提示,可以Tab键自动补全。
安装后运行Lua文件的需要配置文件的解释程序。
这样整体的开发环境就搭配好了。
二、Lua 基本语法
1. 注释
单行注释
-- Lua 的单行注释使用 `--`
print("Hello Lua")
多行注释
--[[多行注释1多行注释2多行注释3
]]
print("Hello Lua")
2. 标识符
和其他编程语言一样,Lua的标识符由字母、数字以及下划线组成,且不能以数字开头,不能是关键字
比如:__index
3. 变量
Lua作为弱类型语言,变量的定义不需要声明数据类型。
-- 整数变量 a
a = 1-- 字符串变量 a
a = "Test"-- 布尔类型 a
a = false
变量的作用域默认是全局,只有声明为local
的变量才是局部变量
弱类型语言的变量使用很随意,如果变量的数据类型使用错误,只有在运行时才能发现,体现的是规范大于约定的思想。
4. 数据类型
虽然变量的定义不需要声明数据类型,但是Lua大体上还是分为8种数据类型
- nil
- number
- string
- boolean
- function
- table
- userdata
- thread
nil代表空值,被赋值为nil的变量将清空,userdata和thread目前还没接触,其他类型只有nil和table的概念比较特殊。
注:Lua中的布尔值只有nil和false为条件表达式的否判断。
-- number类型也是真值
a = 1
if a thenprint("a是ture")
end -- 函数类型也是真值
function fn() print("fn函数")
end
if fn thenprint("fn函数只要不为nil也是真值")
end
5. Lua 运算符
1)算术运算符
+
加法-
减法*
乘法/
除法(真实运算,不会取整数,Java里边10/3=3,这里10/3=3.3333333333333)%
求余^
幂运算( 2^3=8.0)-
负号,对数字前面直接加取负数//
整数运算,这个才是同Java的除法运算,10/3=3
2)逻辑运算符
- and 逻辑与运算
- or 逻辑或运算
- not 逻辑非运算
3)关系运算符
>
<
==
>=
<=
~=
注意Lua的不等于写法~=
这个还是挺新鲜的写法。
4)其他运算符
Lua 没有类似++
, --
的操作,循环时候一定要注意!
..
拼接字符串,同Java的 + 拼接字符串#
返回字符串或者表的长度
print(#"Hello") -- 输出 5tab = {1,2,3}
print(tab) -- 输出3
三、循环与流程控制
Lua循环和其他语言类似,只是需要注意基本的格式。
1. 循环
三种循环方式
- while
- for
- repeat … until
-- 方式一 while 循环
a = 0
while a < 10 doprint("while循环"..a)a = a + 1
end-- 方式二 for 循环
for i = 1, 10 doprint("for循环"..i)
end-- 方式三 repeat 循环
c = 0
repeatprint("repeat循环"..c)c = c + 1
until c > 10
repeat … until 循环好比是Java中的 do…while循环,至少执行一次。
2. 流程控制
-- if 结构
if a>b thenreturn a
end-- if elseif else 结构
if a > 90 thenprint("优秀")
elseif a > 80 thenprint("良好")
elseif a > 60 thenprint("及格")
elseprint("不及格")
end
循环和流程控制都没有特殊的,需要注意的是基本格式,Lua里边无论函数还是循环控制,都是以end结尾
四、函数
Lua函数比较特殊,第一次遇到可以返回多个值。
1. 基本定义
-- 方式一
function f1()-- do something
end-- 方式二
local f2 = function(arg1,arg2,arg3) -- do somethingreturn arg1,arg2,arg3
end
Lua的函数修饰符只有local
定义是否为局部函数,参数可选。主要是可以类似函数f2,可以返回多个数据。
接收函数返回值的时候也可以多个变量一一对应接收。
function f3()return 1,2,3
enda,b,c = f3()
print(a,b,c) -- 输出 1,2,3
2. 可变参数
函数接收可变参数有个内置变量arg这个需要主要,他是一个表table
-- `...` 代表可变参数
function f4(...)local arg = {...}-- 遍历方式一for k, v in pairs(arg) doprint(k,v)end-- 遍历方式二for k, v in ipairs{...} doprint(k,v)endprint(#arg) -- 定义 local = {...} 可通过 #arg 获取参数个数print(select("#",...)) -- select("#",...) 获取可变参数个数
endf4(1,2,3,4) -- 最后打印 4-- 函数作为参数传递function f5(func)local ret = funcend
注:可变参数的获取方式和数据获取方式是重点
另外,ipairs和pairs的主要区别是ipairs遇到nil则会停下来,而pairs遍历不会。
for k,v in pairs(table) do … end 这是一种基本固定的遍历表的写法,类似Java中的增强for循环。
3. 函数作为参数传递
函数作为参数传递给下个函数时,函数内可以调用这个函数做一些数据处理
function max(a,b)if a > b thenreturn aelsereturn bend
endfunction printMax(a,b,maxFunc)print(maxFunc(a,b))
endprintMax(2,3,max)
五、表与数组
表是Lua中最常用的数据结构了,基本上数据的使用都是通过表,但我理解更像是一个Map容器,存储一些key,value的键值对。
1. 基本定义
-- 数组没有单独的表示方式,数组就是用表表示的,只不过没有key,默认key就是1,2,3...连续索引
-- 一对花括号就代表这是一个表,前面的数据类型有一个就是table表数据类型
tab = {}-- 也可以初始化(数组)
fruits = {"apple","peach","banana"}-- 这个更应该被认为是表,以指定的key-value形式存在
table_define = {key1 = "val",key2 = "val2",key3 = "val3"
}-- 或者给表增加数据
tab["key"] = "value"-- 赋值 nil 就清空了一个表
tab = nil-- 表的引用赋值给另一个
newFruits = fruits
fruits = nil -- newFruits 指向的表实际还在,这里只是清空了fruits的引用而已
2. 表操作的常用方法
注:Lua中的索引都是从1开始的,而不是0!
2.1 插入数据
tab = {}
-- 默认插入表的末尾
table.insert(tab,"value1")
print(tab[1])-- 插入指定索引位置
table.insert(tab,1,"value2")print(tab[1])
2.2 删除数据
fruits = {"apple", "peach", "banana", "orange"}fruits[1] = nil
print(fruits[1]) -- niltable.remove(fruits,2)
print(fruits[2]) -- banana-- 默认移除表最后的数据
table.remove(fruits)
for k,v in pairs(fruits) doprint(v) -- banana
end
2.3 拼接表数据
tab = {"Lua", "C#", "Java"}-- 拼接数据元素
print(table.concat(tab)) -- LuaC#Java-- 指定拼接的分隔符
print(table.concat(tab," ")) -- Lua C# Java-- 指定分隔符和指定索引范围内数据拼接
print(table.concat(tab," ",2,3)) -- C# Java
2.4 排序
tab = {"Lua", "C#", "Java"}-- 调用table的排序方法
table.sort(tab)
六、模块与包
这里才是最接近编程开发的概念了,将代码进行模块化管理,但是Lua并没有真正的包Module的概念,包也是通过表来实现的。
1. 模块定义
Module = {}Module.name = "模块包"Module.func = function() print("文件module.lua中的函数func")
end-- 可以通过local封装,只能通过方法调用
local address = "SZ"
Module.getAddress = function()return address
endlocal func_local = function()print("local函数")
endModule.getLocalFunc = function()func_local()
end
2. 包的引入
包的引入使用关键字require
-- 两种写法,之类的module,是文件名,module.lua这个文件就被引入进来了
require "module"require("module")-- 引入包之后就可以访问包的内容了
print(Module.name)
Module.func()print(Module.getAddress())
print(Module.getLocalFunc())
七、元表
元表理解更像是定义元
那样,更微小的单元,他允许我们去定义一个表之外的行为,除了赠删改之外的动作。比如两个表进行相加。
1. 基本定义
myTable = {} -- 普通表
myMetaTable = {} -- 元表
-- 会返回普通表
myTable = setmetatable(myTable,myMetaTable) -- 将myMetaTable设置为myTable的元表getmetatable(myTable) -- 返回 myMetaTable 元表
2. 元方法
2.1 __index 元方法
当查找普通表中的key对应值为没有时,则去调用此方法寻找对应的表中的数据。
理解更像是对普通表的一种扩展。
other = {key = 3}
t = setmetatable({},{__index = other}) -- 普通表是空的,元表存在元素 key = 3print(t.key) -- 3
如果__index包含一个函数的话,Lua就会调用那个函数,table和键会作为参数传递给函数。
__index 元方法查看表中元素是否存在,如果不存在,返回结果为 nil;如果存在则由 __index 返回结果。
mytable = setmetatable({ key1 = "value1" } , { __index = function(mytable,key)if key == "key2" thenreturn "metatablevalue"elsereturn nilendend
})
print(mytable.key1,mytable.key2)
总结
来源菜鸟教程
Lua 查找一个表元素时的规则,其实就是如下 3 个步骤:
- 在表中查找,如果找到,返回该元素,找不到则继续
- 判断该表是否有元表,如果没有元表,返回 nil,有元表则继续。
- 判断元表有没有 __index 方法,如果 __index 方法为 nil,则返回 nil;
- 如果 __index 方法是一个表,则重复 1、2、3;
- 如果 __index 方法是一个函数,则返回该函数的返回值。
2.2 __newIndex 元方法
__newIndex针对不存在的key进行赋值时候调用
mytable = setmetatable({key1 = "value1"}, {__newindex = function(mytable, key, value)rawset(mytable, key, "\""..value.."\"")end
})mytable.key1 = "new value"
mytable.key2 = 4print(mytable.key1,mytable.key2)
2.3 __tostring 元方法
__tostring使得print(table)时候可以输出字符串而不是表的引用
mytable = setmetatable({ 10, 20, 30 }, {__tostring = function(mytable)sum = 0for k, v in pairs(mytable) dosum = sum + vendreturn "表所有元素的和为 " .. sumend
})
print(mytable)
八、协同程序
协同程序实际上就是协同函数,让函数可以再执行到一半的时候挂起,然后再继续执行下面的代码,知道执行启动该协同函数才会继续执行协同函数肚饿代码。
1. 常用方法:
- coroutine.create(func) 创建一个协同函数,参数为函数
- coroutine.resume() 启动创建的协同函数,搭配coroutine.create()
- coroutine.yield() 暂停协同函数,参数可以作为函数的返回值,同return效果
- coroutine.status() 查看协同函数的状态,存在三种:suspended,dead,running分别为暂停,死亡,运行
- coroutine.wrap() 同create+resume,创建并启动,停止后好像没法继续启动
- coroutine.running() 返回正在跑的coroutine,一个正在跑的coroutine就是一个线程
2. 经典生产消费问题
local co-- 生产者每生产一个就提供给消费者
function productor()local i = 0while true doi = i + 1print("生产者生产\t"..i)sleep(1)coroutine.yield(i)endprint("无产品生产,生产者结束")
endfunction consumer()while coroutine.status(co) ~= "dead" doprint("消费者唤醒生产")local status, value = coroutine.resume(co)print("消费者消费\t"..value)sleep(1)endprint("消费者得知生产结束,消费结束")
endfunction sleep(n)local t = os.clock()while os.clock() -t <=n do end
end-- 创建生产者协同函数
co = coroutine.create(productor)
-- 开始消费
consumer()
注:Lua语言没有原生的线程睡眠函数,这里定义的也不是很懂,大概理解是os.clock返回程序运行时长,用时间差判断得到睡眠时间。
九、文件IO
文件IO分为简单模式和完全模式,文件打开针对权限分为:
- r 只读,文件必须存在
- w 只写,以覆盖的方式写入
- a 只写,以附加的方式写入
- r+ 可读写,文件必须存在
- w+ 可读写,文件存在则覆盖,文件不存在则创建
- a+ 可读写,以附加的方式写入
- b 二进制文件,如果打开的是二进制文件需要加上 b
- 表示可读可写
1. 简单模式
-- 以只读的方式打开一个文件
file = io.open("tmp.txt","r")-- 设置io的输入文件是file
io.input(file)-- 读取一行
line = io.read()-- 关闭文件也要指定文件
io.close(file)
2. 完全模式
-- 以只读的方式打开tmp.txt文件
file = io.open("tmp.txt", "r")-- 直接读取一行,不用io.input(file)指定输入文件了
line = file:read()-- 关闭文件
file:close()
上面的read方法都是默认读取一行数据,可以指定read()函数的参数,代表不同的读取方法
*n
读取一个数字并返回,必须是数字*a
读取整个文件内容*|
读取一行(默认方式)测试时候好像一直报错,未知原因number
举例:file.read(4) 指定读取4个字符
十、面向对象
Lua的面向对象的使用应该才是最终极的形态了。
面向对象逃不开三个特征,封装、继承、多态。
- 封装 有效封装属性,合理对外暴露。
- 继承 对父类属性的扩展,比如存在人这个模型,可以将学生对象继承自父类对象人,使其具备人有的一些属性方法。
- 多态 一种类型的多种形态,按照Java的说法就是父类的引用指向子类的对象,比如数据都是动物,但是可以不同的形态如,猫,狗都属于动物
对象
-- Lua 里边都是表数据,没有对象的概念,所以所谓类,对象都是模拟出来的一种形态-- 定义一个Person类
Person = {name = nil,age = nil
}-- 定义方法
Person.eat = function(self)print(self.name.."在吃东西")
end-- 调用
Person.name = "周杰伦"
Person.age = 42
Person.eat(Person)-- 如果还有一个Person对象,则需要再次重复定义上面的Person,所以可以模拟一个类的构造方法出来
function Person:new(o)local t = o or {}setmetatable(t, {__index = self})return t
end-- eat 方法改进,后面编程都是以这种形式出现的居多
function Person:eat()print(self.name.."在吃东西")
endp1 = Person:new()
p1.name = "黄圣依"
print(p1:eat())
十一、饥荒源码Lua学习
饥荒里边的源码都是Lua文件,采用ECS(Entity, Components, System)框架,即实体、组建、系统。
这种框架和面向对象不同,他更多的是面向数据;
实体理解就是对象,一个一个的实物,比如游戏里边的花、猪人,兔子等,而组件则是行为,比如花存在可采集的属性,所以Pickable.lua文件存在组件中代码。
而系统则是控制整个游戏的机制玩法。系统不关心组件存在什么行为,组件不关系实体具体是什么。
饥荒中类的定义
class.lua文件–基本上看完感觉上面的Lua都白学了,吐了。
-- class.lua
-- Compatible with Lua 5.1 (not 5.0).local TrackClassInstances = falseClassRegistry = {}if TrackClassInstances == true thenglobal("ClassTrackingTable")global("ClassTrackingInterval")ClassTrackingInterval = 100
endlocal function __index(t, k)local p = rawget(t, "_")[k]if p ~= nil thenreturn p[1]endreturn getmetatable(t)[k]
endlocal function __newindex(t, k, v)local p = rawget(t, "_")[k]if p == nil thenrawset(t, k, v)elselocal old = p[1]p[1] = vp[2](t, v, old)end
endlocal function __dummy()
endlocal function onreadonly(t, v, old)assert(v == old, "Cannot change read only property")
endfunction makereadonly(t, k)local _ = rawget(t, "_")assert(_ ~= nil, "Class does not support read only properties")local p = _[k]if p == nil then_[k] = { t[k], onreadonly }rawset(t, k, nil)elsep[2] = onreadonlyend
endfunction addsetter(t, k, fn)local _ = rawget(t, "_")assert(_ ~= nil, "Class does not support property setters")local p = _[k]if p == nil then_[k] = { t[k], fn }rawset(t, k, nil)elsep[2] = fnend
endfunction removesetter(t, k)local _ = rawget(t, "_")if _ ~= nil and _[k] ~= nil thenrawset(t, k, _[k][1])_[k] = nilend
endfunction Class(base, _ctor, props)local c = {} -- a new class instancelocal c_inherited = {}if not _ctor and type(base) == 'function' then_ctor = basebase = nilelseif type(base) == 'table' then-- our new class is a shallow copy of the base class!-- while at it also store our inherited members so we can get rid of them-- while monkey patching for the hot reload-- if our class redefined a function peronally the function pointed to by our member is not the in in our inherited-- tablefor i,v in pairs(base) doc[i] = vc_inherited[i] = vendc._base = baseend-- the class will be the metatable for all its objects,-- and they will look up their methods in it.if props ~= nil thenc.__index = __indexc.__newindex = __newindexelsec.__index = cend-- expose a constructor which can be called by <classname>(<args>)local mt = {}if TrackClassInstances == true and CWD~=nil thenif ClassTrackingTable == nil thenClassTrackingTable = {}endClassTrackingTable[mt] = {}local dataroot = "@"..CWD.."\\"local tablemt = {}setmetatable(ClassTrackingTable[mt], tablemt)tablemt.__mode = "k" -- now the instancetracker has weak keyslocal source = "**unknown**"if _ctor then-- what is the file this ctor was created in?local info = debug.getinfo(_ctor, "S")-- strip the drive letter-- convert / to \\source = info.sourcesource = string.gsub(source, "/", "\\")source = string.gsub(source, dataroot, "")local path = sourcelocal file = io.open(path, "r")if file ~= nil thenlocal count = 1for i in file:lines() doif count == info.linedefined thensource = i-- okay, this line is a class definition-- so it's [local] name = Class etc-- take everything before the =local equalsPos = string.find(source,"=")if equalsPos thensource = string.sub(source,1,equalsPos-1)end-- remove trailing and leading whitespacesource = source:gsub("^%s*(.-)%s*$", "%1")-- do we start with local? if so, strip itif string.find(source,"local ") ~= nil thensource = string.sub(source,7)end-- trim again, because there may be multiple spacessource = source:gsub("^%s*(.-)%s*$", "%1")breakendcount = count + 1endfile:close()endendmt.__call = function(class_tbl, ...)local obj = {}if props ~= nil thenobj._ = { _ = { nil, __dummy } }for k, v in pairs(props) doobj._[k] = { nil, v }endendsetmetatable(obj, c)ClassTrackingTable[mt][obj] = sourceif c._ctor thenc._ctor(obj, ...)endreturn objendelsemt.__call = function(class_tbl, ...)local obj = {}if props ~= nil thenobj._ = { _ = { nil, __dummy } }for k, v in pairs(props) doobj._[k] = { nil, v }endendsetmetatable(obj, c)if c._ctor thenc._ctor(obj, ...)endreturn objendendc._ctor = _ctorc.is_a = function(self, klass)local m = getmetatable(self)while m doif m == klass then return true endm = m._baseendreturn falseendsetmetatable(c, mt)ClassRegistry[c] = c_inherited-- local count = 0-- for i,v in pairs(ClassRegistry) do-- count = count + 1-- end-- if string.split then-- print("ClassRegistry size : "..tostring(count))-- endreturn c
endfunction ReloadedClass(mt)ClassRegistry[mt] = nil
endlocal lastClassTrackingDumpTick = 0function HandleClassInstanceTracking()if TrackClassInstances and CWD~=nil thenlastClassTrackingDumpTick = lastClassTrackingDumpTick + 1if lastClassTrackingDumpTick >= ClassTrackingInterval thencollectgarbage()print("------------------------------------------------------------------------------------------------------------")lastClassTrackingDumpTick = 0if ClassTrackingTable thenlocal sorted = {}local index = 1for i,v in pairs(ClassTrackingTable) dolocal count = 0local first = nilfor j,k in pairs(v) doif count == 1 thenfirst = kendcount = count + 1endif count>1 thensorted[#sorted+1] = {first, count-1}endindex = index + 1end-- get the top 10table.sort(sorted, function(a,b) return a[2] > b[2] end )for i=1,10 dolocal entry = sorted[i]if entry thenprint(tostring(i).." : "..tostring(sorted[i][1]).." - "..tostring(sorted[i][2]))endendprint("------------------------------------------------------------------------------------------------------------")endendend
end
Lua 编程学习笔记相关推荐
- 多线程编程学习笔记——async和await(三)
接上文 多线程编程学习笔记--async和await(一) 接上文 多线程编程学习笔记--async和await(二) 五. 处理异步操作中的异常 本示例学习如何在异步函数中处理异常,学习如何对多 ...
- 多线程编程学习笔记——任务并行库(二)
接上文 多线程编程学习笔记--任务并行库(一) 三. 组合任务 本示例是学习如何设置相互依赖的任务.我们学习如何创建一个任务的子任务,这个子任务必须在父任务执行结束之后,再执行. 1,示例代码如下 ...
- 多线程编程学习笔记——任务并行库(三)
接上文 多线程编程学习笔记--任务并行库(一) 接上文 多线程编程学习笔记--任务并行库(二) 六. 实现取消选项 本示例学习如何实现基于Task的异步操作进行取消流程,以及在任务真正运行前如何知 ...
- Linux与C++11多线程编程(学习笔记)
多线程编程与资源同步 在Windows下,主线程退出后,子线程也会被关闭; 在Linux下,主线程退出后,系统不会关闭子线程,这样就产生了僵尸进程 3.2.1创建线程 Linux 线程的创建 #inc ...
- Cocoa编程学习笔记一
Cocoa编程学习笔记一 一.Cocoa的起源 Mac OS X的窗口服务器与UNIX中的X窗口服务器具有相同的功能:从用户那里接受事件,并将时间转发给应用程序,将应用程序发过来的数据显示在屏幕上.N ...
- 多线程编程学习笔记——使用并发集合(三)
接上文 多线程编程学习笔记--使用并发集合(一) 接上文 多线程编程学习笔记--使用并发集合(二) 四. 使用ConcurrentBag创建一个可扩展的爬虫 本示例在多个独立的即可生产任务又可消费 ...
- Java 8 函数式编程学习笔记
Java 8 函数式编程学习笔记 @(JAVASE)[java8, 函数式编程, lambda] Java 8 函数式编程学习笔记 参考内容 Java 8中重要的函数接口 扩展函数接口 常用的流操作 ...
- java 网络编程学习笔记
java 网络编程学习笔记 C/S模式:客户端和服务器 客户端创建流程 1 1.建立Socket端点 2 3 Socket s = new Socket(绑定地址, 绑定端口); 2.确认源数据方式和 ...
- lua本学习笔记功能
Lua本学习笔记功能 1. 函数返回 指定任务的主要功能是完成,在这种情况下,函数被用作调用语句.函数可以计算并返回值,在这种情况下,作为分配值表达式语句使用. 语法: funcationfunc_ ...
最新文章
- .Net Compact Framework实现文件下载功能
- 如何搭建基于容器的工业互联网PaaS平台
- 操作系统课设--系统调用
- VTK:Filtering之ConnectivityFilter
- cocos2d-x游戏实例(1)-视角跟随主角
- JS判断访问设备(userAgent)加载不同页面 JS判断客户端操作系统类型(platform)
- spring之基本介绍以及老版本框架的下载地址
- 腾讯广告算法大赛已启动,逆向算法,等你来战
- html货币相关符号
- 英雄联盟游戏结束后显示与服务器失去连接,英雄联盟游戏被终止连接不上解决方法...
- Excel基础知识(10):多工作簿数据的引用与更新
- 如何批量隔行删除Excel行
- JTree创建、获取和删除节点的方法
- 各大著名汽车标志图 来历
- app运营,如何提高用户的参与度?
- 按自己的需要获取对象中的属性
- ueditor统计字数中文_百度UEditor修改右下角统计字数包含html样式
- java 蓝牙打印_Android - 将收据打印到蓝牙打印机
- 删除win10桌面上IE的方法
- 《算法零基础100讲》(第42讲) 位运算 (位与) 入门
热门文章
- python实现爬虫探探_全栈 - 9 实战 爬取豆瓣电影数据
- 超强古文...... yi
- OpenCV-Python之画椭圆
- win7连接sftp_WinSCP(SFTP客户端)官方版下载_WinSCP(SFTP客户端) v5.17.7.10640中文版 - Win7旗舰版...
- order by语句使用
- drag与drop事件
- Android App包瘦身优化
- SharePoint服务器端对象模型 之 使用CAML进行数据查询
- 暗黑2符文之语大全_暗黑破坏神2符文之语一览,附符文镶嵌顺序和底材
- 工具--国内最常用开源镜像站大汇总,解决你下载软件慢的问题