NCL数据处理与绘图基础-1-基本语法

1. 语法字符

=赋值
:=重赋值
;注释
/;...;/注释块
@创建或引用一个属性
!创建或引用一个命名维数
&创建或引用一个坐标变量
$...$
使用函数 addfile 输入输出变量时用于
封装字符串变量
{…}
利用坐标变量截取数组
[…]
列表变量的截取
(/../)
创建数组
[/…/]
创建列表变量
:
数组下标分隔符
|
命名维度的分隔符
\
连接符
::
调用外部函数时的分隔符
->
读入/写出所支持的数据类型

2. 表达式

2.1 数学表达式

+加,或连接字符串
-减 / 负号
*
/除以
%

余数

>大于
<小于
^幂指数
#矩阵乘

  • 注:
  1. 使用括号可以修改计算优先级
  2. ‘+’号有两重含义,对应两种应用:(1)加法;(2)连接字符串“value:”+6 ->“value:6”
  3. ‘-’号有两重含义,对应两种应用:(1)负号,具有最高优先级;(2)减法

2.2 逻辑表达式

.lt.小于
.le.小于等于
.eq.等于
.ne.不等于
.ge.大于等于
.gt.

大于

.and.并且
.or.
.xor.异或
.not.

  • 注:逻辑算符从左向右执行,直至返回值为否。
  • 例如:

if ( x .gt. 3 .and. x .lt. 7) then

      [statement(s)]

end if
 在该例中,NCL 将首先判断变量 x 是否大于 3,如果为否,则不再判断 x 是否小
于 7。可见,为提高计算时效,用户可将最不易成立的逻辑表达式放在最左边。 

2.3 数组表达式

  • 数学运算符(加、乘、 除、比较等)可以用于标量和数组。数组运算符需要所有数组需要具有相同的维 数和数组大小。例如:
  a = (/ 4, 2, 1, 3 /)
  b = (/ 0, 2, 2, 0 /)
  c = a + b   ->   c = (/ 4, 4, 3, 3 /)
  c = a - b    ->   c = (/ 4, 0, -1, 3 /)
  c = a * b    ->   c = (/ 0, 4, 2, 0 /)
  c = a/(b+0.1)   ->    c = (/ 40, 0.952381, 0.4761905, 30 /)
  • 注:
  1.  NCL 中数组的截取是从下标 0 开始;
  2. 对于多维数组,其最左侧的维变化最慢,而最右侧维的变化最快,数组按照 “行×列”存储。
  • 例如:
T(12, 6, 4)为一个三维数组,
左侧维为第 0 维,由 12 个要素组成
中部维为第 1 维,由 6 个要素组成
右侧维为第 2 维,由 4 个要素组成
  • 在NCL中,`new`函数用于创建新的数组。这个函数的基本语法是:

new(array_size, type, [_FillValue])

其中各个参数的含义如下:

1. `array_size`:这是一个整数或整数数组,表示新创建数组的尺寸。如果是一个整数,那么创建的是一维数组,其长度为`array_size`指定的值。如果是一个整数数组,那么创建的是多维数组,其维度大小由`array_size`数组中的每个元素指定。

2. `type`:这是一个字符串,指定新数组的数据类型。常见的数据类型包括`float`(浮点型)、`double`(双精度浮点型)、`int`(整型)等。

3. `_FillValue`:这是一个可选参数,用来指定数组中未初始化元素的缺省值。如果提供了这个参数,那么在数组创建时,所有未赋值的元素都会被设置为这个缺省值。如果不提供这个参数,NCL会根据数组的数据类型自动设置一个缺省值。如果希望数组中没有缺省值,可以指定`"No_FillValue"`作为这个参数。

  • 例:

- `m = new(12, float)` 表示创建一个长度为12的一维浮点型数组,未指定缺省值,所以NCL会为这个数组自动设置浮点型的缺省值。

- `q = new((/2,3,6/), float)` 表示创建一个三维浮点型数组,其维度大小分别为2、3和6,同样未指定缺省值。

- `k = new(100, float, 1e20)` 表示创建一个长度为100的一维浮点型数组,并且指定了缺省值为`1e20`。

- `l = new(100, float, "No_FillValue")` 表示创建一个长度为100的一维浮点型数组,并且明确指出没有缺省值。

- `p = new(dimsizes(m),double)` 表示创建一个与数组`m`具有相同维度大小的双精度浮点型数组,这里没有指定缺省值,所以会使用双精度浮点型的缺省值。

在使用`new`函数声明数组时,如果用户没有指定缺省值,也没有使用`"No_FillValue"`,NCL会根据数组的数据类型自动设定一个缺省值,这个缺省值通常是该数据类型可能的最小值或者一个特定的标记值,用于表示该位置的数据未被初始化。
 

2.3.1 数组下标

  • 在NCL中,下标用于访问和操作数组中的元素。下标可以是单个整数、整数范围或带有间隔的范围。
  • 一维数组的下标使用

对于一维数组 `a = (/1, 2, 3, 4, 5, 6, 7, 8, 9, 10 /)`:

 `a1 = a`:直接赋值整个数组,`a1` 与 `a` 相同,即 `(/1, 2, 3, 4, 5, 6, 7, 8, 9, 10 /)`。
 `a2 = a(3)`:选择数组 `a` 的第四个元素(下标从0开始),因此 `a2 = 4`。
 `a3 = a(1:4)`:选择数组 `a` 中下标从1到4的元素,即 `(/2, 3, 4, 5 /)`。
 `a4 = a(0:9:3)`:选择数组 `a` 中下标从0到9,间隔为3的元素,即 `(/1, 4, 7, 10 /)`。
 `a5 = a(:5)`:选择数组 `a` 中下标从0到5的元素,即 `(/1, 2, 3, 4, 5, 6 /)`。
 `a6 = a(7:)`:选择数组 `a` 中从下标7开始到数组末尾的所有元素,即 `(/8, 9, 10 /)`。
 `a7 = a(1:6:-1)`:选择数组 `a` 中下标从1到6,间隔为-1的元素,即 `(/2, 3, 4, 5, 6 /)`,这里间隔为-1表示逆序选择。
 `a8 = a(8:4)`:选择数组 `a` 中下标从8到4的所有元素,即 `(/9, 8, 7, 6, 5, 4 /)`。
 `a9 = a(::-1)`:选择数组 `a` 中所有元素,间隔为-1,即从最后一个元素开始逆序选择,结果为 `(/10, 9, 8, 7, 6, 5, 4, 3, 2, 1 /)`。

  •  多维数组的下标使用

对于多维数组 `T`,假定其大小为 `12 x 100 x 120`:

- `T1 = T(0:11:3, :19, :)`:选择数组 `T` 中第一个维度下标从0到11,间隔为3的元素;第二个维度下标从0到19的所有元素;第三个维度的所有元素。因此 `T1` 的大小为 `4 x 20 x 120`。

这种下标使用方法允许你灵活地选择和操作数组中的特定部分,无论是单个元素还是子数组。

