运算符详解

运算符详解

> LeekScript 教程

现在来聊聊运算符。相信我,它们很快就会变得必不可少。

为此,我们要做一点数学。别慌!!别跑!放心,你只需要最基本的运算:加法、减法、乘法、除法,以及取模。如果你是新手,可能还不认识最后这个,但它一点也不复杂。

我们还会看到一些运算符,可以用简单的“=”以外的方式给变量赋值。

算术运算符

你的韭葱心算非常厉害。先用几道简单的计算给它热热身吧。

加法

LeekScript 中,做加法的符号是“+”。

用法很简单,像你平时那样写一个加法就行(我们希望你写过)。

还记得吗,给变量赋值是这样写的:

var unEntier = 6 debug(unEntier); // 显示:6

要做加法,只需把这个值换成你的数学表达式。

var addition = 3 + 5; debug(addition) // 显示:8

搞定!你的韭葱完成了它的第一次加法,还把结果告诉了你。 你也可以直接把算式作为参数传给 debug 函数。

debug(3+5); // 显示:8

减法

减法的符号毫无意外,就是“-”。

var soustraction = 7 - 4; debug(soustraction); // 显示:3

乘法

做这个运算时,别想着用字母“x”表示“乘”。在编程中,乘法用的是符号“\*”。

var multiplication = 3 * 5; debug(multiplication); // 显示:15

除法

至于除法,用的是符号“/”。

var division = 20 / 4; debug(division);

取模

先简单回顾一下取模。数学家朋友们有一种叫“欧几里得除法”的东西(也就是带余除法)。

普通除法中,7 除以 2 得 3.5。欧几里得除法则把结果拆成“商”和“余数”:7 = 2 * 3 + 1,商是 3,余数是 1。

我们这里关心的正是这个余数。取模就是求欧几里得除法余数的运算,符号是“%”。

例如 30%7 的结果是 2,因为 30 = 7 * 4 + 2。

var modulo = 42 % 9; debug(modulo) // 显示:6

整除(欧几里得除法)

这种除法返回两个数做欧几里得除法得到的商,符号是“\\”。

取模会把小数考虑在内,整除则相反,会先去掉两个数的小数部分。所以 5.9 \ 2.1 会被当作 5 \ 2 来算,结果是 2。

var quotient = 30 \ 7; debug(quotient) // 显示:4

运算优先级

和数学里一样,运算也有优先级。

乘法(\)、除法(/)和取模(%)优先于加法(+)和减法(-)。优先级相同的运算符(\、/ 和 %,或 + 和 -)在表达式中按从左到右的顺序计算。

如果要改变计算的优先顺序,就得用圆括号。例如:

debug(2 + 3 * 4); // 显示:14 debug((2 + 3) * 4); // 显示:20

debug(12 / 2 * 3); // 显示:18 debug(12 / (2 * 3)); // 显示:2

赋值运算符

你已经认识“=”了,它用来给变量赋值:

var maVariable = 8; debug(maVariable); // 显示:8

其实这个运算符还有几位哥哥。假设你想给一个变量加上某个值:变量的值是 8,我们想给它加 3。可以这样写:

var maVariable = 8; maVariable = maVariable + 3; debug(maVariable); // 显示:11

这样完全可行,但还能写得更好。算术运算符可以和“=”合并,于是就有了这 5 个非常实用的运算符:“+=”、“-=”、“\*=”、“/=”和“%=”。

上面的例子就变成了:

var maVariable = 8; maVariable +=3; // 等同于 maVariable = maVariable + 3; debug(maVariable); // 显示:11

结果一样,但少了一点重复。

var maVariable = 9; maVariable += 3; // 12 maVariable -= 2; // 10 maVariable *= 4; // 40 maVariable /= 2; // 20 maVariable %= 6; // 2 debug(maVariable); // 显示:2

自增运算符

这是两个小巧又很有用的运算符:自增运算符“++”和自减运算符“--”。

自增一个变量,就是把它的值加 1。

var maVariable = 3; maVariable++; // 等同于 maVariable = maVariable + 1; debug(maVariable); // 显示:4

而自减一个变量,就是把它的值减 1。

