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

时间:2018-03-20 23:57:48

标签: nim

我是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)

  # ...

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

  # 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)

但是,如果我使用var代替???,那么我似乎在pos中创建了一个副本,这意味着原始版本未更新。 <{1}}是不允许的,ref不允许我改变它。

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

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

我希望的是像Lisp / Haskell的ptr构造......

2 个答案:

答案 0 :(得分:4)

另一种解决方案,可能更像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

您现在可以用以下代码替换您的代码:

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)

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

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

template alias(newName: untyped, call: untyped) =
  template newName(): untyped = call

然后可以在代码中使用它:

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)

正如您所看到的那样,只有多次使用该解决方案才真正有用。另请注意,由于别名模板在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()

答案 1 :(得分:2)

rules to creating a reference所以您可能需要使用不安全的指针进入Game变量所拥有的内存,如下所示:

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

  RendererPtr = ref object
    dummy: int

  Ball = object
    pos: Vector[float]

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

proc filledCircleRGBA(renderer: RendererPtr, x, y: int16,
    a, b, c, d, e: int) =
  discard

proc render(game: Game) =
  # Render the balls
  for ix in low(game.ball)..high(game.ball):
    let ball: ptr Ball = addr game.ball[ix]
    game.renderer.filledCircleRGBA(
        int16(ball.pos.x), int16(ball.pos.y),
        10, 100, 100, 100, 255)

请注意,let仅适用于本地ball别名,您仍然可以改变其指向的内容。减少键入的另一种方法可能是在filledCircleRGBA周围编写一个包装器,它接受Game和要呈现的Ball的索引:

proc filledCircleRGBA(renderer: RendererPtr, x, y: int16,
    a, b, c, d, e: int) =
  discard

proc filledCircleRGBA(game: Game, ballIndex: int,
    a, b, c, d, e: int) =
  filledCircleRGBA(game.renderer,
    game.ball[ballIndex].pos.x.int16,
    game.ball[ballIndex].pos.y.int16,
    a, b, c, d, e)

proc render(game: Game) =
  # Render the balls
  for ix in low(game.ball)..high(game.ball):
    game.filledCircleRGBA(ix, 10, 100, 100, 100, 255)

根据您的性能需求,您可以inline包装proc或将其转换为template,保证不会产生转发费用。