2.3.2 命名下标

  • 在NCL中,命名下标(也称为坐标轴名称)允许你对数组进行重新排序或选择特定的子集。命名下标的主要优势在于,你可以通过维度的名称来引用和操作数组,而不是仅仅依赖于维度的顺序。这种方法在处理多维数据时特别有用,因为它使得代码更加清晰和易于理解。

  • 命名下标的基本概念

当你给数组的每个维度指定一个名称时,你可以使用这些名称来引用和操作数组。例如,对于一个二维气压变量 `pres`,其维度名称分别为 `lat` 和 `lon`,你可以通过这些名称来访问和操作数据。

  • 重新排序数组

在NCL中,使用命名下标重新排序数组的语法如下:

pres_reord = pres(dim1|:, dim2|:)

这里的 `dim1` 和 `dim2` 是维度的名称,`:` 表示选择该维度的所有元素。垂直线 `|` 是必须的,用于分隔维度名称和下标表达式。

  • 例:

 `pres(21,40)` 是一个二维数组,维度名称为 `lat` 和 `lon`,分别对应纬度和经度。
 `pres_reord1 = pres(lon|:, lat|:)`:这里,我们通过命名下标重新排序了 `pres` 数组。`lon|:` 表示选择经度维度的所有元素,`lat|:` 表示选择纬度维度的所有元素。结果是 `pres_reord1` 的维度顺序变为 `lon` 和 `lat`,即 `pres_reord1(40,21)`。
 `pres_reord2 = pres(lon|19:39, lat|0:9)`:在这个例子中,我们不仅重新排序了维度,还选择了特定的子集。`lon|19:39` 表示选择经度维度中下标从19到39的元素,`lat|0:9` 表示选择纬度维度中下标从0到9的元素。结果是 `pres_reord2` 的维度顺序为 `lon` 和 `lat`,大小为 `20 x 10`。

  • 注:

- 使用命名下标时,必须为所有维度提供名称和下标表达式。
- 垂直线 `|` 是必须的,用于明确区分维度名称和下标表达式。
- 重新排序数组时,维度的顺序会根据你在表达式中指定的顺序改变。

这种命名下标的方法使得在处理复杂多维数据时,代码更加直观和易于维护,尤其是在需要对数据进行重新排序或选择特定子集时.

2.3.3 坐标下标

  • 在NCL中,坐标下标(coordinate subscript)是一种强大的工具,它允许你通过实际的坐标值而不是简单的整数索引来访问和操作数组。这种方法特别适用于处理具有物理意义的坐标数据,例如经纬度、时间等。

  • 普通下标与坐标下标

**普通下标**:使用整数索引来访问数组元素。例如,`m(0)` 表示访问数组 `m` 的第一个元素。
**坐标下标**:使用实际的坐标值来访问数组元素。例如,`m&lat = m` 定义了一个坐标变量 `lat`,之后你可以通过坐标值来访问数组,如 `m(-5.0)`。

  • 坐标下标的使用方法

在NCL中,你可以使用大括号 `{}` 来指定坐标范围,从而以一种“自然”的方式对数组进行截取。

  • 例:

m = (/ -5.0, 10.0, 15.0, 20.0, 25.0, 30.0 /)
m!0 = "lat"  ; 命名维
m&lat = m    ; 定义其坐标变量
mn = m( { -5.0: 25.0: 2 } )  ; 使用坐标下标进行截取

在这个例子中:

- `m` 是一个包含多个浮点数的一维数组。
- `m!0 = "lat"` 将数组的第一个维度命名为 `lat`。
- `m&lat = m` 定义了坐标变量 `lat`,使得你可以通过坐标值来访问数组。
- `mn = m( { -5.0: 25.0: 2 } )` 使用坐标下标进行截取。这里 `{ -5.0: 25.0: 2 }` 表示选择坐标值从 `-5.0` 到 `25.0`,间隔为 `2` 的元素。因此,`mn` 包含的元素为 `-5.0, 15.0, 25.0`。

  • 注意事项

- 使用坐标下标时,必须确保数组已经定义了相应的坐标变量。
- 坐标下标允许你以更直观的方式处理具有物理意义的数据,例如在气象数据中根据实际的经纬度或时间值来选择数据。
- 坐标下标特别适用于处理多维数据集,其中每个维度都有明确的物理意义和坐标值。

通过使用坐标下标,你可以更灵活地操作和分析具有物理意义的多维数据集,使得代码更加直观和易于理解.

2.4 函数表达式

由于函数返回数值,所以可以认为函数也是一种表达式。函数包括声明和调
用,声明和调用需使用函数名称和相关的变量列表。具体用法详见 7.5

3.数据类型

在NCL(NCAR Command Language)中,数据类型分为数值类型、Enumeric类型和非数值类型。以下是每种类型的详细说明:

3.1 数值类型

**double(双精度型)**:
  - **位数**:64位
  - **特点**:用于存储双精度浮点数,精度较高,适用于需要较高精度的科学计算.

- **float(浮点型)**:
  - **位数**:32位
  - **特点**:用于存储单精度浮点数,精度相对较低,但占用的存储空间较小,适用于一般的科学计算.

- **long(长整型)**:
  - **位数**:32位或64位
  - **特点**:用于存储较大的整数,具体位数取决于系统的架构(32位系统为32位,64位系统为64位).

- **integer(整型)**:
  - **位数**:32位
  - **特点**:用于存储一般的整数,适用于大多数整数运算.

- **byte(字节型)**:
  - **位数**:8位
  - **特点**:用于存储小范围的整数,适用于存储二进制数据或进行位操作.

3.2 Enumeric类型

- **int64**:
  - **位数**:64位
  - **特点**:有符号整数类型,用于存储较大的整数,范围为[-2^63, 2^63-1].

- **uint64**:
  - **位数**:64位
  - **特点**:无符号整数类型,用于存储非负整数,范围为[0, 2^64-1].

- **uint**:
  - **位数**:32位
  - **特点**:无符号整数类型,用于存储非负整数,范围为[0, 2^32-1].

- **ulong**:
  - **位数**:32位或64位
  - **特点**:无符号整数类型,用于存储较大的非负整数,具体位数取决于系统的架构.

- **ushort**:
  - **位数**:16位
  - **特点**:无符号整数类型,用于存储较小的非负整数,范围为[0, 2^16-1].

- **ubyte**:
  - **位数**:8位
  - **特点**:无符号整数类型,用于存储非常小的非负整数,范围为[0, 2^8-1].

3.3 非数值类型

- **string(字符串型)**:
  - **特点**:用于存储文本数据,可以包含任意字符序列,适用于文本处理和字符串操作.

- **character(字符型)**:
  - **特点**:用于存储单个字符,适用于字符级别的操作和处理.

- **graphic(图形)**:
  - **特点**:用于存储图形对象,可以表示各种图形元素和属性,适用于图形绘制和可视化.

- **file(文件型)**:
  - **特点**:用于表示文件对象,可以用于文件的读取、写入和操作,适用于文件I/O操作.

- **logical(逻辑型)**:
  - **特点**:用于存储布尔值(true或false),适用于逻辑运算和条件判断.