var maVariable = 3; maVariable--; // 等同于 maVariable = maVariable - 1; debug(maVariable); // 显示:2

逻辑运算符

逻辑运算符根据其他逻辑值算出一个新的逻辑值:

另请参阅:https://leekwars.com/encyclopedia/Conditions#Op%C3%A9rateurs_logiques

位运算符

位运算符不如经典的数学运算符(+、-、/ 等)那么出名,但资深饲养员会用它们写出很省操作数的函数或 AI。

前置知识

要理解这些运算符的用法,你得熟悉数字的二进制表示法(也就是以 2 为基数的记数法)。如果你对此还不了解,网上有大量讲解、教程和资料,能帮你很快弄明白。

如果看到 9 = 1001 这个等式你一头雾水,那最好先去了解一下二进制表示法,再往下读。

记法

本章接下来会经常在二进制数和十进制数之间切换。为了便于理解、避免混淆,我们采用以下记法:

于是,下面这些命题全都成立:

5d === 101b // 全部换成十进制,就是 5d === 5d 10d > 101b // 换成十进制,就是 10d > 5d 11d !== 11b // 还是换成十进制,就是 11d !== 3d

位运算符的用法

位运算符和其他数学运算符(+、-、/ 等)一样,作用于 2 个数字操作数,返回的结果也是数字。和经典的数学运算符一样,它们写在两个操作数之间。

它们可以用在表达式中(见下一章)、用于给变量赋值……总之!和经典的数学运算符一模一样。

例如:

debug(3 + 4); // 调试器中会显示 7 debug(3 & 4); // 调试器中会显示 0(计算过程见下文)

各个运算符详解

位运算符是“按位”工作的:它们会根据传入数字的二进制表示,对两个操作数的每一对比特分别做运算。不过在详细讲解之前,我们先看看每个运算符作用于单个比特时的表现。

重要提示:位运算符的名字(有时连 LeekScript 中的写法也是)和逻辑运算符(and、or 等)非常相似。一定要记住:

所以千万别把按位与(AND)运算符“&”和逻辑与(AND)运算符“ && ”(在脚本中也可以直接写成“and”)搞混。

与(AND)运算符:& 这个运算符在 LeekScript 中写作 &,作用于一对比特时,只有两个输入比特都为 1,结果才为 1,其余情况结果都为 0。 写成真值表就是:

a | b | | a & b --|---|----|------ 0 | 0 | -> | 0 0 | 1 | -> | 0 1 | 0 | -> | 0 1 | 1 | -> | 1

或(OR)运算符:| 或(OR)运算符在 LeekScript 中写作“|”(法语键盘上是 AltGr + 6),只要两个输入比特中至少有一个为 1,结果就为 1;两个都为 0 时结果为 0。

真值表如下: a | b | | a \| b --|---|----|------ 0 | 0 | -> | 0 0 | 1 | -> | 1 1 | 0 | -> | 1 1 | 1 | -> | 1

异或(XOR)运算符:^ 异或(XOR)运算符在 LeekScript 中写作“^”,两个输入比特中恰好有一个为 1 时结果为 1,否则为 0。

真值表: a | b | | a ^ b --|---|----|------ 0 | 0 | -> | 0 0 | 1 | -> | 1 1 | 0 | -> | 1 1 | 1 | -> | 0

非(NOT)运算符:~ 非(NOT)运算符在 LeekScript 中写作“~”,会把二进制数中的每个 0 和 1 互换。

真值表: a | | ~a --|----|------ 0 | -> | 1 1 | -> | 0

但要注意:在 LeekScript 中,整数变量以 32 位存储。对 10d(二进制 1010)做 NOT,得到的不是 5d(0101b),而是 -11,也就是二进制的 1111111111111111 1111111111110101。

位运算符的工作原理

“好极了!这不就是把逻辑运算符原封不动再讲一遍嘛!”

并不完全是。我们刚才看的是这些运算符在输入一对比特时的表现。不过前面也说过,这些运算符是作用于数字的。

正如它们的名字所暗示的,这些运算符工作在奇妙的二进制世界里。

简单来说,它们会逐位遍历两个输入数字的二进制写法,算出一个二进制数,再把结果作为数字输出。

