求值顺序

求值顺序严格从左到右,由内到外,这是大多数其他强类型编程语言的典型做法:

  1. var s = ""
  2. proc p(arg: int): int =
  3. s.add $arg
  4. result = arg
  5. discard p(p(1) + p(2))
  6. doAssert s == "123"

赋值也不特殊,左边的表达式在右边的表达式之前被求值:

  1. var v = 0
  2. proc getI(): int =
  3. result = v
  4. inc v
  5. var a, b: array[0..2, int]
  6. proc someCopy(a: var int; b: int) = a = b
  7. a[getI()] = getI()
  8. doAssert a == [1, 0, 0]
  9. v = 0
  10. someCopy(b[getI()], getI())
  11. doAssert b == [1, 0, 0]

原由:与重载赋值或类似赋值的运算符保持一致,a = b 可以理解为 performSomeCopy(a, b) 。

然而,”求值顺序” 的概念只有在代码被规范化之后才适用。规范化涉及到模板的扩展和参数的重新排序,这些参数已经被传递给命名参数。

  1. var s = ""
  2. proc p(): int =
  3. s.add "p"
  4. result = 5
  5. proc q(): int =
  6. s.add "q"
  7. result = 3
  8. # 由于模板扩展语义,求值顺序是 'b' 在 'a' 之前。
  9. template swapArgs(a, b): untyped =
  10. b + a
  11. doAssert swapArgs(p() + q(), q() - p()) == 6
  12. doAssert s == "qppq"
  13. # 求值顺序不受命名参数的影响:
  14. proc construct(first, second: int) =
  15. discard
  16. # 'p' 在 'q' 之前求值!
  17. construct(second = q(), first = p())
  18. doAssert s == "qppqpq"

原由: 这比其他设想的替代方案容易实现。