- **list(列表型)**:
  - **特点**:用于存储一组有序的数据项,可以包含不同类型的元素,适用于数据集合的存储和操作.

这些数据类型为NCL提供了丰富的数据表示和处理能力,使得用户可以根据不同的需求选择合适的数据类型进行科学计算、数据处理和可视化等操作.


4. 变量及元数据

在NCL中,变量的定义和赋值非常灵活,不需要事先声明变量的类型。变量的类型是根据赋值的内容自动确定的。这种动态类型系统使得代码编写更加简洁和直观。

  • 变量定义

- **整型**:  x = 2
  这里 `x` 被赋值为整数 `2`,因此 `x` 是一个整型变量。

- **浮点型**:  y = 3.6
  这里 `y` 被赋值为浮点数 `3.6`,因此 `y` 是一个浮点型变量。

- **双精度型**: z = 30.d
  这里 `z` 被赋值为双精度数 `30.d`,其中 `d` 表示双精度类型。

- **字符串**:  title = "This is the title string"
  这里 `title` 被赋值为字符串 `"This is the title string"`,因此 `title` 是一个字符串变量。

- **逻辑型**:a = True
  这里 `a` 被赋值为逻辑值 `True`,因此 `a` 是一个逻辑型变量。

- **数组**:

 a = (/1, 4, 3, 2/)
  这里 `a` 被赋值为一个整型数组 `(/1, 4, 3, 2/)`。

  b = (/1, 2.0, 3.0, 4.0/)
  这里 `b` 被赋值为一个浮点型数组 `(/1, 2.0, 3.0, 4.0/)`。

  c = (/1., 2, 3., 4.0/) * 1d5
  这里 `c` 被赋值为一个双精度型数组,因为 `1d5` 是双精度数,所以整个数组被提升为双精度型。

  d = (/"red", "blue"/)
 这里 `d` 被赋值为一个字符串数组 `(/"red", "blue"/)`。

  e = (/True, False, False, True/)
  这里 `e` 被赋值为一个逻辑数组 `(/True, False, False, True/)`。

   f = (/ (/1, 2/), (/3, 6/), (/4, 2/) /)
   这里 `f` 被赋值为一个二维数组,形状为 `3x2`。

  • 变量名称

- NCL的变量名称是区分大小写的,例如 `"H2M"` 和 `"h2m"` 是两个不同的变量。

  • 元数据

元数据是指与变量或文件相关的文字性描述或数值信息。在NCL中,元数据对于计算函数和绘图函数非常重要,因为它们通常会使用变量的元数据来执行操作。NCL变量有三种主要的元数据:

1. **属性(Attributes)**:
   - 属性是与变量相关的附加信息,可以是文字或数值。例如,可以为变量设置单位、描述等属性。

 a@units = "m/s"

 a@description = "Velocity"

2. **命名维度(Named Dimensions)**:
   - 命名维度是给数组的每个维度指定一个名称,使得代码更加清晰和易于理解。

   a!0 = "time"
   a!1 = "lat"

3. **坐标变量(Coordinate Variables)**:
   - 坐标变量用于定义数组的坐标轴,使得可以通过坐标值来访问数组元素。
   

   lat = (/ -90, -60, -30, 0, 30, 60, 90 /)
   a&lat = lat

4.1 属性

在NCL中,属性是与变量相关联的附加信息,通常用于描述变量的特征、单位、数据质量等。属性可以通过“@”符号来读取、赋值或修改。以下是一些常用的属性及其用法:

  • 常用属性

- **_FillValue**:用于指定数组中缺失值的填充值。在读取数据时,这个值会被识别为缺失值。

temp@_FillValue = 1e20

- **missing_value**:与 _FillValue 类似,用于指定缺失值的值。

    temp@missing_value = 1e20

- **units**:用于指定变量的单位。

  var@units = "degK"
  lon@units = "degrees_east"

- **long_name**:用于提供变量的详细描述。

  t@long_name = "Near-Surface Temperature"

- **standard_name**:用于指定变量的标准名称,通常用于与其他数据集进行比较和互操作。

  t@standard_name = "air_temperature"

- **coordinates**:用于指定变量的坐标轴信息。
 

  t@coordinates = "lat lon"

- **scale_factor** 和 **add_offset**:用于数据的缩放和偏移,通常用于压缩数据。
 

  t@scale_factor = 0.1
  t@add_offset = 273.15

- **valid_min** 和 **valid_max**:用于指定变量的有效范围。

  t@valid_min = -50.0
  t@valid_max = 50.0

- **axis**:用于指定坐标轴的类型,例如 "X", "Y", "Z", "T" 等。
 

  lon@axis = "X"

  • 读取和修改属性

- **读取属性**:

  date = time@units

- **修改属性**:

  var@units = "degC"

  • 从文件中读取属性

可以使用 `getfilevaratts` 函数从文件中读取变量的属性:

fid = addfile("file.nc", "r")
file_atts = getfilevaratts(fid, "slp")

  • 检查属性是否存在

可以使用 `isatt` 函数来确认变量的某个属性是否存在:

if (isatt(slp, "units")) then
  print(slp@units)
end if

  • 注:

- 如果设置了 `missing_value` 属性,则 `_FillValue` 也必须使用相同的类型和数值。这确保了数据的一致性和正确处理。
- 属性的正确设置对于数据的解释和分析非常重要,尤其是在与其他数据集进行比较或进行科学计算时.

4.2 命名纬

  • 在NCL中,维度的命名是通过“!”符号来实现的。这种命名方式使得代码更加清晰和易于理解,因为它允许你通过有意义的名称来引用数组的各个维度,而不是仅仅依赖于维度的索引位置。以下是对维度命名的详细说明:
  • 维度命名的语法

- **命名维度**:
  ```ncl
  test!0 = "time"
  test!1 = "height"
  test!2 = "latitude"
  test!3 = "longitude"
  ```
  在这个例子中,`test` 是一个四维数组,通过“!”符号对每个维度进行了命名:
  - `test!0 = "time"`:将第0维命名为“time”。
  - `test!1 = "height"`:将第1维命名为“height”。
  - `test!2 = "latitude"`:将第2维命名为“latitude”。
  - `test!3 = "longitude"`:将第3维命名为“longitude”。

  • 维度的索引和形状

- **维度索引**:在NCL中,数组的维度从左至右分别为第0维至第n-1维,其中n是数组变量的维数大小。这种索引方式与C语言类似。
- **维度形状**:每个维度的大小是一个整数,表示该维度的元素总数。例如,如果一个数组的形状为 `(10, 20, 30, 40)`,则第0维的大小为10,第1维的大小为20,依此类推。

  • 标量

- **标量**:具有一个值的单维数变量被称为标量。标量可以看作是一个0维数组,即没有维度的数组。

  • 读取和修改维度名称

- **读取维度名称**:
 

  print(test!0)  ; 输出 "time"

- **修改维度名称**:

   test!0 = "new_time"

  • 优点

