如何在 Nim 中创建变量别名?

wu-*_*lee 2 nim-lang

我是 Nim 的新手,所以这可能是一个迟钝的问题,但是为了简化代码,如何创建速记别名变量?

例如:

import sdl2
import sdl2.gfx

type
  Vector[T] = object
    x, y: T

  Ball = object
    pos: Vector[float]

  Game = ref object
    renderer: RendererPtr
    ball: array[10, Ball]

proc render(game: Game) =
  # ...

  # Render the balls
  for ix in low(game.ball)..high(game.ball):
    var ball : ref Ball = game.ball[ix]
    game.renderer.filledCircleRGBA(
        int16(game.renderer.ball[ix].pos.x),
        int16(game.renderer.ball[ix].pos.y),
        10, 100, 100, 100, 255)

  # ...
Run Code Online (Sandbox Code Playgroud)

而不是最后一部分,我想使用较短的别名来访问球位置:

  # Update the ball positions
  for ix in low(game.ball)..high(game.ball):
    ??? pos = game.ball[ix].pos
    game.renderer.filledCircleRGBA(
        int16(pos.x),
        int16(pos.y),
        10, 100, 100, 100, 255)
Run Code Online (Sandbox Code Playgroud)

但是,如果我使用 avar代替???,那么我似乎在 中创建了一个副本pos,这意味着原始文件没有更新。Aref是不允许的,let也不会让我改变它。

这似乎是很自然的事情,所以如果 Nim 不让你这样做,我会感到惊讶,我只是在手册或教程中看不到任何内容。

[后来] 好吧,除了“滥用”ptr来实现这一点,但我认为ptr除了 C API 互操作性之外,不鼓励使用。

我希望的是类似于 Lisp/Haskell 的let*构造......

PMu*_*nch 6

另一种可能更像 Nim 的解决方案是使用模板。Nim 中的模板只是 AST 级别的简单替换。因此,如果您创建几个这样的模板:

template posx(index: untyped): untyped = game.ball[index].pos.x.int16
template posy(index: untyped): untyped = game.ball[index].pos.y.int16
Run Code Online (Sandbox Code Playgroud)

您现在可以将代码替换为:

proc render(game: Game) =
  # Render the balls
  for ix in low(game.ball)..high(game.ball):
    var ball : ref Ball = game.ball[ix]
    game.renderer.filledCircleRGBA(
      posx(ix),
      posy(ix),
      10, 100, 100, 100, 255)
Run Code Online (Sandbox Code Playgroud)

这将在编译时转换为您的原始代码,并且不会带来任何开销。它还将保持与原始代码相同的类型安全性。

当然,如果这是你经常做的事情,你可以创建一个模板来创建模板:

template alias(newName: untyped, call: untyped) =
  template newName(): untyped = call
Run Code Online (Sandbox Code Playgroud)

然后可以在您的代码中像这样使用它:

proc render(game: Game) =
  # Render the balls
  for ix in low(game.ball)..high(game.ball):
    var ball : ref Ball = game.ball[ix]
    alias(posx, game.ball[ballIndex].pos.x.int16)
    alias(posy, game.ball[ballIndex].pos.y.int16)
    game.renderer.filledCircleRGBA(
      posx(ix),
      posy(ix),
      10, 100, 100, 100, 255)
Run Code Online (Sandbox Code Playgroud)

如您所见,该解决方案只有在多次使用时才真正有用。另请注意,由于别名模板在 for 循环中展开,因此创建的模板也将在其中限定范围,因此可以很好地共享名称。

当然,在游戏设置中可能更正常的做法是使用更面向对象的方法(恕我直言,OO 真正有意义的少数情况之一,但这是另一个讨论)。如果您为球类型创建程序,您可以使用{.this: self.}编译指示对其进行注释以节省一些输入:

type
  A = object
    x: int

{.this: self.}
proc testproc(self: A) =
  echo x # Here we can acces x without doing self.x

var t = A(x: 10)
t.testproc()
Run Code Online (Sandbox Code Playgroud)