过程

大多数编程语言中称之为 methods:idx “方法”或 functions:idx “函数”,在 Nim 中则称为 procedures:idx “过程”。 过程声明由标识符、零个或多个形参、返回值类型和代码块组成,形参声明为由逗号或分号分隔的标识符列表。形参由 : typename 给出一个类型。 该类型适用于紧接其之前的所有参数,直到参数列表的开头的分号分隔符或已经键入的参数。 分号可使类型和后续标识符的分隔更加清晰。

  1. # 只使用逗号
  2. proc foo(a, b: int, c, d: bool): int
  3. # 使用分号进行显式的区分
  4. proc foo(a, b: int; c, d: bool): int
  5. # 会失败: a是无类型的, 因为 ';' 为停止类型传播
  6. proc foo(a; b: int; c, d: bool): int

可以使用默认值声明参数,如果调用者没有为参数提供值,则使用该默认值,每次调用函数时,都会重新计算该值。

  1. # b是可选的, 默认值为 47 。
  2. proc foo(a: int, b: int = 47): int

参数可以声明为可变的,过程允许通过类型修饰符 var 来修饰参数。

  1. # 通过第二个参数 "返回" 一个值给调用者
  2. # 请注意, 该函数实际没有使用真实的返回值(即 void)
  3. proc foo(inp: int, outp: var int) =
  4. outp = inp + 47

如果 proc 声明没有过程体, 则是 forward “前置”声明。 如果 proc 返回一个值,那么过程体可以访问一个名为 result 的隐式变量。 过程可能会重载,重载解析算法会确定哪个 proc 是参数的最佳匹配。 示例:

  1. proc toLower(c: char): char = # toLower 字符
  2. if c in {'A'..'Z'}:
  3. result = chr(ord(c) + (ord('a') - ord('A')))
  4. else:
  5. result = c
  6. proc toLower(s: string): string = # 字符串 toLower
  7. result = newString(len(s))
  8. for i in 0..len(s) - 1:
  9. result[i] = toLower(s[i]) # 为字符调用 toLower ,不递归!

调用过程可以通过多种方式完成:

  1. proc callme(x, y: int, s: string = "", c: char, b: bool = false) = ...
  2. # 带位置参数的调用 # 参数绑定:
  3. callme(0, 1, "abc", '\t', true) # (x=0, y=1, s="abc", c='\t', b=true)
  4. # 使用命名参数和位置参数调用:
  5. callme(y=1, x=0, "abd", '\t') # (x=0, y=1, s="abd", c='\t', b=false)
  6. # 带命名参数的调用(顺序无关):
  7. callme(c='\t', y=1, x=0) # (x=0, y=1, s="", c='\t', b=false)
  8. # 作为命令语句调用:不需要():
  9. callme 0, 1, "abc", '\t' # (x=0, y=1, s="abc", c='\t', b=false)

过程可以递归地调用自身。

Operators “操作符”是将特定运算符作为标识符的过程:

  1. proc `$` (x: int): string =
  2. # 将整数转换为字符串;这是一个前缀操作符。
  3. result = intToStr(x)

具有一个参数的操作符是前缀操作符,有两个参数的运算符是中缀操作符。(但是, 解析器将这些与操作符在表达式中的位置区分开来。) 无法声明后缀运算符,所有后缀运算符都是内置的,由语法明确指出。

任何操作符都可以像普通的 proc 一样用 `opr` 表示法调用。(因此操作符可以有两个以上的参数):

  1. proc `*+` (a, b, c: int): int =
  2. # 乘 和 加
  3. result = a * b + c
  4. assert `*+`(3, 4, 6) == `+`(`*`(a, b), c)