- **代码可读性**:通过命名维度,代码变得更加直观和易于理解。例如,`test(time, height, latitude, longitude)` 比 `test(0, 1, 2, 3)` 更容易理解。
- **灵活性**:在处理多维数据时,维度名称可以用于更复杂的操作,如数据切片、排序等。

4.3 坐标变量

在NCL中,坐标变量是用于描述数组数据的物理坐标信息的重要工具。坐标变量通过“&”符号来读取、赋值或修改。以下是关于坐标变量的详细说明和设置步骤:

  • 坐标变量的定义

- **定义**:坐标变量必须是一维变量,它与数组变量的对应维度同名且大小相同。这意味着,如果你有一个二维数组 `temp`,其维度分别为 `lat` 和 `lon`,那么你需要为 `lat` 和 `lon` 各自定义一个一维的坐标变量。
- **作用**:坐标变量允许你通过实际的物理坐标值(如经纬度、高度等)来访问和操作数据,而不仅仅是通过整数索引。

  • 设置坐标变量的步骤

1. **命名维度**:

   temp!0 = "lat"  ; 命名第 0 维为 lat
   temp!1 = "lon"  ; 命名第 1 维为 lon

2. **定义坐标变量的数值**:
 

   lon_pts = (/ 0., 15., 30., 45., 60. /)  ; 数组大小为 5,对应 temp 的第 1 维
   lat_pts = (/ 30., 40., 50., 60. /)      ; 数组大小为 4,对应 temp 的第 0 维

3. **设定坐标变量的属性**:
 

  lon_pts@units = "degrees_east"
  lat_pts@units = "degrees_north"

4. **将坐标变量赋给数组**:

   temp&lon = lon_pts
   temp&lat = lat_pts

  • 注意事项

- **无缺省值**:坐标变量不能有缺省值,即不能有 `_FillValue` 或 `missing_value` 属性。
- **单调性**:坐标变量中的数值必须单调递增或单调递减。这对于数据的插值和分析非常重要。
- **数据类型**:坐标变量可以是任何数值类型,如浮点型、整型等,具体选择取决于数据的精度需求和存储效率。

  • 查看坐标变量

你可以使用以下命令来查看坐标变量的信息:

- **查看坐标变量的值**:

  print(temp&lon)

- **查看变量的详细信息**:

  printVarSummary(temp)

4.4 字符串引用

在NCL中,当需要动态地引用变量的属性和坐标变量名称时,可以使用“$”符号来实现。这种方法特别适用于在不知道具体属性名或坐标变量名的情况下进行操作。以下是对这种用法的详细解释:

  •  使用“$”符号引用属性和坐标变量

- **引用属性**:

  attnames = (/ "_FillValue", "long_name" /)
  att0 = var@$attnames(0)$

  这里,`attnames` 是一个包含属性名称的字符串数组。使用 `var@$attnames(0)$` 可以动态地引用 `var` 的 `_FillValue` 属性。这相当于直接写 `var@_FillValue`。

- **引用坐标变量**:

  newData = (/Data/)  ; 仅将 Data 中的数值复制至 newData, 不复制元数据
  if (.not.ismissing(var!0) .and. iscoord(var, var!0)) then
    newData!0 = Data!0  ; 定义新数组的坐标变量名称
    newData&$Data!0$ = Data&$Data!0$  ; 新数组的坐标变量的赋值
  end if

  在这个例子中:
  - `newData = (/Data/)` 创建了一个新数组 `newData`,仅复制了 `Data` 的数值,没有复制元数据。
  - `if (.not.ismissing(var!0) .and. iscoord(var, var!0)) then` 检查 `var` 的第0维是否存在坐标变量。`ismissing` 函数用于检查维度名称是否缺失,`iscoord` 函数用于检查指定的维度名称是否为坐标变量。
  - 如果条件为真,则将 `Data` 的第0维坐标变量名称和值赋给 `newData`。

  • 动态引用的优势

- **灵活性**:在处理不同数据集或变量时,属性和坐标变量的名称可能会有所不同。使用“$”符号可以动态地引用这些名称,使得代码更加灵活和通用。
- **减少硬编码**:避免在代码中硬编码具体的属性名或坐标变量名,使得代码更容易维护和适应不同的数据结构。

  • 注意事项

- **确保变量存在**:在使用“$”符号引用属性或坐标变量时,需要确保目标变量确实存在相应的属性或坐标变量,否则可能会导致错误或异常。
- **代码可读性**:虽然“$”符号提供了灵活性,但在某些情况下可能会使代码的可读性降低。因此,在使用时需要权衡灵活性和可读性,适当注释以提高代码的可理解性。

4.5 列表变量

在NCL中,列表变量是一种非常灵活的数据结构,可以包含多个异构变量,即每个变量可以有不同的类型、大小和形状。列表变量的处理方式类似于栈或队列,提供了多种操作方法。以下是对列表变量的详细说明和操作示例:

  • 创建列表变量

1.使用方括号创建列表变量

```ncl
i = (/ (/1,2,3/), (/4,5,6/), (/7,8,9/) /) ; 二维整型数组
x = 5.0 ; 浮点型标量
d = (/100000.d, 28304309.23d/) ; 一维双精度数组
s = "abcde" ; 字符串
c = stringtochar("abcde") ; 字符
v1 = [/i, x, d, c, x/] ; 通过[/…/]构建列表变量
```

在这个例子中,`v1` 是一个列表变量,包含了不同类型的变量 `i`、`x`、`d`、`c` 和 `x`。

2. 使用栈的方式创建列表变量

```ncl
x = (/1,2,3,4/)
x@attr = "integer array"
y = (/ 9., 8., 7., 6. /)
y@attr = "float array"
s = (/ "one", "three", "two" /)
s@attr = "string array"
my_list = NewList("lifo") ; 创建列表变量 my_list
ListPush(my_list, x) ; 添加 x 变量至列表 my_list 中
ListPush(my_list, y) ; 添加 y 变量至列表 my_list 中
ListPush(my_list, s) ; 添加 s 变量至列表 my_list 中
```

在这个例子中,`my_list` 是一个列表变量,使用 `NewList` 函数创建,并通过 `ListPush` 函数动态地添加变量 `x`、`y` 和 `s`。

注:当使用程序 ListPush 将变量添加至列表变量时,新添加的变量总是位

于最前。

  •  访问和操作列表变量

1. 获取列表元素个数

```ncl
cnt = ListCount(my_list)
print(cnt)
```

这将输出列表 `my_list` 中的元素个数。

2.检查元素是否存在

```ncl
idx = ListIndex(my_list, x)
print(idx)
```

这将输出变量 `x` 在列表 `my_list` 中的下标位置。

  • 访问列表元素

1. **通过下标访问**:

  ```ncl
  e = my_list[1]
  print(e)
  ```

  这将输出列表 `my_list` 中第二个元素的内容。

-2. **通过 `ListPop` 访问**:

  ```ncl
  a = ListPop(my_list)
  ```

  这将从列表 `my_list` 中移除并返回最后一个元素(如果列表类型为LIFO)或第一个元素(如果列表类型为FIFO)。

  •  列表类型的操作

