Lua


3.7 其他运算符


文档摘要

3.7 其他运算符 Lua 语言中的其他运算符详解 本文将围绕以下 Lua 语言中的 "其他运算符" 展开详细讲解,并结合实际代码示例,帮助你深入理解和掌握它们的应用: 字符串连接运算符 ( ) 取长度运算符 ( ) 表构造器 ( ) (虽然不是严格意义上的运算符,但其行为类似于运算符,用于创建表) 变长参数运算符 ( ) 函数调用运算符 ( ) (同样,虽然不是传统意义上的运算符,但其在 Lua 中扮演着类似的角色) 成员访问运算符 ( , ) 幂运算符 ( ) 位运算符 ( , , , , , ) (Lua 5.3 及以上版本引入) 字符串连接运算符 ( ) 内容详解: 字符串连接运算符 用于连接两个字符串。这是 Lua 中拼接字符串的主要方式。

3.7 其他运算符

Lua 语言中的其他运算符详解

本文将围绕以下 Lua 语言中的 "其他运算符" 展开详细讲解,并结合实际代码示例,帮助你深入理解和掌握它们的应用:

  1. 字符串连接运算符 (..)

  2. 取长度运算符 (#)

  3. 表构造器 ({}) (虽然不是严格意义上的运算符,但其行为类似于运算符,用于创建表)

  4. 变长参数运算符 (...)

  5. 函数调用运算符 (()) (同样,虽然不是传统意义上的运算符,但其在 Lua 中扮演着类似的角色)

  6. 成员访问运算符 (., :)

  7. 幂运算符 (^)

  8. 位运算符 (&, |, ~, >>, <<, //) (Lua 5.3 及以上版本引入)

1. 字符串连接运算符 (..)

内容详解:

字符串连接运算符 .. 用于连接两个字符串。这是 Lua 中拼接字符串的主要方式。 与其他一些语言使用 + 运算符不同,Lua 明确使用 .. 来区分字符串连接和数值加法,避免了类型混淆和潜在的错误。

代码实践:

-- 示例 1: 连接两个字符串字面量 local str1 = "Hello" local str2 = "World" local result = str1 .. str2 print(result) --> 输出: HelloWorld -- 示例 2: 连接字符串变量和字面量 local name = "Alice" local greeting = "Welcome, " .. name .. "!" print(greeting) --> 输出: Welcome, Alice! -- 示例 3: 连接数字和字符串 (Lua 会自动将数字转换为字符串) local age = 30 local message = "You are " .. age .. " years old." print(message) --> 输出: You are 30 years old. -- 示例 4: 连接多个字符串 local part1 = "Lua" local part2 = "is" local part3 = "powerful" local part4 = "and" local part5 = "flexible." local sentence = part1 .. " " .. part2 .. " " .. part3 .. " " .. part4 .. " " .. part5 print(sentence) --> 输出: Lua is powerful and flexible. -- 示例 5: 在循环中高效构建字符串 (建议使用 table.concat 更高效,但这里演示 .. 运算符) local words = {"This", "is", "an", "example."} local longString = "" for i, word in ipairs(words) do longString = longString .. word .. " " end print(longString) --> 输出: This is an example. -- 示例 6: 连接特殊字符和转义序列 local specialStr = "Line 1\nLine 2\tTabbed" local combinedStr = "Special characters: " .. specialStr print(combinedStr) -- 输出: -- Special characters: Line 1 -- Line 2 Tabbed

总结:

.. 运算符是 Lua 中处理字符串拼接的基石。它简单易用,并且能够自动处理数字到字符串的转换,使得字符串操作更加方便。 在实际应用中,你需要根据场景选择合适的字符串构建方法。 对于少量字符串的连接,.. 运算符已经足够高效。 但如果需要在循环中频繁拼接大量字符串,为了提高性能,建议使用 table.concat 函数,它可以更高效地连接表中的字符串。

2. 取长度运算符 (#)

内容详解:

取长度运算符 # 用于获取字符串或表的长度。 对于字符串,它返回字符串中字符的个数(字节数,对于 ASCII 字符串和 UTF-8 字符串通常是字符数,但对于包含多字节字符的 UTF-8 字符串,字节数和字符数可能不同)。 对于表,它返回表作为序列部分(数组部分)的长度。

代码实践:

-- 示例 1: 获取字符串长度 local str = "Hello Lua" local length = #str print(length) --> 输出: 9 local emptyStr = "" local emptyLength = #emptyStr print(emptyLength) --> 输出: 0 local utf8Str = "你好世界" -- UTF-8 字符串 local utf8Length = #utf8Str print(utf8Length) --> 输出: 12 (注意:这里返回的是字节数,因为 UTF-8 编码中文通常占3个字节) -- 示例 2: 获取表(序列)长度 local arrayTable = {1, 2, 3, 4, 5} local arrayLength = #arrayTable print(arrayLength) --> 输出: 5 local mixedTable = {1, 2, 3, key = "value", 4, 5} local mixedLength = #mixedTable print(mixedLength) --> 输出: 5 (只计算序列部分) local sparseTable = {1, 2, nil, 4, 5} local sparseLength = #sparseTable print(sparseLength) --> 输出: 2 (在第一个 nil 处停止计数,因为 # 运算符只计算连续的序列部分) local emptyTable = {} local emptyTableLength = #emptyTable print(emptyTableLength) --> 输出: 0 -- 示例 3: 使用 # 运算符遍历序列表 local data = {"apple", "banana", "cherry"} for i = 1, #data do print(i, data[i]) end -- 输出: -- 1 apple -- 2 banana -- 3 cherry

重要注意事项:

  • 字符串长度: 对于 ASCII 字符串,# 返回的长度通常就是字符数。但对于 UTF-8 字符串,# 返回的是字节数,而不是字符数。 如果你需要获取 UTF-8 字符串的字符数,可能需要使用专门的 UTF-8 库来处理。

  • 表长度: # 运算符只适用于序列表(即数组部分)。 它从索引 1 开始计数,直到遇到第一个 nil 值为止。 如果表中存在键值对(非数字索引),# 运算符会忽略这些键值对,只计算序列部分的长度。 如果表中有显式的 nil 值作为序列的一部分,# 运算符会在第一个 nil 处停止计数。 对于非序列表(例如,只包含键值对的表),# 运算符的行为是未定义的,不应该依赖。 要获取表中键值对的数量,你需要手动迭代表并计数。

总结:

# 运算符是获取字符串和序列表长度的便捷工具。 理解其在不同数据类型上的行为,特别是对于 UTF-8 字符串和表的序列部分的限制,对于避免潜在的错误至关重要。 在处理表时,要明确你的目标是获取序列部分的长度还是整个表的元素数量,并选择合适的方法。

3. 表构造器 ({})

内容详解:

表构造器 {} 在 Lua 中用于创建新的表。 虽然它不是传统意义上的运算符,但它在语法上和行为上都类似于运算符,因为它接受输入(键值对或列表元素)并产生一个新的表作为输出。 表是 Lua 中唯一的数据结构,它既可以作为数组使用,也可以作为关联数组(哈希表)使用。 表构造器提供了多种灵活的方式来初始化表。

代码实践:

-- 示例 1: 创建空表 local emptyTable = {} print(type(emptyTable)) --> 输出: table -- 示例 2: 创建列表式表 (序列表) local listTable = {10, 20, 30, 40} print(listTable[1]) --> 输出: 10 print(listTable[3]) --> 输出: 30 print(#listTable) --> 输出: 4 -- 示例 3: 创建键值对表 (关联数组) local keyValueTable = {name = "Bob", age = 25, city = "New York"} print(keyValueTable.name) --> 输出: Bob print(keyValueTable["age"]) --> 输出: 25 -- 示例 4: 混合列表式和键值对表 local mixedTable = { "apple", "banana", color = "red", price = 1.5, "cherry" } print(mixedTable[1]) --> 输出: apple print(mixedTable[3]) --> 输出: cherry (注意 "banana" 是索引 2) print(mixedTable.color) --> 输出: red print(#mixedTable) --> 输出: 3 (序列部分长度为 3: "apple", "banana", "cherry") -- 示例 5: 嵌套表 local nestedTable = { {x = 1, y = 2}, {a = "hello", b = "world"}, [3] = {name = "Inner Table 3"} -- 使用 [index] 语法显式指定索引 } print(nestedTable[1].x) --> 输出: 1 print(nestedTable[2].b) --> 输出: world print(nestedTable[3].name) --> 输出: Inner Table 3 -- 示例 6: 使用索引初始化列表式表 local indexedList = { [1] = "first", [5] = "fifth", [3] = "third" } print(indexedList[1]) --> 输出: first print(indexedList[3]) --> 输出: third print(indexedList[5]) --> 输出: fifth print(#indexedList) --> 输出: 0 (由于索引不连续,序列部分长度为 0) -- 示例 7: 表构造器中使用表达式 local x = 5 local y = 10 local expressionTable = { x + y, x * y, string.upper("lua") } print(expressionTable[1]) --> 输出: 15 print(expressionTable[2]) --> 输出: 50 print(expressionTable[3]) --> 输出: LUA -- 示例 8: 使用分号分隔表元素 (可选,与逗号效果相同) local semicolonTable = { "a"; "b"; "c"; key="value" } print(semicolonTable[1]) --> 输出: a print(semicolonTable.key) --> 输出: value

表构造器的语法:

  • 空表: {} 创建一个没有任何元素的空表。

  • 列表式表: {value1, value2, value3, ...} 创建序列表,元素按顺序从索引 1 开始排列。

  • 键值对表: {key1 = value1, key2 = value2, ...} 创建关联数组,使用键值对初始化表。 键可以是标识符(例如 name)或用方括号括起来的表达式(例如 ["my key"][1+2])。

  • 混合表: 可以同时包含列表式元素和键值对元素。

  • 嵌套表: 表可以嵌套在其他表中,创建复杂的数据结构。

  • 索引指定: 可以使用 [index] = value 语法显式指定列表式元素的索引,允许创建非连续索引的序列。

总结:

表构造器 {} 是 Lua 中创建表的核心机制。 它提供了灵活多样的语法,可以创建各种类型的表,从简单的列表到复杂的嵌套数据结构。 熟练掌握表构造器是 Lua 编程的基础,因为表是 Lua 中最重要、最常用的数据结构。

4. 变长参数运算符 (...)

内容详解:

变长参数运算符 ... 在 Lua 中用于处理函数的可变数量参数(也称为 varargs)。 它允许函数接受任意数量的参数,而无需在函数定义时预先声明参数的个数。 在函数定义中,... 表示函数可以接受变长参数。 在函数体内部,... 作为一个特殊的表达式,表示一个包含所有变长参数的隐式表

代码实践:

-- 示例 1: 简单变长参数函数 function sum(...) local total = 0 for i, v in ipairs{...} do -- {...} 将变长参数转换为表 total = total + v end return total end print(sum(1, 2, 3)) --> 输出: 6 print(sum(10, 20, 30, 40, 50)) --> 输出: 150 print(sum()) --> 输出: 0 (没有参数时,变长参数表为空) -- 示例 2: 结合固定参数和变长参数 function greet(greeting, ...) local names = {...} for i, name in ipairs(names) do print(greeting .. ", " .. name .. "!") end end greet("Hello", "Alice", "Bob", "Charlie") -- 输出: -- Hello, Alice! -- Hello, Bob! -- Hello, Charlie! -- 示例 3: 使用 select("#", ...) 获取变长参数的个数 function argCount(...) local count = select("#", ...) -- select("#", ...) 返回变长参数的个数 print("Number of arguments:", count) for i = 1, count do print("Argument", i, ":", select(i, ...)) -- select(i, ...) 返回第 i 个变长参数 end end argCount(1, "hello", true) -- 输出: -- Number of arguments: 3 -- Argument 1 : 1 -- Argument 2 : hello -- Argument 3 : true -- 示例 4: 将变长参数传递给另一个函数 function outerFunction(...) print("Outer function received:", ...) -- 直接打印 ...,会以逗号分隔输出所有参数 innerFunction(...) -- 将所有变长参数传递给 innerFunction end function innerFunction(a, b, c) print("Inner function received:", a, b, c) end outerFunction(100, 200, 300) -- 输出: -- Outer function received: 100 200 300 -- Inner function received: 100 200 300 -- 示例 5: 使用 table.pack 和 table.unpack (Lua 5.2+) function packArgs(...) local args = table.pack(...) -- table.pack 将变长参数打包成一个表,并包含参数个数 'n' print("Packed arguments:", args) return table.unpack(args) -- table.unpack 解包表,返回所有参数 end local a, b, c = packArgs("x", "y", "z") print(a, b, c) --> 输出: x y z -- 示例 6: 变长参数为空的情况 function emptyVarargs(...) local args = {...} print("Arguments table:", args) print("Number of arguments:", #args) end emptyVarargs() -- 输出: -- Arguments table: table: 0x... (空表的内存地址) -- Number of arguments: 0

关键要点:

  • ... 在函数定义中: 表示函数可以接受变长参数。

  • ... 在函数体内: 表示一个包含所有变长参数的隐式表。 但需要注意,直接使用 ... 本身并不是一个表,而是用于传递和访问变长参数的机制。 通常需要使用 {...} 将其转换为表,以便使用 ipairs# 等表操作。

  • select("#", ...): 获取变长参数的个数。

  • select(i, ...): 获取第 i 个变长参数。

  • table.pack(...) (Lua 5.2+): 将变长参数打包成一个表,并包含参数个数 n。 推荐使用 table.pack 而不是 {...} 来将变长参数转换为表,因为它能正确处理 nil 值作为参数的情况。 {...} 会在遇到第一个 nil 参数时停止收集。

  • table.unpack(table) (Lua 5.2+): 解包表,返回表中的所有元素作为多个返回值。

总结:

变长参数运算符 ... 为 Lua 函数提供了极大的灵活性,允许函数处理不同数量的输入。 它在编写通用函数、处理不确定数量的数据或实现类似 C 语言 printf 风格的格式化输出函数时非常有用。 理解 ... 的工作原理,以及如何使用 selecttable.pack/unpack 来操作变长参数,是编写更强大和可复用 Lua 代码的关键。

5. 函数调用运算符 (())

内容详解:

函数调用运算符 () 用于调用函数。 在 Lua 中,函数是一等公民,可以像其他值一样赋值给变量、作为参数传递给其他函数、以及作为返回值返回。 函数调用运算符是执行函数代码的核心。 它跟在函数名或函数表达式之后,并可以包含零个或多个参数,参数之间用逗号分隔。

代码实践:

-- 示例 1: 调用无参数函数 function sayHello() print("Hello!") end sayHello() -- 调用 sayHello 函数 --> 输出: Hello! -- 示例 2: 调用带参数函数 function add(a, b) return a + b end local sum = add(5, 3) -- 调用 add 函数,传递参数 5 和 3 print(sum) --> 输出: 8 -- 示例 3: 调用匿名函数 (lambda 函数) local multiply = function(x, y) return x * y end local product = multiply(4, 6) -- 调用匿名函数 print(product) --> 输出: 24 -- 示例 4: 函数作为参数传递和调用 function applyOperation(func, a, b) return func(a, b) -- 调用作为参数传递的函数 func end local result1 = applyOperation(add, 10, 2) -- 传递 add 函数 local result2 = applyOperation(multiply, 7, 3) -- 传递 multiply 函数 print(result1) --> 输出: 12 print(result2) --> 输出: 21 -- 示例 5: 方法调用 (使用冒号运算符 `:`) local myTable = { value = 10, increment = function(self, step) -- 方法定义,使用 self 参数 self.value = self.value + step end } myTable:increment(5) -- 使用冒号运算符调用方法,self 隐式传递 myTable print(myTable.value) --> 输出: 15 -- 示例 6: 函数返回函数并调用 function createMultiplier(factor) return function(x) -- 返回一个匿名函数 return x * factor end end local double = createMultiplier(2) -- createMultiplier 返回一个函数,赋值给 double local triple = createMultiplier(3) -- createMultiplier 返回另一个函数,赋值给 triple print(double(7)) --> 输出: 14 print(triple(9)) --> 输出: 27 -- 示例 7: 链式函数调用 function getObject() return { setValue = function(self, val) self.data = val return self -- 返回对象自身,支持链式调用 end, getValue = function(self) return self.data end } end local obj = getObject() obj:setValue(10):setValue(20) -- 链式调用 setValue 方法 print(obj:getValue()) --> 输出: 20

函数调用的语法:

  • functionName(): 调用名为 functionName 的函数,不传递任何参数。

  • functionName(arg1, arg2, ...): 调用名为 functionName 的函数,传递参数 arg1, arg2, ...

  • expression(): 如果 expression 计算结果为函数,则调用该函数。 例如,multiply(4, 6)(function(x) return x * 2 end)(5)

  • object:methodName(arg1, arg2, ...): 使用冒号运算符调用对象的方法。 冒号运算符会自动将对象自身作为第一个参数(通常命名为 self)传递给方法。

  • 链式调用: 如果函数返回对象自身或其他可以调用方法的对象,可以连续调用多个函数,例如 obj:method1():method2():method3().

总结:

函数调用运算符 () 是 Lua 中执行函数的核心。 理解函数调用的语法,包括如何传递参数、调用匿名函数、方法调用以及链式调用,是编写 Lua 程序的基础。 函数在 Lua 中扮演着重要的角色,用于组织代码、实现模块化、以及构建各种功能和逻辑。

6. 成员访问运算符 (., :)

内容详解:

成员访问运算符 .: 用于访问表的成员(字段或方法)。 . 运算符用于访问表的字段,即通过键名(字符串或数字)来获取表中关联的值。 : 运算符主要用于调用表的方法,它实际上是 . 运算符的语法糖,并会自动将表自身作为第一个参数传递给方法。


发布者: 作者: 转发
评论区 (0)
U