lua基础语法

基础语法

最基础的

-- 两个破折号开始一行注释。

--[[
     添加两个 [ 和 ] 可以使其成为
     多行注释。
--]]

----------------------------------------------------
-- 1. 变量和流程控制。
----------------------------------------------------

num = 42  -- 数字可以是整数或浮点数。

s = 'walternate'  -- 不可变字符串类似于 Python。
t = "双引号也可以"
u = [[ 双重括号
       开始和结束
       多行字符串。]]
t = nil  -- 取消定义 t;Lua 有垃圾回收。

-- 块由 do/end 关键字表示:
while num < 50 do
  num = num + 1  -- 没有 ++ 或 += 类型的运算符。
end

-- 如果子句:
if num > 40 then
  print('超过 40')
elseif s ~= 'walternate' then  -- ~= 代表不等于。
  -- 等于检查是 ==,就像 Python;对字符串也可以。
  io.write('不超过 40\n')  -- 默认输出到 stdout。
else
  -- 变量默认是全局的。
  thisIsGlobal = 5  -- 驼峰命名法很常见。

  -- 如何将变量设为局部:
  local line = io.read()  -- 读取下一个标准输入行。

  -- 字符串连接使用 .. 运算符:
  print('冬天来了,' .. line)
end

-- 未定义的变量返回 nil。
-- 这不是错误:
foo = anUnknownVariable  -- 现在 foo = nil。

aBoolValue = false

-- 只有 nil 和 false 是虚假的;0 和 '' 是真的!
if not aBoolValue then print('它是假的') end

-- '或' 和 '与' 是短路运算。
-- 这类似于 C/js 中的 a?b:c 运算符:
ans = aBoolValue and 'yes' or 'no'  --> 'no'

karlSum = 0
for i = 1, 100 do  -- 范围包括两端。
  karlSum = karlSum + i
end

-- 使用 "100, 1, -1" 作为范围进行倒计时:
fredSum = 0
for j = 100, 1, -1 do fredSum = fredSum + j end

-- 一般来说,范围是"开始,结束, 步长"。

-- 另一种循环结构:
repeat
  print('未来之路')
  num = num - 1
until num == 0

函数

function fib(n)
  if n < 2 then return 1 end
  return fib(n - 2) + fib(n - 1)
end

-- 闭包和匿名函数是可以的:
function adder(x)
  -- 返回的函数是在 adder 被调用时创建的,
  -- 并记住 x 的值:
  return function (y) return x + y end
end

-- 返回函数
--[[
...
  return function (y) 
  .... 
  end
...
]]

a1 = adder(9)
a2 = adder(36)
print(a1(16))  --> 25
print(a2(64))  --> 100

-- 返回值、函数调用和赋值都可以处理
-- 可能长度不匹配的列表。
-- 不匹配的接收者是 nil;
-- 不匹配的发送者会被丢弃。

x, y, z = 1, 2, 3, 4
-- 现在 x = 1, y = 2, z = 3,4 被丢弃。

function bar(a, b, c)
  print(a, b, c)
  return 4, 8, 15, 16, 23, 42
end

x, y = bar('zaphod')  --> 打印 "zaphod  nil nil"
-- 现在 x = 4, y = 8,值 15...42 被丢弃。

-- 函数是第一类,可以是局部/全局的。
-- 这些是相同的:
function f(x) return x * x end
f = function (x) return x * x end

-- 这些也是相同的:
local function g(x) return math.sin(x) end
local g; g  = function (x) return math.sin(x) end
-- 'local g' 声明使得 g 自引用是可以的。

-- 顺便说一句,三角函数以弧度为单位。

-- 只有一个字符串参数的调用不需要括号:
print 'hello'  -- 正常工作。

-- 表 = Lua 唯一的复合数据结构;
--          它们是关联数组。
-- 类似于 php 数组或 js 对象,它们是
-- 哈希查找字典,也可以用作列表。

-- 将表用作字典/映射:

-- 字典字面量默认具有字符串键:
t = {key1 = 'value1', key2 = false}

-- 字符串键可以使用类似 js 的点表示法:
print(t.key1)  -- 打印 'value1'。
t.newKey = {}  -- 添加一个新的键/值对。
t.key2 = nil   -- 从表中删除 key2。