1. **获取列表类型**:

  ```ncl
  lt = ListGetType(my_list)
  print(lt)
  ```

2. **设置列表类型**:

```ncl
  ListSetType(my_list, "lifo")
  lt = ListGetType(my_list)
  print(lt)
  ```

4.6变量赋值

在NCL中,变量赋值是一个灵活且重要的操作,其具体过程取决于变量的状态和赋值的内容。以下是对NCL变量赋值过程的详细解释:

  • 变量赋值的基本规则

1. **变量未定义的情况**:
   - 如果等号左侧的变量未定义或已被删除,NCL会先对该变量进行声明,然后使用等号右侧的值或变量对其进行赋值。
   - 赋值后,左侧变量的类型和维数将与右侧变量或值一致。
   - 例:

   ```ncl
     x = 5  ; x 被声明为整型变量,并赋值为 5
     y = (/1, 2, 3/)  ; y 被声明为一个整型数组,并赋值为 (/1, 2, 3/)
     ```

2. **变量已定义的情况**:
   - 如果等号左侧的变量已经定义,则赋值时需要满足以下条件之一:
     - 右侧变量和左侧变量具有相同的类型和数组大小。
     - 如果类型不同但数组大小一致,右侧变量的类型会在赋值时被强制转换为左侧变量的类型。
   - 例:

   ```ncl
     x = 5  ; x 已经定义为整型变量
     x = 5.0  ; 虽然 5.0 是浮点数,但会被强制转换为整型赋值给 x
     ```

  •  类型转换和数组大小匹配

- **类型转换**:
  - 当右侧变量的类型与左侧变量不同时,NCL会尝试进行类型转换。例如,将浮点数赋值给整型变量时,浮点数会被截断为整数。
  - 如果类型转换无法进行(例如,将字符串赋值给整型变量),NCL会报错。

- **数组大小匹配**:
  - 如果右侧变量是数组,其大小必须与左侧变量的大小一致,否则赋值操作会失败。
  - 例:

    ```ncl
    a = (/1, 2, 3/)  ; a 是一个长度为 3 的整型数组
    b = (/4, 5, 6/)  ; b 也是一个长度为 3 的整型数组
    a = b  ; 赋值成功,因为 a 和 b 的大小一致
    ```

  • 注意事项

- **变量声明的隐式性**:NCL允许在赋值时隐式声明变量,这使得代码编写更加灵活,但也需要注意变量的类型和维数。
- **类型转换的局限性**:虽然NCL支持一定程度的类型转换,但在某些情况下(如从字符串到数值类型),转换可能会失败,需要显式地进行类型转换。
- **数组大小的一致性**:在进行数组赋值时,确保数组大小一致是非常重要的,否则会导致错误。

4.6.1 值至变量赋值

在NCL中,将值赋给变量是一个常见的操作,尤其是在处理数组和表达式时。以下是对值至变量赋值的具体过程和注意事项的详细解释:

  • 值至变量赋值的基本规则

1.变量未定义的情况

- **赋值操作**:如果等号左侧的变量未定义,NCL会首先声明该变量,然后将右侧的值或表达式赋值给该变量。
- **结果**:通过这种方式得到的变量通常没有命名维度、坐标变量和其他属性,除了可能的 `_FillValue` 属性。如果右侧的表达式没有定义 `_FillValue`,则赋值后的变量也不会包含 `_FillValue`。
- 例:

  ```ncl
  x = 5  ; x 被声明为整型变量,并赋值为 5
  ```

2. 变量已定义的情况

- **赋值操作**:如果等号左侧的变量已经定义,右侧的值将直接赋值给左侧变量,而不需要重新声明变量。
- **维度一致性**:通常情况下,右侧的值或数组的维度必须与左侧变量的维度一致。但如果右侧是一个标量值,则可以赋值给左侧变量的多个位置。
- 例:
 

```ncl
  a = (/1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12/)
  a(0:4) = -1  ; 将 -1 赋值给 a 的前 5 个元素
  ```

  • 元数据的赋值

- **元数据赋值**:如果左侧是变量的元数据(如属性),则只有数值会被赋值给左侧变量,其他属性保持不变。
- 例:
 

```ncl
  a@units = "m/s"  ; 赋值属性,不影响数组本身
  ```

  •  标量赋值给多个位置

- **标量赋值**:当右侧是一个标量值时,它可以赋值给左侧变量的多个位置,而不需要维度匹配。
- 例:

  ```ncl
  a = (/1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12/)
  a(0:4) = -1  ; 将 -1 赋值给 a 的前 5 个元素
  ```

  • 注意事项

- **类型匹配**:在赋值时,右侧的值类型会自动转换为左侧变量的类型(如果可能)。例如,将浮点数赋值给整型变量时,浮点数会被截断为整数。
- **维度一致性**:对于数组赋值,除了标量赋值的情况外,右侧的数组维度必须与左侧变量的维度一致。
- **属性处理**:如果左侧变量已经定义了属性(如 `_FillValue`),则在赋值时这些属性通常不会被改变,除非显式地进行修改。

4.6.2 变量至变量赋值

在NCL中,变量到变量的赋值是一个复杂的过程,涉及到变量的数值、属性、命名维度和坐标变量等多个方面。以下是对变量到变量赋值的详细解释和示例:

  • 变量到变量赋值的基本规则

1. 未定义变量的赋值

- **赋值操作**:如果等号左侧的变量未定义,NCL会首先声明该变量,然后将右侧变量的数值和所有元数据(属性、命名维度和坐标变量)传递给左侧变量。
- 例:

  ```ncl
  right = (/ (/1.0, 2.0, 3.0/), (/4.0, 5.0, 6.0/), (/7.0, 8.0, 9.0/) /)
  right!0 = "dim0"
  right!1 = "dim1"
  right@units = "none"
  right@dim0 = (/ 1, 2, 3 /)
  right@dim1 = (/ 10, 100, 1000 /)
  left = right
  ```


  在这个例子中,`left` 会被赋予与 `right` 相同的数值和元数据.

2. 已定义变量的赋值

- **赋值操作**:如果等号左侧的变量已经定义,且没有使用下标,右侧变量的数值和所有元数据会被传递给左侧变量。如果使用下标,则仅对指定的部分进行赋值。
- **维度和命名维度**:如果等号左右两侧变量的命名维度不一致,NCL会用右侧的命名维度覆盖左侧的命名维度,并可能给出警告信息。
- **属性合并**:右侧变量的属性会合并到左侧变量中,如果属性名称相同,则右侧的属性值会覆盖左侧的属性值。如果属性类型不同,则会报错.
- **坐标变量**:如果左侧变量没有坐标变量,而右侧变量有,则左侧变量的坐标变量会被创建并赋值。如果是部分赋值,则仅被赋值的部分坐标变量会被重新赋值,其余部分会被赋予缺省值.
- 示例:

  ```ncl
  right = (/ (/0.1, 0.2, 0.3/), (/1.1, 1.2, 1.3/), (/2.1, 2.2, 2.3/) /)
  right!0 = "test0"
  right!1 = "test1"
  right@units = "Degrees"
  right@long_name = "A"
  left = (/ (/0., 1.0, 2.0/), (/3.0, 4.0, 5.0/), (/6.0, 7.0, 8.0/) /)
  left!0 = "dim0"
  left!1 = "dim1"
  left@units = "none"
  left&dim0 = (/.1, .2, .3/)
  left&dim1 = (/10, 100, 1000/)
  left = right
  ```


  在这个例子中,`left` 的命名维度和坐标变量会被更新为 `right` 的值,属性也会合并。

