将元组的值作为参数传递给 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 中朝这个方向“向上”。