-- 任何(非 nil)值作为键的字面量表示法:
u = {['@!#'] = 'qbert', [{}] = 1729, [6.28] = 'tau'}
print(u[6.28])  -- 打印 "tau"

-- 对于数字和字符串,键匹配基本上是按值
-- 而对于表是按标识。
a = u['@!#']  -- 现在 a = 'qbert'。
b = u[{}]     -- 我们可能期望 1729,但它是 nil:
-- b = nil,因为查找失败。查找失败是因为
-- 我们使用的键不是用于存储原始值的同一个对象。因此
-- 字符串和数字是更可移植的键。

-- 一个只有一个表参数的函数调用不需要括号:
function h(x) print(x.key1) end
h{key1 = 'Sonmi~451'}  -- 打印 'Sonmi~451'。

for key, val in pairs(u) do  -- 表迭代。
  print(key, val)
end

-- _G 是一个特殊的所有全局变量的表。
print(_G['_G'] == _G)  -- 打印 'true'。

-- 将表用作列表/数组:

-- 列表字面量隐式设置整数键:
v = {'value1', 'value2', 1.21, '吉瓦'}
for i = 1, #v do  -- #v 是 v 的大小。
  print(v[i])  -- 索引从 1 开始!!真疯狂!
end
-- '列表' 不是一个真实的类型。v 只是一个表
-- 具有连续的整数键,被视为一个列表。

元表和元方法

-- 一个表可以有一个元表,给表
-- 操作重载的行为。稍后我们将看到
-- 元表如何支持 js 原型行为。

f1 = {a = 1, b = 2}  -- 表示分数 a/b。
f2 = {a = 2, b = 3}

-- 这将失败:
-- s = f1 + f2

metafraction = {}
function metafraction.__add(f1, f2)
  sum = {}
  sum.b = f1.b * f2.b
  sum.a = f1.a * f2.b + f2.a * f1.b
  return sum
end

setmetatable(f1, metafraction)
setmetatable(f2, metafraction)

s = f1 + f2  -- 在 f1 的元表上调用 __add(f1, f2)

-- f1 和 f2 没有其元表的键,不像
-- js 中的原型,因此您必须像这样检索它:
-- getmetatable(f1)。元表是一个普通的表
-- 具有 Lua 知道的键,例如 __add。

-- 但下一行失败,因为 s 没有元表:
-- t = s + s
-- 下面给出的类模式可以解决这个问题。

-- 元表上的 __index 重载点查找:
defaultFavs = {animal = 'gru', food = 'donuts'}
myFavs = {food = 'pizza'}
setmetatable(myFavs, {__index = defaultFavs})
eatenBy = myFavs.animal  -- 成功!谢谢,元表

-- 直接的表查找失败将重试使用
-- 元表的 __index 值,这会递归进行。

-- __index 值也可以是一个函数(tbl, key)
-- 用于更自定义的查找。

-- __index,__add 等的值被称为元方法。
-- 完整列表。这里 a 是一个具有元方法的表。

-- __add(a, b)                     用于 a + b
-- __sub(a, b)                     用于 a - b
-- __mul(a, b)                     用于 a * b
-- __div(a, b)                     用于 a / b
-- __mod(a, b)                     用于 a % b
-- __pow(a, b)                     用于 a ^ b
-- __unm(a)                        用于 -a
-- __concat(a, b)                  用于 a .. b
-- __len(a)                        用于 #a
-- __eq(a, b)                      用于 a == b
-- __lt(a, b)                      用于 a < b
-- __le(a, b)                      用于 a <= b
-- __index(a, b)  <fn 或一个表>  用于 a.b
-- __newindex(a, b, c)             用于 a.b = c
-- __call(a, ...)                  用于 a(...)

类似类的表和继承

-- 类不是内置的;有不同的方法
-- 使用表和元表来创建它们。

-- 下面是此示例的解释。

Dog = {}                                   -- 1.

function Dog:new()                         -- 2.
  newObj = {sound = '汪'}                  -- 3.
  self.__index = self                      -- 4.
  return setmetatable(newObj, self)        -- 5.
end