3. 强制数值赋值

- **使用 `(/` 和 `/)`**:在赋值时,如果使用 `(/` 和 `/)`,则仅将右侧变量的数值赋值给左侧变量,忽略属性、命名维度和坐标变量。
- 例:
 

```ncl
  left_new = (/right/)
  ```


  在这个例子中,`left_new` 仅包含 `right` 的数值,不包含任何元数据.

4.部分赋值

- **部分赋值操作**:可以对变量的部分数值进行赋值,此时需要使用下标来指定赋值的部分。
- 例:

  ```ncl
  a = (/ (/1.1, 1.2, 1.3/), (/2.1, 2.2, 2.3/), (/3.1, 3.2, 3.3/) /)
  a!0 = "dim0"
  a!1 = "dim1"
  a&dim0 = (/ .1, .2, .3 /)
  a&dim1 = (/ .1, .01, .001 /)
  b = (/ (/7.0, 8.0, 9.0/), (/4.0, 5.0, 6.0/), (/1.0, 2.0, 3.0/) /)
  b!0 = "dim0"
  b!1 = "dim1"
  b&dim0 = (/ .1, .2, .3 /)
  b&dim1 = (/ 10.0, 100.0, 1000.0 /)
  b(0,:) = a(0,:)
  ```


  在这个例子中,`b` 的第一行被更新为 `a` 的第一行的值,坐标变量 `dim1` 也会被更新.

  • 注:

- **维度一致性**:在赋值时,确保变量的维度一致是非常重要的,否则可能会导致错误或不期望的结果.
- **属性和坐标变量的处理**:在赋值过程中,属性和坐标变量的处理可能会导致一些复杂的情况,因此在进行赋值操作时,需要仔细考虑这些因素.
- **使用 `(/` 和 `/)’进行数值赋值**:如果仅关注数值的赋值,而不关心元数据的处理,可以使用 `(/` 和 `/)’来简化赋值操作.

4.7 变量重赋值

在NCL中,重赋值是一种特殊的操作,用于重新定义和赋值一个先前声明过的变量。重赋值使用符号“:=”,并且在NCL版本6.1.1及之后版本中得到支持。以下是关于重赋值的详细说明和应用场景:

  •  重赋值的基本概念

- **重赋值符号**:使用“:=”而不是普通的等号“=”。
- **操作过程**:
  - 如果等号左侧的变量已经声明过,则首先删除该变量。
  - 然后根据等号右侧的变量类型和大小重新定义左侧变量,并进行赋值。
- **结果**:左侧变量的类型、维数大小、数值和元数据将完全由右侧变量决定。

  • 应用场景

- **循环中使用**:重赋值特别适用于循环中,因为数组的大小可能会在循环中发生变化。使用重赋值可以确保每次循环时变量的大小和类型都与新的数据一致.
- **动态数据处理**:在处理动态变化的数据时,重赋值可以方便地更新变量的大小和内容,而不需要手动删除和重新声明变量.

  • 例:

以下是一个简单的示例,展示了如何在循环中使用重赋值:

```ncl
x = (/1, 2, 3/)  ; 初始赋值
print(x)

do i = 1, 3
  x := (/i, i+1, i+2/)  ; 重赋值
  print(x)
end do
```

在这个例子中:

- 初始时,`x` 被赋值为一个包含三个元素的数组。
- 在循环中,每次使用 `x := (/i, i+1, i+2/)` 进行重赋值,`x` 的内容和大小都会根据新的表达式进行更新.
- 每次循环时,`x` 的内容都会被重新定义,确保其与新的数据一致.

  • 注意事项

- **版本兼容性**:重赋值功能仅在NCL版本6.1.1及之后版本中支持,因此在使用时需要确保NCL版本符合要求.
- **变量删除**:在重赋值过程中,左侧变量会被删除并重新定义,这意味着在重赋值之前,变量的所有信息(包括属性和坐标变量)都会被清除.
- **灵活性**:重赋值提供了更大的灵活性,特别是在处理动态数据时,但同时也需要注意变量的管理和维护,以避免潜在的错误.

4.7.1 值的重赋值

在NCL中,值的重赋值允许你将一个变量的类型和大小完全改变,而不仅仅是更新其数值。这种操作使用重赋值符号“:=”,并且不需要等号两边的变量具有相同的类型和大小。以下是对值的重赋值的详细解释和示例:

  • 值的重赋值的基本概念

- **重赋值符号**:使用“:=”而不是普通的等号“=”。
- **操作过程**:
  - 重赋值会删除左侧变量的当前定义(包括类型、大小、数值和元数据)。
  - 然后根据右侧的值重新定义左侧变量,并赋值。
- **结果**:左侧变量的类型、维数大小、数值和元数据将完全由右侧的值决定。

以下是一个示例,展示了如何使用值的重赋值来改变变量的类型和大小:

```ncl
a = (/ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 /)  ; 此时变量 a 为一个 1x11 的整型数组
print(a)

a := (/ (/"It ", "is "/), (/"string ", "now!"/) /)  ; 重赋值 a
print(a)
```

在这个例子中:

- 初始时,`a` 被赋值为一个包含11个整数的数组。
- 使用重赋值 `a := (/ (/"It ", "is "/), (/"string ", "now!"/) /)` 后,`a` 的类型从整型变为字符串,并且数组大小从一维的 1x11 变为二维的 2x2.
- 重赋值后,`a` 的内容和结构完全由右侧的字符串数组决定.

4.7.2 变量到变量的重赋值

在NCL中,变量到变量的重赋值是一个非常灵活的操作,它允许你将一个变量的类型、大小、数值和元数据完全改变为另一个变量的属性。以下是关于变量到变量重赋值的详细说明和示例:

  • 变量到变量重赋值的基本概念

- **重赋值符号**:使用“:=”而不是普通的等号“=”。
- **操作过程**:
  - 如果等号左侧的变量已经声明过,则首先删除该变量。
  - 然后根据等号右侧变量的类型、大小、数值和元数据重新定义左侧变量,并进行赋值。
- **结果**:左侧变量将完全继承右侧变量的所有属性,包括类型、维数、数值、命名维度、坐标变量和属性.

  • 例:

以下是一个示例,展示了如何在NCL中进行变量到变量的重赋值:

```ncl
; 创建 20×20 的字符串数组 a
a = new( (/20, 20/), string )

; 创建 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
print(a)

; 变量到变量重赋值,等号左边变量未声明
c := b
print(c)
```

  • 输出结果

