挑战TJS Section1.6:操作符
正篇
Yuu:上一回,我们使用了System
类的inputStirng()
方法,不过我们写的脚本得到的内容嘛…
Yuni:即使输入的是一个数字,得到的也是一个字符串。一次我们必须将得到的字符串转成数字。
Yuu:没错。不过将字符串转成数字是很容易的。
Yuni:嗯?真的吗?
Yuu:嗯,因为有将字符串转成数字的操作符。顺便提一句,操作符是一种符号,我们用它来进行计算。
Yuni:那,加法“+”是操作符吗?
Yuu:没错。TJS中有很多操作符,所以这次我们介绍一些基本的操作符,其中就包括了类型转换操作符。
Yuni:好。
Yuu:首先是“=”。这被称为简单赋值操作符(英:Simple Assignment Operator,日:単純代入演算子)译者注:等号在这里的含义是赋值,将等号右面的当前值赋予等号左边内容(通常是变量)。注意这个符号不是等于比较的含义。
/简单赋值操作符/
var a, b; // 定义变量a、b
a = 1; // 将等号右侧的值(1)代入等号左边(变量a)
b = 1 + 2; // 将等号右侧的计算结果(3)代入等号左边(变量b)
Yuni:嗯。我们用了好几次了,所以大家也都很熟悉他们了。
Yuu:下一个运算符是“+”。这是一个加法运算符(简称加号,英:Addition Operator,日:加算演算子),用于数值相加和字符串连接的操作符。如果加号左右都是数值,得到的结果是两者的和;如果加号左右至少有一个是字符串,得到的结果就是连接后的字符串。
/加法运算符/
var a, b, c;
a = 4 + 8; // 变量a的结果是4+8的和为12
b = "T" + "J" + "S"; // 变量b的值是T、J、S连接在一起的字符串“TJS”
c = "1+1等于" + 2; // 数字2会转成字符串“2”,然后与前面的“1+1等于”连接为“1+1等于2”,并赋值给变量c
Yuni:嗯,没问题
Yuu:下面是“-”。这是个减法运算符(简称减号,英:Subtraction Operator,日:減算演算子),用于计算减法。这里的减号和四则运算的减号含义相同,减法得到的结果是减号左边的值减去右边的值。如果减号左右存在字符串,那么这个字符串会先转成数字再完成减法。译者注:这种转换是比较“危险”的,因为它可能产生意想不到的结果。如果字符串本身就是合法的数值,转换不会有问题。如果字符串包含非数字内容,转换并不会产生错误,但得到的结果不一定是我们想要的。
/减法操作符/
var a, b;
a = 100 - 99; // 变量a的值是100-99的差为1
b = "10" - 2; // 字符串的值是“10”,会先转换为数值10,再做减法,得到的结果8代入变量b中
Yuni:变量a的值很容易理解因为它就是普通的减法运算。但是…变量b的那句就让人迷糊了。如果操作数存在字符串,会先转换成数值…译者:这和加法是完全相反的好不好…
Yuu:嘛…加法其实是最特殊的,乘除法在这上面的规定都和减法相同:出现字符串的话会先转成数值。
Yuni:这样的吗?
Yuu:嗯。字符串的减法、乘法和除法没有意义。
Yuni:你这么说也没问题。
Yuu:所以只有加法才有将数值转成字符串的情况。
Yuni:Ok了解。
Yuu:好,下面是“*”。这是乘法操作符(简称乘号,英:Multiplication Operator,日:乗算演算子),用来计算乘号左右的乘积。同样地,如果其中一个操作数是字符串,它会先被转换成数值。
/乘法操作符/
var a, b;
a = 3 * 6; // 变量a是3×6的计算结果等于18
b = "20" * 5; // 字符串“20”先转换为数值20,然后乘5,得到100,赋值给变量b
Yuni:这就是普通的乘法啊。
Yuu:我想也是。下面是除法运算符(简称除号,英:Division Operator;日:除算演算子),用于计算两个数的商。有两种除号“/”和“\”,它们都是计算除号左边的数除以右边的数的商,不过“/”得到是实数类型;而“\”得到的是整数类型。译者注:在其他语言中,“/”称为浮点除,“\”称为整除。和四则运算的规则相同,除数不能是0.
/除法操作符/
var a, b, c, d;
a = 10 / 4; // 变量a的值是10÷4的结果为2.5
b = 10 \ 4; // 变量b的值是10÷4的结果的整数部分,为2
c = "15" / 5; // 字符串的值“15”先转换为数值15,然后再做除法,得到结果3赋值给c
d = "15" \ 5; // 字符串的值“15”先转换为数值15,然后再做除法,得到结果3(整数部分就是3)赋值给c
Yuu:对于“\”,得到的商是整数类型,因此小数部分被截尾。
Yuni:是截尾而不是四舍五入?
Yuu:对。因为小数部分总是被截掉,因此即便原本的结果是2.99999999999,得到的也是2. 并且,负数也遵循这样的原则,-10 \ 4
的结果是-2.5,截尾后是-2.
Yuni:嗯嗯。
译者注:到此,四则运算都介绍完了。下面介绍的运算符是程序语言中普遍定义的运算。
Yuu:下一个是“%”。你可以称它是取模操作符(英:Modulo Operation,日:剰余算演算子),用于计算取模操作符左边与右边作商后的余数。译者注:由于取模运算本身基于除法,因此除数不能是0。
/取模操作符/
var a, b;
a = 10 % 4; // 变量a是10÷4的余数,为2
b = "10" % 5; // 字符串的值”10“,先转换成数字10,然后计算10÷5的余数是0,并代入变量b中
Yuni:取模操作的对象都是整数。译者注:如果操作数有小数,它会被先截尾转成整数,再参与运算。
Yuu:没错。另外,加减乘除和取模操作符(+、-、*、/、\、%)都可以和赋值号“=”组合成组合赋值运算符(英:Compound Assignment Operator)
/组合赋值运算符/
var a = 3, b = 3, c = 3, d = 3, e = 3, f = 3;
a += 2; // 等价于a = a + 2,a的值是5
b -= 2; // 等价于b = b + 2,b的值是1
c *= 2; // 等价于c = c * 2,c的值是6
d /= 2; // 等价于d = d / 2,d的值是1.5
e \= 2; // 等价于e = e \ 2,e的值是1
f %= 2; // 等价于f = f % 2,f的值是1
Yuu:举个例子,你可以使用“+=”运算符快速地将a=a+b
写成a+=b
.
Yuni:这是个还挺方便的运算符。译者注:组合赋值运算符的计算方式是:先计算赋值号右边的值,然后按照a = a op b
的形式计算新的等号左值。
Yuu:+=
和-=
从某种程度上很像接下来要介绍的“++”、“–”。它们分别被称为自增运算符和自减运算符(英:Increment/Decrement Operator,日:インクリメント演算子/デクリメント演算子),用于对某个变量进行加1和减1的操作。等同于a += 1
和a -= 1
。
/自增/自减操作符/
var a = 3, b = 3, c = 3, d = 3;
++a; // 前置自增操作符,等同于a = a + 1,a的值是4
b++; // 后置自增操作符,等同于b = b + 1,b的值是4
--c; // 前置自减操作符,等同于c = c - 1,c的值是2
d--; // 后置自减操作符,等同于d = d - 1,d的值是2
Yuu:对于“++”和“–”,数值可以在操作符的左边也可以在右边。在数值左边的“++”、“–”被称为前置自增/自减操作符;相反的,在数值右边的“++”、“–”被称为后置自增/自减操作符。
Yuni:在上面的例子中,它们都产生了相同的结果,那前置和后置的差别是什么?
Yuu:在下面的例子中,前置和后置将产生不一样的结果。
/前置后置自增自减运算符的差异/
var a = 3, b = 3, c = 3, d = 3;
System.inform(++a); // 前置自增操作符:a先加1得4,再显示a(4)
System.inform(b++); // 后置自增操作符:先显示b(3)之后b再加1
System.inform(--c); // 前置自减操作符:c先减1得2,再显示c(2)
System.inform(d--); // 后置自减操作符:先显示d(3)之后d再减1
Yuu:对于前置的(在数值左边)自增自减运算符,数值总会在它被使用前自增自减;对于后置的(在数值右边)自增自减运算符,数值总会在它被使用后自增自减。
Yuni:这听起来很难被使用啊…
Yuu:但是在这之后它还是很常用的,所以我觉得还是要适应它的使用。
Yuni:是吗?
Yuu:嗯。下一个是将字符串转成数值的“+”运算符!
Yuni:诶?“+”运算符不是用来进行加法的吗。
Yuu:实际上,有两种长得都是“+”的运算符。一个是加法,另一个是将字符串转成数字的。
Yuni:额,这一点也不好。“+”出现的时候你怎么知道是哪种运算符?
Yuu:嘛…加号是一个二元运算符(英:Binary Operator,日:2項演算子),而字符串转数字是一个单目运算符(英:Unary Operator,日:単項演算子)。
Yuni:什么是单目运算符和双目运算符?
Yuu:双目运算符是拥有两个操作数的运算符,通常两个操作数一个位于符号的左边一个位于右边。单目运算符只拥有一个操作数,操作数在符号的一边,具体是哪一边取决于符号的定义。像这样。
/“+”的单目运算与双目运算/
var a, b;
a = "1" + "2"; // 这里“+”的左右都有操作数,是双目运算符,a的结果是12
b = +"1"; // 这里“+”的左边没有操作数,是单目运算符(字符串转数值),b的结果是数值1
Yuni:没错。如果想将字符串转成数值,“+”的左边就没有操作数。
Yuu:没错,这是我们能分辨的原因。
Yuni:那,我们可以像自增自减运算符那样,把这里的“+”放到数值的后面,像这样a = "1" +;
吗?
Yuu:不可以的。自增自减运算符在哪个边都可以,但是单目的“+”必需出现在字符串的左边。
Yuni:那我们怎么知道运算符应该是在左边还是在右边?
Yuu:单目运算符既可以在左边可以在右边,还可以两边都可以,这取决于运算符。因此我们无法仅凭运算符的类型知道。译者注:通常意义上,我们认为前置和后置自增自减是不同的运算符。因此,一个运算符只能出现在操作数的一侧。
Yuni:哦好吧。
Yuu:你可以记住一些常见的运算符的结合性。如果你不了解一个运算符的话,你可以去阅读TJS2文档中的“式と演算子”部分。
Yuni:好我知道了。
Yuu:此外,“-”也是一个值在右侧的单目运算符,你能猜出什么吗?
Yuni:额…
Yuu:像a = -1;
这样。
Yuni:这是个可以取负数的运算符。
Yuu:没错。准确的来说,它是一个取相反数的运算符。
Yuni:嗯嗯,如果a
是-1,-a
就是1。
Yuu:没错。另外还有一些可以改变数据类型的单目运算符,这里也来介绍一下。
Yuni:好。
Yuni:它们是int操作符,用来转换成整数。real操作符,用来转换成实数。string操作符用来转换成字符串。它们的使用方式如下:
/类型转换操作符/
var a, b, c;
a = int "123"; // 字符串“123”转成整数123
b = real "2.6"; // 字符串2.6转成实数2.6
c = string -20; // 数值-20转成字符串“-20”
Yuni:这和单目运算符“+”是一样的,数值在运算符的右边。哦对了,int和real操作符也可以将字符串转成数值,它们和单目的“+”有什么区别呢?
Yuu:int
和real
分别转成整数和实数,不论操作数是什么样的;而“+”可以根据操作数的内容动态选择转换的类型。如果字符串内容是个整数,转换的结果就是整数;如果内容是小数,转换的结果是实数。
/字符串转成数字的运算符差别/
var a, b, c, d;
a = int "1.2"; // 1.2的整数部分1是a的值
b = real "3"; // b的值是实数类型的3
c = +"1.2"; // “1.2”是小数形式,转成的数值是实数1.2
d = +"3"; // “3”是整数形式,转成的数值的整数3
Yuni:明白了。这看上去有点难以区分呢…
Yuu:嗯,当需要转换成数值的时候,你可以通常使用“+”操作符,除非你需要指定结果的类型。
Yuni:哦,这样好记多了。
Yuu:嘛,最后一个是typeof操作符。这也是个单目运算符,它告诉我们它右值的类型。
/typeof操作符/
var a = 1, b = 0.1, c = "abc", d = new Date(), e;
var A, B, C, D, E;
A = typeof a; // 由于a是整数类型,A的值是“Integer”(表示整数)
B = typeof b; // 由于b是实数类型,B的值是“Real”(表示实数)
C = typeof c; // 由于c是字符串类型,C的值是“String”(表示字符串)
D = typeof d; // 由于d是对象类型,D的值是“Object”(表示对象)
E = typeof e; // 由于e没有被任何实体赋值,E的值是“void”
Yuu:由于TJS中有多种类型的变量,因此如果你需要知道当前变量的类型,它会很有用。
Yuni:哈,这也算是操作符啊。
Yuu:还有不少操作符没有介绍呢,不过这次就先介绍这些了。不过这回解决了字符串转数值的问题,下回我们继续1.5节的脚本。
Yuni:要继续啦…
Yuu:嗯,这次介绍了不少运算符的内容,下次就会关注脚本的编写了。
Yuni:okay
Yuu:嗯,下回见啦。
要点
- 运算符可以根据操作数的个数分为单目运算符和双目运算符。单目运算符只有一个操作数,可能出现在符号的左边或右边。双目运算符拥有两个操作数,它们通常一个在符号左边一个在符号右边。符号左边的称为左值,右边的称为右值。
- 赋值符号用“=”表示,赋值方向是从右向左。也即先计算赋值号右边的内容,然后把结果代入左边的内容(通常是变量)。这里,左值必须是一个可修改的内容,字面常量不可以作为左值。
2 = 4
是不合规的。赋值号并不是用来判断相等的。赋值符号是一个双目运算符。 -
四则运算:加号。加号完成数值加法之外,还可以连接字符串。加号是一个双目运算符。如果其中一个操作数是字符串,得到的结果就是字符串。非字符串的操作数会自动转换成字符串。
-
四则运算:减号、乘号。这两者都是完成普通四则运算中的减法和乘法。如果操作数的其中是字符串,它会先被转换成数值。字符串的转换方式是,从第一个字符开始转换,直到无法转换为止,中间已经转换的值就是它的值。这一点和PHP很相似。例:“152”->152、“15qwer”->15、“qwer15”->0.
-
四则运算:除法。除法的定义和四则运算的除法相同,如果操作数存在字符串也是先转换成数值。除号有两种,一种是浮点除“/”,得到商的真实值;另一种是整除“\”,得到商的整数部分。和四则运算相似,除数不能是0.
-
取模运算。这个就是求两个数的余数用的,也是双目运算符。和上面不同的是,它的两个操作数都必须是整数。如果不是,将会被转换成整数。取模运算在实际编程中出现频率还是比较高的。
-
单目运算符“+”和“-”。这两个符号都出现在操作数的左侧。“+”本身的含义是保持一个数字的本身数值,它经常用于将字符串转成数值,并且这种转换是考虑内容的。小数会转换成实数类型,整数会转换成整数类型。“-”本身的含义是取相反数,如果它后面接的是字符串,字符串会和“+”一样转换成对应的数值,再取相反数。
var a = 3.5, b = "3.5";
System.inform((+a) + " " + (+b) + " " + (-a) + " " + (-b));
-
组合赋值运算符:这种是一个运算符和赋值号结合的形式,对于加法来说,
a += b
是a = a + (b)
的简写形式。这里把b用括号括起来的原因是执行顺序上,右侧的部分会先执行,得到一个值再展开成原始赋值的形式。如果考虑到运算的优先级,a *= 5 + 2
等价于a = a * (5 + 2)
而不是a = a * 5 + 2
. -
自增自减运算符。这个和C里面自增自减是一样的。自增自减本身可以表达为
a += 1
和a -= 1
。因此,自增自减的运算对象是出现在赋值号左边的,它只能是变量,常量不可以(像++3
)。自增自减符号可以放在操作数的左边或右边。放在左边的称为前置自增自减,放在右边的称为后置自增自减。前置自增自减在使用这个变量的值之前完成自增自减,后置自增自减在使用这个变量的值之后完成自增自减。自增自减在使用上建议单独出现,不要放在复杂的表达式中,否则容易出错,虽然这种是C语言中无聊的常见考题。例如a+=++i+j++
这样。 -
类型转换运算符。其实就是在要转换的内容前转换后的类型名称。虽然类型有6个但是转换符只有3个:
int
、real
和string
。例如int "123"
、string 3.5
。这种转换时类型指定的,这意味着如果指定了整型,即使数值时小数也会转成整数,例int "3.5"
等于3而不是3.5。这一点是它和单目运算符“+”的区别。 -
typeof运算符。这个运算符可以得到操作数的类型,是一个字符串。类型与typeof的对应关系:
类型 | 对应字符串 |
---|---|
void | void |
整数 | Integer |
实数 | Real |
对象 | Object |
字符串 | String |
八位二进制 | Octet |
指定的对象成员不存在(第二章会讲解) | undefined |
拓展
优先级
虽然正篇中没有直接提及,但是我们都知道运算符本身是具有优先级的。比如乘除优先于加减执行。同一级不同运算符还存在着执行的顺序,有的是从左向右,有的是从右向左,这一点我们成为结合性。
简单总结下本次出现运算符的优先级。这个规律只对本次出现的运算符有效。
单目运算符 都大于 双目运算符
乘除+取模 都大于 加减
单目运算符之间都是右结合的,也就是从右向左执行;双目运算符都是左结合的,也就是从左向右执行。
举例:
1 * 2 + 3 % 4 => (1 * 2) + (3 % 4)
1 * 2 % 3 + 4 \ 5 => ((1 * 2) % 3) + (4 \ 5)
a++ * 2 + -b => ((a++) * 2) + (-b) => a * 2 + (-b), a+=1
可以看到,最后一行这样的写法是正确的,但是并不那么易懂。为了避免潜在的错误,建议使用括号指定运算顺序。
[…] Yuu:变量b是字符串类型,但是通过1.6节讲到的单目操作符+就可以转换成数值类型了。它也就和变量a的值相同,都是1了。 […]