NCL运算符与表达式¶
运算符或操作符指的是那些你从小就在使用的加号 +
、减号 -
等等。
运算符通过对值做“操作”或“运算”来生成新的值,运算符操作的对象是 操作数 或
运算子 ,根据操作对象的数量可以将常见的运算符分为 一元运算符 如求反
-
或 二元运算符 如加减乘除 + - * /
。对于二元运算符,可以将
其操作的两个操作数按相对位置分为左操作数和右操作数。
按照运算符的功能,可以将常用的运算符分为赋值运算符、算术运算符和逻辑运算符等
算术运算符¶
算术运算符操作基本数值类型的数组。NCL包含九个主要的算术运算符,分别是乘 *
、
除 /
、幂指数 ^
、加 +
、减 -
、取模(求余) %
、
小于选择 >
、大于选择 <
、和求反 -
。
除求反运算符为一元运算符外,所有的运算符都是二元运算符,都要求两个操作数。
一般来说,所有的操作数要求有相同的类型,如果对不同的类型变量进行算术运算, NCL将尝试转换它们到相同的类型,如转换失败,NCL将抛出类型不匹配错误。 同时,操作数应该有相同的维数和大小,或者操作数分别为标量和数组,运算符 对整个数组进行操作。
运算符 | 符号名称 | 符号解释 |
---|---|---|
- | 一元求反 | 生成操作数的负数 |
^ | 幂指数 | 右操作数成为左操作数的幂 |
* / % # |
乘 | 左操作数乘右操作数 |
除 | 左操作数除右操作数,支持整数除法 | |
取模 | 整数除法的整数余数,要求操作数都为整数 | |
矩阵乘 | 支持一维或二维数组,计算两数组的点积 | |
+ - |
加 | 左操作数加右操作数 |
减 | 左操作数减右操作数 | |
< > |
小于选择 | 选择对应位置左右操作数中的较小的值(标量和数组,扩展标量到整个数组) |
大于选择 | 类似小于选择运算符,相反操作 |
注:同一栏下优先级相同,不同栏从上之下优先级降低,即求反优先级最高。
关系运算符¶
NCL参考手册中将关系运算符(大于、小于、大于等于、小于等于、等于、不等于)和逻辑 运算符(与、或、非、异或)合并在一起,这里我将把这两种运算符拆分开来说明。
关系运算符是你熟悉的小于、大于这类运算符,关系运算符是构成关系表达式的基础,当然 关系表达式也是逻辑表达式最简单的一种情形。
NCL关系运算符和其他语言一致,提供6种关系运算符分别是大于、小于、大于等于、 小于等于、等于、不等于。比较可惜的是,只能使用英文字母缩写的形式。
运算符 | 含义 | 数学符号 | 英语含义 |
---|---|---|---|
.gt. | 大于 | \(>\) | greater than |
.ge. | 大于等于 | \(\geqslant\) | greater than or equa to |
.lt. | 小于 | \(<\) | less than |
.le. | 小于等于 | \(\leqslant\) | less than or equal to |
.eq. | 等于 | \(=\) | equal to |
.ne. | 不等于 | \(\ne\) | not equal to |
逻辑运算符¶
逻辑运算符通常用于逻辑变量的运算,返回结果为逻辑值。现实生活中常常碰到逻辑运算的 情况,比如“他不在家,并且也不在学校”。NCL中提供了四个逻辑逻辑运算符,以应对可能 的复合逻辑命题。
运算符 | 中文名 | 英语含义 | 示例 | 说明 |
---|---|---|---|---|
.and. | 逻辑与 | and | A .and. B | A,B都为真时,结果为真 |
.or. | 逻辑或 | or | A .or. B | A,B之一为真时,结果为真 |
.not. | 逻辑非 | not | .not. A | A为真时,结果为假 |
.xor. | 逻辑异或 | exclusive or | A .xor. B | A,B只有一个为真时,结果为真 |
短路求值/惰性求值¶
逻辑表达式支持所谓的短路求值或惰性求值,是一种逻辑运算符的求值策略,只有当第一个
操作数的值无法确定逻辑运算的结果时,才对第二个操作数进行求值。例如,当
.and.
的第一个操作数的值为 False
时,其结果必定为 False
;
当or的第一个操作数为 True
时,最后结果必定为 True
,在这种情况下,
就不需要知道第二个操作数的具体值。对于一些逻辑运算,如 .xor.
,
短路求值是不可能的。[1]
NCL中短路求值的具体判定如下表
左操作数 | 运算符 | 右操作数 | 结果 | 短路求值 |
---|---|---|---|---|
False | .and. | 任何逻辑值 | False | yes |
True | .and. | False | False | no |
True | .and. | True | True | no |
True | .and. | Missing | Missing | no |
Missing | .and. | 任何逻辑值 | Missing | no |
True | .or. | 任何逻辑值 | True | yes |
False | .or. | True | True | no |
False | .or. | False | False | no |
False | .or. | Missing | Missing | no |
Missing | .or. | 任何逻辑值 | Missing | no |
可以发现任何包含 Missing
操作数的逻辑运算其结果都将是 Missing
。
赋值运算符¶
赋值运算符是程序语言中使用最普遍的一个运算符,然而要深究其含义则在各种不同语言中 有不同的内在功效。由于NCL变量包含元数据,理解NCL赋值运算显得更加必要。
等号 =
即为赋值运算符,是赋值表达式的基础。通常而言,赋值运算符基于右操
作数给左操作数赋值。即, x = y
,将把y的值赋给x。然而在NCL中实际情况并不
往往如此,由于变量具有元数据,如当y具有一些属性时,赋值表达式就不仅仅是赋值了。
正因此,NCL将赋值运算分为了值到变量( Value-only )的赋值和变量到变量 ( Variable-to-variable )的赋值两种情形,下面也分别讨论。
值到变量赋值¶
指的是等号右边的仅仅是值,而不包含任何元数据的情况。这种情况是常见的赋值运算,等
号右边的值将被赋给左边的变量。如若仅仅要将一个包含元数据的变量的值赋给等号左边的
变量,需要使用数据定义符 (/ /)
将变量置于其中,此时元数据将全部丢弃。
a = 1
b = (/1, 2, 3, 4, 5/)
a@att1 = 1
a@att2 = 2
c = (/a/)
变量到变量赋值¶
指的是等号右边的是一个已经存在的变量,可能包含各种元数据。此时的赋值语句将尝试将 等号右边变量的属性、维名称和坐标赋给左侧变量。
b = (/ (/1.0,2.0,3.0/), (/4.0,5.0,6.0/), (/7.0,8.0,9.0/) /)
b!0 = "dim0"
b!1 = "dim1"
b@units = "none"
b&dim0 = (/.1,.2,.3/)
b&dim1 = (/10,100,1000/)
a = b
print(a)
a = (/ (/1.1,1.2,1.3/), (/2.1,2.2,2.3/), (/3.1,3.2,3.3/) /)
a!0 = "test0"
a!1 = "test1"
a@units = "Degrees"
a@long_name = "A"
b = (/ (/1.0,2.0,3.0/), (/4.0,5.0,6.0/), (/7.0,8.0,9.0/) /)
b!0 = "dim0"
b!1 = "dim1"
b@units = "none"
b&dim0 = (/.1,.2,.3/)
b&dim1 = (/10,100,1000/)
a = b
print(a)
重新赋值运算符¶
有时,我们可能需要给一个已经赋值过的的变量再次赋值,比如循环处理某些数据,而这些 数据的维数大小在每次循环中可能发生变化。这种情况下,我们自然而然地就会出现NCL 异常:
错误
fatal:Dimension sizes of left hand side and right hand side of assignment do not match fatal:[“Execute.c”:8573]:Execute: Error occurred at or near line 1
在NCL6.1.1版以前,面对这种情况,我们需要在循环结束前使用程序 delete
来释
放变量。但是在NCL6.1.1版中加入重新赋值运算符,可以很好地处理这一情况。
重新赋值运算符的形式为 :=
,即冒号和等号,中间不可有空格。
其含义是,尝试重新使用一个已经定义过的变量(若没有定义过,则重新赋值运算符等价于
赋值运算符),即将运算符右边的值或变量赋值(或重新赋值)给左边的变量,而不论左边
变量是否已经定义。事实上,如果左边的变量已经定义,那么NCL解释器将删除左边的变量,
然后根据右边的类型和维数重新定义左边的变量。
值到变量重新赋值¶
a = (/1, 2, 3, 4, 5/) ; 将整型一维5元素数组赋给变量a
a := (/"I", "like", "NCL"/) ; 给变量a重新赋值, a变为字符串型一维3元素数组
变量到变量重新赋值¶
a = new((/2, 2/), string) ; 创建一个20*20的字符串组
; 定义一个新的NCL变量b
b = (/ (/1.0,2.0,3.0/), (/4.0,5.0,6.0/), (/7.0,8.0,9.0/) /)
b!0 = "dim0"
b!1 = "dim1"
b@units = "none"
b&dim0 = (/.1,.2,.3/)
b&dim1 = (/10,100,1000/)
a := b ; 用变量b给变量a重新赋值,注意变量a此前已经定义
c := b ; 用变量b给变量c重新赋值,注意变量c此前未定义
print(a)
print(c)