举个例子胜过长篇大论,表达式 “10 & 9” 是这样计算的:

| | 十进制表示 | | 二进制表示 ---------------------------|-------------------|----|------------------ 操作数 1 | 10d | -> | 1010b 操作数 2 | 9d | -> | 1001b 应用运算符 | | | 1000b 转换为十进制 | 8d | | 1000b

仔细看从 1010b & 1001b 得到 1000b 的过程:其实就是对两个数的每一位分别应用“&”运算符:

数字 | 第 1 位 | 第 2 位 | 第 3 位 | 第 4 位 ------------|---------|---------|---------|-------- 10d / 1010b | 1 | 0 | 1 | 0 9d / 1001b | 1 | 0 | 0 | 1 结果 | 1 | 0 | 0 | 0

因此会得到这样的结果:

debug(10 & 9); // 调试器中会显示 8。

如果两个数的二进制位数不同,较短的那个会在第一个 1 前面补上“无效”的 0。

例如,表达式 14 ^ 3 的计算过程如下: | | 十进制表示 | | 二进制表示 ---------------------------|-------------------|----|------------------ 操作数 1 | 14d | -> | 1110b 操作数 2 | 3d | -> | 0011b 应用运算符 | | | 1101b 转换为十进制 | 13d | | 1101b

因此:

debug(14 ^ 3); // 调试器中会显示 13。

移位

移位运算符用来把一个数的各个比特平移若干位,符号是 >> 和 **> 运算符把数的所有比特向右移,如果数是正数就在左边补 0,是负数就补 1。相当于把这个数除以 2。

>> 的示例(5 位数字) 运算 | 二进制表示 | 结果 | 二进制表示 ----------|-------------------------|----------|---------------------- 10d >> 1 | 01010b >> 1 | 5d | 00101b 10d >> 2 | 01010b >> 2 | 2d | 00010b 10d >> 3 | 01010b >> 3 | 1d | 00001b -5d >> 3 | 11011b >> 3 | -1d | 11111b

>>> 的示例(5 位数字) 运算 | 二进制表示 | 结果 | 二进制表示 ----------|-------------------------|----------|---------------------- 10d >>> 3 | 01010b >>> 3 | 1d | 00001b -5d >>> 3 | 11011b >>> 3 | 3d | 00011b

**>= 1;||3|00011b| maVariable >>= 1||6|00110b|

这些运算符有什么用

位运算符的一大优势是:它们消耗的操作数非常少。因此,你可以用适中的开销写出复杂的函数;对于需要大量调用、埋在多层嵌套循环深处的函数,也能借此压低它的开销。

现在你已经掌握了使用这些新运算符的基础。发挥一点想象力,完全可以想到它们能派上什么用场……

表达式

表达式到底是什么?如果我对你说“8x - 5”,这就是一个“数学表达式”。编程中的表达式也差不多。

我们在本章写的这些,都是表达式。当你写下:

你就是把表达式“8 * 9 + 4 * 2”的结果赋给变量“maVariable”。计算机理解这个表达式,算出结果是 80,然后把 80 这个值赋给变量。

记住这个名字,后面我们还会讲到它。

不过,表达式可不只是一串数字和符号。表达式里还可以包含其他变量!

你完全可以这样写:

同样要清楚:给变量赋值时,如果它原来已经有值,旧值就会被覆盖。例如:

阅读代码时,一定要把这些概念记在心里。

练习

练习是本教程非常重要的一部分,建议你每一道都做,毕竟熟能生巧嘛。你很可能觉得这一章很简单,但在编程中,再小的细节也很重要。下面是第一道练习:

练习:SWAP(交换)

先来个简单的:交换两个变量的值,轮到你了 :) 如果还是太简单,试过不借助第三个变量来完成吗?

总结

运算符差不多都介绍完了,只剩下比较运算符还没讲。它们和条件密切相关,我们会在后面的章节中介绍。

现在你已经会用 LeekScript 做计算了。也许眼下你还觉得这没多大用处,但在接下来的章节里,你就会明白会做计算有多重要。

接下来的章节会讲条件和循环,这是编程中两个极其重要的概念。