将字符串内插值传递给宏

Passing string interpolated values into macro

我对这段代码的问题是当传递一个内插字符串时宏失败:

macro t(a, b, c)
    quote
        println(:c, " = " , $c)
        ($a, $b, $c)
    end
end

function test()
    # works:
    @t 1 2 3

    # doesn't work:
    x = π
    @t 1 2 "x is $x"
end

test()
test()
c = 3   # works
ERROR: UndefVarError: x not defined

因此在 t 内插值 x 值不可用。是否有解决方案,或者在这里使用宏只是个坏主意?

此类宏的计算方式是,任何非宏函数本身局部的变量名都被视为全局变量名。没有使用宏点的调用范围。

所以这有效:

macro t(a, b, c)
    quote
        println(:c, " = " , $c)
        ($a, $b, $c)
    end
end

function test()
    # works:
    @t 1 2 3

    x = π  # this x is not seen inside the macro's scope
    @t 1 2 "x is $x"
end

x = 2π  # this one is global

test()

为了达到你想要的效果,你需要使用 esc:

macro t(a, b, c)
    quote
        local a1 = $(esc(a))
        local b1 = $(esc(b))
        local c1 = $(esc(c))
        println(:c, " = ", c1)
        (a1, b1, c1)
    end
end

请注意,我定义了变量 a1b1c1 一次,然后重新使用它们。原因是如果你写了类似这样的东西:

macro t(a, b, c)
    quote
        println(:c, " = ", $(esc(c)))
        ($(esc(a)), $(esc(b)), $(esc(c)))
    end
end

这是很自然的事情(或者可能不是 :)),你会遇到问题,因为 c 会被评估两次,例如。在这个例子中:

julia> macro t(a, b, c)
           quote
               println(:c, " = ", $(esc(c)))
               ($(esc(a)), $(esc(b)), $(esc(c)))
           end
       end
@t (macro with 1 method)

julia> function test()
           @t 1 2 rand()
       end
test (generic function with 1 method)

julia> test()
c = 0.03771143425073453
(1, 2, 0.1819496773810383)

请注意,打印了不同的值并返回了不同的值。此问题存在于您的原始宏中(即使它使用@Bill 指出的全局变量):

julia> macro t(a, b, c)
           quote
               println(:c, " = " , $c)
               ($a, $b, $c)
           end
       end
@t (macro with 1 method)

julia> @t 1 2 rand()
c = 0.7021554643798531
(1, 2, 0.6363717837673994)

总的来说,我认为 @code_lowered@macroexpand 宏在您使用元编程调试代码时会很有用。