- **变量 `a` 的输出**:
  ```
  Variable: a
  Type: float
  Total Size: 36 bytes
   9 values
  Number of Dimensions: 2
  Dimensions and sizes: [dim0 | 3] x [dim1 | 3]
  Coordinates: 
   dim0: [0.1..0.3]
   dim1: [10..1000]
  Number Of Attributes: 1
   units : none
  (0,0) 1
  (0,1) 2
  (0,2) 3
  (1,0) 4
  (1,1) 5
  (1,2) 6
  (2,0) 7
  (2,1) 8
  (2,2) 9
  ```

- **变量 `c` 的输出**:
  ```
  Variable: c
  Type: float
  Total Size: 36 bytes
   9 values
  Number of Dimensions: 2
  Dimensions and sizes: [dim0 | 3] x [dim1 | 3]
  Coordinates: 
   dim0: [0.1..0.3]
   dim1: [10..1000]
  Number Of Attributes: 1
   units : none
  (0,0) 1
  (0,1) 2
  (0,2) 3
  (1,0) 4
  (1,1) 5
  (1,2) 6
  (2,0) 7
  (2,1) 8
  (2,2) 9
  ```


5. 语句

在NCL中,语句是构成程序的基本元素,用于执行各种操作和控制程序的流程。根据功能的不同,NCL中的语句可以分为以下几类:

  •  块语句

- **定义**:块语句由一系列的语句组成,通常用花括号 `{}` 包围。
- **作用**:用于将多个语句组合在一起,作为一个整体执行,特别是在控制结构中使用。
-例:

  ```ncl
  {
    x = 5
    y = x * 2
    print(y)
  }
  ```

  • 条件表达式

1. **if-then**:
 


    ```ncl
    if (condition) then
      statement
    end if
    ```


  - **作用**:当条件 `condition` 为真时,执行 `statement`。

2. **if-then-else**:
  


    ```ncl
    if (condition) then
      statement1
    else
      statement2
    end if
    ```

  - **作用**:当条件 `condition` 为真时,执行 `statement1`;否则执行 `statement2`。
  

  •  循环语句

1.- **do**:

  
    ```ncl
    do i = start, end, step
      statement
    end do
    ```


  - **作用**:从 `start` 开始,每次增加 `step`,直到达到 `end`,执行 `statement`。
 

2. - **do-while**:
 


    ```ncl
    do
      statement
    end do while (condition)
    ```


  - **作用**:先执行 `statement`,然后检查条件 `condition`,如果为真则继续执行循环。
 

  • 其他语句

1. **赋值语句**:用于给变量赋值。
  ```ncl
  x = 5
  ```

2.**打印语句**:用于输出信息到控制台。
  ```ncl
  print("Hello, World!")
  ```

3.**函数调用语句**:用于调用函数执行特定操作。
  ```ncl
  result = myFunction(x, y)
  ```

5.1 块

在NCL中,块(block)是由一系列语句组成的代码段,通常用于组织和封装代码,使其更加清晰和易于管理。块的使用在自定义函数和程序中尤为重要。以下是对块的详细说明:

  • 块的基本结构

- **定义**:块以 `begin` 开始,以 `end` 结束。在 `begin` 和 `end` 之间的语句将被顺序执行。


  ```ncl
  begin
    语句 1
    语句 2
    ...
  end
  ```

  • 块的应用场景

- **自定义函数**:在定义自定义函数时,函数体就是一个块。例如:

  ```ncl
  function myFunction(x: float, y: float): float
  begin
    result = x + y
    return(result)
  end
  ```


  在这个例子中,`begin` 和 `end` 之间的语句组成了函数体,执行了计算和返回操作.

- **程序主体**:虽然在主程序中 `begin` 和 `end` 不是必须的,但使用它们可以使代码结构更加清晰。例如:

  ```ncl
  begin
    x = 5
    y = 10
    z = x + y
    print(z)
  end
  ```


  这种方式使得主程序的结构更加明确,便于阅读和维护.

  • 块的作用

- **代码组织**:块将相关的语句组织在一起,使得代码更加模块化和易于理解。
- **作用域管理**:在块内部定义的变量通常具有局部作用域,只在块内部有效,这有助于避免变量名冲突和提高代码的安全性.
- **逻辑封装**:块可以封装一系列逻辑操作,使得代码更加简洁和高效。

  •  注意事项

- **缩进和格式**:在编写块时,注意保持良好的缩进和格式,以提高代码的可读性。通常,`begin` 和 `end` 之间的语句会缩进一定的空格或制表符.
- **变量作用域**:在块内部定义的变量通常具有局部作用域,这意味着它们不能在块外部访问。这有助于避免变量名冲突和提高代码的安全性.
- **良好的编程习惯**:虽然在主程序中 `begin` 和 `end` 不是必须的,但使用它们是一个良好的编程习惯,可以使代码结构更加清晰和易于维护.

5.2 if语句

在NCL中,`if`语句用于根据逻辑表达式的真假来执行不同的代码块。虽然NCL没有直接支持`else if`,但可以通过嵌套`if`语句来实现类似的功能。以下是对`if`语句的详细说明和示例:

  •  If 语句的基本结构

- **基本形式**:

  ```ncl
  if (逻辑表达式) then
    [语句]
  else
    [语句]
  end if
  ```


  - **逻辑表达式**:一个返回布尔值的表达式,用于判断条件是否满足。
  - **语句**:当条件满足时执行的代码块,或者条件不满足时执行的代码块.

  • 实现 Else If

- **嵌套 If 语句**:通过将`if`和`else`嵌套在一起,可以在`end if`之前写在同一行,从而实现`else if`的效果.
 

  ```ncl
  x = 8
  if (x .eq. -6) then
    print("if-statement 1")
  else if (x .gt. 0 .and. x .lt. 5) then
    print("if-statement 2")
  else if (x .lt. 0) then
    print("if-statement 3")
  else
    print("if-statement 1 else")
  end if
  end if
  end if
  ```


  在这个例子中:
  - 首先判断`x .eq. -6`,如果不满足,则进入第一个`else`。
  - 在第一个`else`中,判断`x .gt. 0 .and. x .lt. 5`,如果不满足,则进入第二个`else`。
  - 在第二个`else`中,判断`x .lt. 0`,如果不满足,则执行最后一个`else`中的语句。

  • 输出结果

- **输出**:
  ```
  (0) if-statement 1 else
  ```


  - 在这个例子中,`x`的值为8,不满足任何一个`if`条件,因此执行最后一个`else`中的语句,输出`if-statement 1 else`.

  •  注意事项

- **逻辑表达式**:确保逻辑表达式正确无误,以避免逻辑错误.
- **代码可读性**:虽然嵌套`if`语句可以实现`else if`的效果,但过多的嵌套会使代码难以阅读和维护。尽量保持逻辑简洁.
- **缩进和格式**:使用适当的缩进和格式,使代码结构清晰,便于阅读和调试.

5.3 循环

在NCL中,循环语句用于重复执行一系列语句,直到满足特定条件为止。虽然NCL的循环执行效率相对较低,但在某些情况下仍然非常有用。以下是对NCL中两种循环形式的详细说明:

  • Do 循环

