如何在 Nim 中创建变量别名?
How do I create an variable alias in 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
中创建了一个副本,这意味着原始文件没有更新。 ref
是不允许的,let
不会让我改变它。
这似乎是很自然的事情,所以如果 Nim 不让你这样做我会感到惊讶,我在手册或教程中看不到任何内容。
[later] 嗯,除了 "abusing" ptr
来实现这一点,但我曾认为 ptr
的使用是不鼓励的,除了 C API 互操作性。
我希望的是 Lisp/Haskell 的 let*
构造...
有 所以你可能需要使用一个不安全的指针指向 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 that wrapper proc
or turn it into a template保证没有 proc 调用开销。
另一种解决方案,也许更多 Nim-like,是使用模板。 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)
这将在 compile-time 上转换为您的原始代码,并且不会产生任何开销。它还将保持与原始代码相同的type-safety。
当然,如果这是您发现自己经常做的事情,您可以创建一个模板来创建模板:
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()
我是 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
中创建了一个副本,这意味着原始文件没有更新。 ref
是不允许的,let
不会让我改变它。
这似乎是很自然的事情,所以如果 Nim 不让你这样做我会感到惊讶,我在手册或教程中看不到任何内容。
[later] 嗯,除了 "abusing" ptr
来实现这一点,但我曾认为 ptr
的使用是不鼓励的,除了 C API 互操作性。
我希望的是 Lisp/Haskell 的 let*
构造...
有 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 that wrapper proc
or turn it into a template保证没有 proc 调用开销。
另一种解决方案,也许更多 Nim-like,是使用模板。 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)
这将在 compile-time 上转换为您的原始代码,并且不会产生任何开销。它还将保持与原始代码相同的type-safety。
当然,如果这是您发现自己经常做的事情,您可以创建一个模板来创建模板:
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()