模板

模板是一种简单的替换机制,可以在Nim的抽象语法树上运行。模板在编译器的语义传递中处理。它们与语言的其余部分很好地集成,并且没有C的预处理器宏缺陷。

调用 模板,将其作为过程。

Example:

  1. template `!=` (a, b: untyped): untyped =
  2. # 此定义存在于system模块中
  3. not (a == b)
  4.  
  5. assert(5 != 6) # 编译器将其重写为:assert(not(5 == 6))

!=, >, >=, in, notin, isnot 操作符实际是模板:这对重载自动可用的 == , != 操作符有好处。 (除了IEEE浮点数 - NaN打破了基本的布尔逻辑。)

a > b 变换成 b < aa in b 变换成 contains(b, a)notinisnot 顾名思义。

模板对于延迟计算特别有用。看一个简单的日志记录过程:

  1. const
  2. debug = true
  3.  
  4. proc log(msg: string) {.inline.} =
  5. if debug: stdout.writeLine(msg)
  6.  
  7. var
  8. x = 4
  9. log("x has the value: " & $x)

这段代码有一个缺点:如果 debug 有一天设置为false,那么仍然会执行 $& 操作! (程序的参数求值是 急切 )。

log 过程转换为模板解决了这个问题:

  1. const
  2. debug = true
  3.  
  4. template log(msg: string) =
  5. if debug: stdout.writeLine(msg)
  6.  
  7. var
  8. x = 4
  9. log("x has the value: " & $x)

参数的类型可以是普通类型,也可以是元类型 untypedtypedtypetype 表示只有一个类型符号可以作为参数给出, untyped 表示符号查找,并且在表达式传递给模板之前不执行类型解析。

如果模板没有显式返回类型,则使用 void 与过程和方法保持一致。

要将一个语句块传递给模板,请使用 untyped 作为最后一个参数:

  1. template withFile(f: untyped, filename: string, mode: FileMode,
  2. body: untyped) =
  3. let fn = filename
  4. var f: File
  5. if open(f, fn, mode):
  6. try:
  7. body
  8. finally:
  9. close(f)
  10. else:
  11. quit("cannot open: " & fn)
  12.  
  13. withFile(txt, "ttempl3.txt", fmWrite):
  14. txt.writeLine("line 1")
  15. txt.writeLine("line 2")

在示例中,两个 writeLine 语句绑定到 body 参数。 withFile 模板包含样板代码,有助于避免忘记关闭文件的常见错误。 注意 let fn = filename 语句如何确保 filename 只被求值一次。

示例: 提升过程

  1. import math
  2.  
  3. template liftScalarProc(fname) =
  4. ## 使用一个标量参数提升一个proc并返回一个
  5. ## 标量值(例如 ``proc sssss[T](x: T): float``),
  6. ## 来提供模板过程可以处理单个seq[T]形参或嵌套seq[seq[]]或同样的类型
  7. ##
  8. ## .. code-block:: Nim
  9. ## liftScalarProc(abs)
  10. ## 现在 abs(@[@[1,-2], @[-2,-3]]) == @[@[1,2], @[2,3]]
  11. proc fname[T](x: openarray[T]): auto =
  12. var temp: T
  13. type outType = type(fname(temp))
  14. result = newSeq[outType](x.len)
  15. for i in 0..<x.len:
  16. result[i] = fname(x[i])
  17.  
  18. liftScalarProc(sqrt) # 让sqrt()可以用于序列
  19. echo sqrt(@[4.0, 16.0, 25.0, 36.0]) # => @[2.0, 4.0, 5.0, 6.0]