function Dog:makeSound()                   -- 6.
  print('我说 ' .. self.sound)
end

mrDog = Dog:new()                          -- 7.
mrDog:makeSound()  -- '我说汪'             -- 8.

-- 1. Dog 充当类;它实际上是一个表。
-- 2. function tablename:fn(...) 与
--    function tablename.fn(self, ...)
--    相同。: 会添加一个名为 self 的第一个参数。
--    参见下面的 7 和 8 以了解 self 是如何获得其值的。
-- 3. newObj 将是 Dog 类的一个实例。
-- 4. self = 被实例化的类。通常
--    self = Dog,但继承可以改变它。
--    当我们同时将 newObj 的元表和 self 的 __index 设置为 self 时,newObj 将获得 self 的函数。
-- 5. 提醒:setmetatable 返回其第一个参数。
-- 6. : 的用法与第 2 点相同,但这次我们期望
--    self 是一个实例而不是一个类。
-- 7. 与 Dog.new(Dog) 相同,因此 self = Dog 在 new() 中。
-- 8. 与 mrDog.makeSound(mrDog) 相同;self = mrDog。

----------------------------------------------------

-- 继承示例:

LoudDog = Dog:new()                           -- 1.

function LoudDog:makeSound()
  s = self.sound .. ' '                       -- 2.
  print(s .. s .. s)
end

seymour = LoudDog:new()                       -- 3.
seymour:makeSound()  -- '汪汪汪'              -- 4.

-- 1. LoudDog 继承了 Dog 的方法和变量。
-- 2. self 拥有来自 new() 的 'sound' 键,见 3。
-- 3. 与 LoudDog.new(LoudDog) 相同,并转换为
--    Dog.new(LoudDog),因为 LoudDog 没有 'new' 键,
--    但在其元表上有 __index = Dog。
--    结果:seymour 的元表是 LoudDog,而
--    LoudDog.__index = LoudDog。因此 seymour.key 将
--    = seymour.key,LoudDog.key,Dog.key,取决于
--    哪个表是第一个具有给定键的表。
-- 4. 'makeSound' 键在 LoudDog 中找到;这
--    与 LoudDog.makeSound(seymour) 相同。

-- 如果需要,子类的 new() 类似于基类的:
function LoudDog:new()
  newObj = {}
  -- 设置 newObj
  self.__index = self
  return setmetatable(newObj, self)
end

模块

-- 假设文件 mod.lua 的内容如下:
local M = {}

local function sayMyName()
  print('Hrunkner')
end

function M.sayHello()
  print('你好啊')
  sayMyName()
end

return M

-- 另一个文件可以使用 mod.lua 的功能:
local mod = require('mod')  -- 运行文件 mod.lua。

-- require 是包含模块的标准方式。
-- require 的作用类似于:     (如果没有缓存;见下文)
local mod = (function ()
  <mod.lua 的内容>
end)()
-- 就像 mod.lua 是一个函数体,因此
-- mod.lua 内的局部变量在外部不可见。

-- 这有效,因为这里的 mod = mod.lua 中的 M:
mod.sayHello() -- 打印:你好啊 Hrunkner

-- 这是错误的;sayMyName 只存在于 mod.lua 中:
mod.sayMyName()  -- 错误

-- require 的返回值是缓存的,因此一个文件
-- 最多只运行一次,即使多次 require。

-- 假设 mod2.lua 包含 "print('Hi!')"。
local a = require('mod2')  -- 打印 Hi!
local b = require('mod2')  -- 不打印;a=b。

-- dofile 类似于没有缓存的 require:
dofile('mod2.lua')  --> Hi!
dofile('mod2.lua')  --> Hi!(再次运行)

-- loadfile 加载一个 lua 文件,但尚未运行它。
f = loadfile('mod2.lua')  -- 调用 f() 来运行它。

-- load 是用于字符串的 loadfile。
-- (loadstring 已被弃用,使用 load 代替)
g = load('print(343)')  -- 返回一个函数。
g()  -- 打印出 343;此时没有任何内容打印。

出处

Learn Lua in Y Minutes 使用GPT翻译的这个后来发现官网已经有官方的汉化版本!!!!大写的尴尬,而且这个站点还不止一种语言的教程