模板
模板是一种简单的替换机制,可以在Nim的抽象语法树上运行。模板在编译器的语义传递中处理。它们与语言的其余部分很好地集成,并且没有C的预处理器宏缺陷。
要 调用 模板,将其作为过程。
Example:
- template `!=` (a, b: untyped): untyped =
- # 此定义存在于system模块中
- not (a == b)
- assert(5 != 6) # 编译器将其重写为:assert(not(5 == 6))
!=, >, >=, in, notin, isnot 操作符实际是模板:这对重载自动可用的 == , != 操作符有好处。 (除了IEEE浮点数 - NaN打破了基本的布尔逻辑。)
a > b 变换成 b < a 。 a in b 变换成 contains(b, a) 。 notin 和 isnot 顾名思义。
模板对于延迟计算特别有用。看一个简单的日志记录过程:
- const
- debug = true
- proc log(msg: string) {.inline.} =
- if debug: stdout.writeLine(msg)
- var
- x = 4
- log("x has the value: " & $x)
这段代码有一个缺点:如果 debug 有一天设置为false,那么仍然会执行 $ 和 & 操作! (程序的参数求值是 急切 )。
将 log 过程转换为模板解决了这个问题:
- const
- debug = true
- template log(msg: string) =
- if debug: stdout.writeLine(msg)
- var
- x = 4
- log("x has the value: " & $x)
参数的类型可以是普通类型,也可以是元类型 untyped , typed 或 type 。 type 表示只有一个类型符号可以作为参数给出, untyped 表示符号查找,并且在表达式传递给模板之前不执行类型解析。
如果模板没有显式返回类型,则使用 void 与过程和方法保持一致。
要将一个语句块传递给模板,请使用 untyped 作为最后一个参数:
- template withFile(f: untyped, filename: string, mode: FileMode,
- body: untyped) =
- let fn = filename
- var f: File
- if open(f, fn, mode):
- try:
- body
- finally:
- close(f)
- else:
- quit("cannot open: " & fn)
- withFile(txt, "ttempl3.txt", fmWrite):
- txt.writeLine("line 1")
- txt.writeLine("line 2")
在示例中,两个 writeLine 语句绑定到 body 参数。 withFile 模板包含样板代码,有助于避免忘记关闭文件的常见错误。 注意 let fn = filename 语句如何确保 filename 只被求值一次。
示例: 提升过程
- import math
- template liftScalarProc(fname) =
- ## 使用一个标量参数提升一个proc并返回一个
- ## 标量值(例如 ``proc sssss[T](x: T): float``),
- ## 来提供模板过程可以处理单个seq[T]形参或嵌套seq[seq[]]或同样的类型
- ##
- ## .. code-block:: Nim
- ## liftScalarProc(abs)
- ## 现在 abs(@[@[1,-2], @[-2,-3]]) == @[@[1,2], @[2,3]]
- proc fname[T](x: openarray[T]): auto =
- var temp: T
- type outType = type(fname(temp))
- result = newSeq[outType](x.len)
- for i in 0..<x.len:
- result[i] = fname(x[i])
- liftScalarProc(sqrt) # 让sqrt()可以用于序列
- echo sqrt(@[4.0, 16.0, 25.0, 36.0]) # => @[2.0, 4.0, 5.0, 6.0]