将元组的值作为参数传递给 proc
Pass values of tuple as prarameters to proc
我有一个程序叫做:
proc fill(image: Pixels, r, g, b, a: uint8
它需要 4 个 uint8 值作为参数传递给它以用颜色填充图像。
我还有一个名为 green 的元组:
let green = (0.uint8, 255.uint8, 0.uint8, 255.uint8)
我希望我可以像这样调用过程,或类似的:image.fill(green)
但是类型不匹配的错误。
有没有比写ìmage.fill(green[0], green[1], green[2], green[3])
更优雅的方式?
可能最简单的方法是将 fill()
函数包装在另一个接受元组参数的函数中。这样的事情会起作用,重载 fill()
以采用新的参数类型,只要 fill()
的 return 类型和重载的 fill()
匹配:
proc fill(image: Pixels, color: tuple[r, g, b, a: uint8]): void =
fill(image, color.r, color.g, color.b, color.a)
然后使用元组参数调用 fill()
将调用适当的版本:
myImage.fill(green)
如果你经常想执行这种元组解包(比如 python 中的 f(*a)
/ lisp(s) 中的 apply
),你可以为这种解包写一些宏(并且柯里化,因为您的参数不存在于 fill
所需的元组中)。
拆包/传播/应用:
macro `..*`(f, t: typed): auto =
var args: seq[NimNode] = @[]
let ty = t.getTypeImpl
for e in ty:
args.add(newDotExpr(t, e[0]))
result = newCall(f, args)
给你开箱;但是,您还需要一个 curry
来完成 python 中 functools.partial
的等效操作(对于第一个参数 image
)
柯里化:
macro curry(f: typed; args: varargs[untyped]): untyped =
let ty = f.getType
let tyi = f.getTypeImpl[0]
assert($ty[0] == "proc", "first param is not a function")
let n_remaining = ty.len - 2 - args.len
assert n_remaining > 0, "cannot curry all the parameters"
var callExpr = newCall(f)
args.copyChildrenTo callExpr
var params: seq[NimNode] = @[]
params.add ty[1]
for i in 0..<n_remaining:
let loc = i+2+args.len
let name = $(tyi[loc - 1][0])
let param = ident(name)
params.add newIdentDefs(param, ty[loc])
callExpr.add param
result = newProc(procType = nnkLambda, params = params, body = callExpr)
proc baz(o: string, r, g, b, a: uint8): string = $g
let green: tuple[r, g, b, a: uint8] = (0.uint8, 255.uint8, 0.uint8, 0.uint8)
echo (baz.curry("example") ..* green)
我不认为你可以制作一个对元组本身起作用的宏而不是fill
(或函数)和元组都,因为宏可以不要在 AST 中朝这个方向“向上”。
我有一个程序叫做:
proc fill(image: Pixels, r, g, b, a: uint8
它需要 4 个 uint8 值作为参数传递给它以用颜色填充图像。
我还有一个名为 green 的元组:
let green = (0.uint8, 255.uint8, 0.uint8, 255.uint8)
我希望我可以像这样调用过程,或类似的:image.fill(green)
但是类型不匹配的错误。
有没有比写ìmage.fill(green[0], green[1], green[2], green[3])
更优雅的方式?
可能最简单的方法是将 fill()
函数包装在另一个接受元组参数的函数中。这样的事情会起作用,重载 fill()
以采用新的参数类型,只要 fill()
的 return 类型和重载的 fill()
匹配:
proc fill(image: Pixels, color: tuple[r, g, b, a: uint8]): void =
fill(image, color.r, color.g, color.b, color.a)
然后使用元组参数调用 fill()
将调用适当的版本:
myImage.fill(green)
如果你经常想执行这种元组解包(比如 python 中的 f(*a)
/ lisp(s) 中的 apply
),你可以为这种解包写一些宏(并且柯里化,因为您的参数不存在于 fill
所需的元组中)。
拆包/传播/应用:
macro `..*`(f, t: typed): auto =
var args: seq[NimNode] = @[]
let ty = t.getTypeImpl
for e in ty:
args.add(newDotExpr(t, e[0]))
result = newCall(f, args)
给你开箱;但是,您还需要一个 curry
来完成 python 中 functools.partial
的等效操作(对于第一个参数 image
)
柯里化:
macro curry(f: typed; args: varargs[untyped]): untyped =
let ty = f.getType
let tyi = f.getTypeImpl[0]
assert($ty[0] == "proc", "first param is not a function")
let n_remaining = ty.len - 2 - args.len
assert n_remaining > 0, "cannot curry all the parameters"
var callExpr = newCall(f)
args.copyChildrenTo callExpr
var params: seq[NimNode] = @[]
params.add ty[1]
for i in 0..<n_remaining:
let loc = i+2+args.len
let name = $(tyi[loc - 1][0])
let param = ident(name)
params.add newIdentDefs(param, ty[loc])
callExpr.add param
result = newProc(procType = nnkLambda, params = params, body = callExpr)
proc baz(o: string, r, g, b, a: uint8): string = $g
let green: tuple[r, g, b, a: uint8] = (0.uint8, 255.uint8, 0.uint8, 0.uint8)
echo (baz.curry("example") ..* green)
我不认为你可以制作一个对元组本身起作用的宏而不是fill
(或函数)和元组都,因为宏可以不要在 AST 中朝这个方向“向上”。