【在主画面加入捷径】
       
【选择语系】
繁中 简中

Nim 语言程序教学:模板 (Template)

【赞助商连结】

    模版 (template) 是一种较为高阶的语法特性,透过程序代码代换来改写程序,好处是可以跳脱类型的限制。模版有点类似于 C 的前置处理器 (preprocessor),Nim 编译器会将使用到模版的程序代码代换掉,然后继续编译程序。Nim 的模版也是用 Nim 语言撰写,程序设计者不需额外学习新的语法。

    撰写模版

    见以下例子:

    template `!=` (a, b: untyped): untyped =
      not (a == b)
    
    assert(5 != 6)
    

    在这个例子中,5 != 6 会代换为 not (5 == 6)。在 Nim 里面,有些语法其实是用模板制作的。

    Untyped vs. Typed

    在撰写模版时,可指定变量为 untypedtyped,也可以指定为特定类型,类型的指定会有一些微妙的差别。以下的程序可以顺利执行:

    template declareInt(x: untyped) =
      var x: int
    
    declareInt(x) # valid
    x = 3
    

    但是以下的程序会引发错误:

    template declareInt(x: typed) =
      var x: int
    
    declareInt(x) # invalid, because x has not been declared and so has no type
    

    传递区块

    除了传递单一变量,模板也可以传递程序代码区块,撰写模板时,最后一个参数可视为程序代码区块;在调用该模版时,加入 : (引号) 即可。如下例:

    template withFile(f, fn, mode, actions: untyped): untyped =
      var f: File
      if open(f, fn, mode):
        try:
          actions
        finally:
          close(f)
      else:
        quit("cannot open: " & fn)
    
    withFile(txt, "ttempl3.txt", fmWrite):
      txt.writeLine("line 1")
      txt.writeLine("line 2")
    

    Hygienic

    Nim 的模板是卫生的 (hygienic),意指 Nim 的模板在代换的过程中不会将新的变量置入目前的模板中。

    我们现在写了一个模块 a:

    # module a: a.nim
    var
      lastId = 0
    
    template genId*: untyped =
      inc(lastId)
      lastId
    

    我们引用模块 a 时,lastId 不会置入目前的模块中:

    # main module: main.nim
    import a
    
    echo genId()
    
    # Error
    echo lastId
    

    建立识别字

    模板可以用反引号建立识别字。如下例:

    template typedef(name: untyped, typ: typedesc) =
      type
        `T name`* {.inject.} = typ
        `P name`* {.inject.} = ref `T name`
    
    typedef(myint, int)
    var x: PMyInt