- **基本形式**:
  ```ncl
  do n=start, end [,stride]
    [语句]
  end do
  ```


  - **n**:循环变量。
  - **start**:循环的起始值。
  - **end**:循环的结束值。
  - **stride**:循环的步长(可选)。如果省略,默认为1。

  • 使用说明

  - 循环变量 `n` 从 `start` 开始,每次增加 `stride`,直到达到或超过 `end`。
  - 如果 `end` 的数值小于 `start` 的数值,则必须设定步长 `stride` 为负数,否则循环不会执行.

- **示例**:
  ```ncl
  do i = 1, 5
    print(i)
  end do
  ```


  这个例子将输出1到5的数字.

  • Do While 循环

- **基本形式**:
  ```ncl
  do while (逻辑表达式)
    [语句]
  end do
  ```


  - **逻辑表达式**:一个返回布尔值的表达式,用于判断循环是否继续执行.

  • 使用说明:

  - 首先检查逻辑表达式的值,如果为真,则执行循环体中的语句。
  - 然后再次检查逻辑表达式的值,如果仍为真,则继续执行循环体中的语句,直到逻辑表达式的值为假.

- **示例**:
  ```ncl
  i = 1
  do while (i <= 5)
    print(i)
    i = i + 1
  end do
  ```


  这个例子将输出1到5的数字.

  • 特殊语句

- **break**:中断并退出循环。

  - **示例**:
    ```ncl
    do i = 1, 10
      if (i == 5) then
        break
      end if
      print(i)
    end do
    ```


    这个例子将输出1到4的数字,当 `i` 等于5时,循环中断并退出.

- **continue**:跳过一次循环,开始下一次循环。

  - **示例**:
    ```ncl
    do i = 1, 10
      if (i == 5) then
        continue
      end if
      print(i)
    end do
    ```


    这个例子将输出1到4和6到10的数字,当 `i` 等于5时,跳过该次循环,不执行 `print(i)` 语句.

  • 提高程序执行效率的建议

- **尽量少用循环**:由于NCL的循环执行效率较低,建议在可能的情况下尽量使用矩阵运算符、NCL的内部函数或程序来代替循环.
- **使用外部函数**:对于计算密集型的任务,可以使用Fortran函数或C语言编写子函数,并使用wrapper将其载入NCL脚本,以提高程序的执行效率.

5.4 赋值/重赋值

在NCL中,变量的重赋值是一个重要的功能,尤其是在需要动态改变变量类型和大小的情况下。以下是对变量重赋值的详细说明和示例:

  • 变量重赋值的基本概念

- **传统赋值**:在NCL中,如果一个变量已经被声明并赋值,那么后续的赋值操作必须保持变量的类型和大小不变。如果需要改变变量的类型或大小,必须先删除该变量,然后重新声明和赋值。

  ```ncl
  var = "This is a string"  ; var 为字符串类型
  delete(var)
  var = (/ 1.0, 10.0, 15.0, 20.0 /)  ; var 重新定义为浮点型数组
  ```

- **重赋值表达式“:=”**:从NCL版本6.1.1开始,引入了重赋值表达式“:=”,它允许在不删除变量的情况下改变变量的类型、大小和形态。这使得代码更加简洁和灵活。

  ```ncl
  var = "This is a string"  ; var 为字符串变量
  var := (/ 1.0, 10.0, 15.0, 20.0 /)  ; var 重定义为浮点型数组
  ```

  •  重赋值的应用场景

- **循环中使用**:在循环中,变量的大小和形态可能会发生变化。使用重赋值可以方便地更新变量的大小和内容,而不需要每次删除和重新声明变量.

  ```ncl
  do i = 1, 5
    var := (/ i, i+1, i+2 /)
    print(var)
  end do
  ```


  在这个例子中,每次循环时,`var` 的大小和内容都会根据新的表达式进行更新.

- **动态数据处理**:在处理动态变化的数据时,重赋值可以方便地更新变量的类型和大小,适应不同的数据处理需求.


6. 输出

在NCL中,标准输出程序用于在屏幕上显示变量的值、信息和其他数据。以下是对这些标准输出程序的详细说明和示例:

  • print

- **功能**:输出变量的值或者一个表达式的值。
- **使用方法**:

  ```ncl
  x = 5
  print(x)  ; 输出变量 x 的值
  print(x + 1)  ; 输出表达式 x + 1 的值
  ```

  • printVarSummary

- **功能**:输出一个变量的大致信息,包括变量名、数据类型、维数大小、元数据等。
- **使用方法**:

  ```ncl
  x = 5
  printVarSummary(x)
  ```
- **输出示例**:
  ```
  Variable: x
  Type: integer
  Total Size: 4 bytes
   1 values
  Number of Dimensions: 0
  Coordinates: 
  ```

  • print_table

- **功能**:格式输出列表变量的所有要素。
- **使用方法**:

  ```ncl
  list = [/ 1, 2, 3, 4 /]
  print_table(list)
  ```
- **输出示例**:
  ```
  (0) 1
  (1) 2
  (2) 3
  (3) 4
  ```

  • printMinMax

- **功能**:输出数据变量的最大值和最小值。

- **使用方法**:
  ```ncl
  data = (/ 1.0, 2.0, 3.0, 4.0 /)
  printMinMax(data, 0)
  ```
- **输出示例**:
  ```
  Variable: data
  Type: float
  Total Size: 16 bytes
   4 values
  Number of Dimensions: 1
  Dimensions and sizes: [4]
  Coordinates: 
  (0) 1
  (1) 2
  (2) 3
  (3) 4
  Min: 1
  Max: 4
  ```

  • printFileVarSummary

- **功能**:输出一个文件变量的总体信息。

- **使用方法**:
  ```ncl
  file = addfile("data.nc", "r")
  printFileVarSummary(file, "varName")
  ```
- **输出示例**:
  ```
  Variable: varName
  Type: float
  Total Size: 16 bytes
   4 values
  Number of Dimensions: 1
  Dimensions and sizes: [4]
  Coordinates: 
  ```

  •  应用场景

- **程序调试**:`printVarSummary` 被大量应用于程序的调试中,因为它可以提供变量的详细信息,帮助开发者了解变量的状态和属性.
- **数据分析**:`printMinMax` 用于分析数据变量的最大值和最小值,有助于了解数据的范围和分布.
- **信息展示**:`print` 和 `print_table` 用于在屏幕上展示变量的值和列表变量的要素,方便用户查看和分析数据.

  • 注意事项

- **输出格式**:标准输出程序的输出格式通常为文本形式,适合在控制台或终端中查看.
- **数据类型支持**:这些程序支持多种数据类型,包括数值类型、字符串类型等,可以满足不同的输出需求.
- **调试信息**:在请求帮助或报告问题时,提供 `printVarSummary` 的输出信息将非常有帮助,因为它提供了变量的详细信息,便于他人了解问题的背景和原因.


7.保留的关键词

保留字(keywords)和内置函数的名称是预定义的,具有特定的语法和功能。因此,用户在定义变量、数组、列表、函数和程序等时,不能使用这些保留字或内置函数的名称。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值