macros - 将宏变量传递给函数进行插值

标签 macros julia metaprogramming hygiene

我正在尝试编写一个宏来计算表达式,然后将其与几个值进行比较。 在这篇文章中,我已将问题简化为一个较小的示例。

macro small_bad(item)
    quote
        $(use_val(esc(item)))
    end
end

function use_val(val)
    quote
        if $val == 1
            1
        elseif $val == 2
            2
        else
            -1
        end
    end
end

因为我不想多次计算 expr,所以我想将其保存在变量中。 所以我尝试了这个:

macro small_good(item)
    quote
        begin
            val = $(esc(item))
            $(begin
                  use_val(val)
              end)
        end
    end
end

但后来我发现 val@small_good 的插值中未定义。

我也尝试传递 use_val(:val) 但这也失败,因为宏系统将重命名 val 为其他内容。

我怎样才能实现这个目标?

编辑: 鉴于第一个答案,我在实际代码中尝试了这个

macro match(item, arms)
    var = gensym(:var)
    quote
        let $var = $(esc(item))
            $(begin
                code = :nothing
                for e in reverse(filter((e) -> e isa Expr, arms.args))
                    code = make_match(var, e, code)
                end
                code
            end)
        end
    end
end

并得到UndefVarError:##var#253未定义

gist with the full code here

免责声明:我知道 @match 宏已在 Match.jl 包中实现,我正在重新实现它的一个子集作为学习练习

编辑2:

我明白了。使用 François Févotte 的建议后,我现在必须更改 use_val 的真实版本,它实际上是在执行 $(esc(val)) 而不是 $val >.

我的错误是没有包含该细节。将更新要点以反射(reflect)这一点

最佳答案

如果我明白你想要什么,这应该可行:

macro small(item)
    var = gensym(:var)
    quote
        let $var = $(esc(item))
            $(use_val(var))
        end
    end
end

function use_val(val)
    quote
        if $val == 1
            1
        elseif $val == 2
            2
        else
            -1
        end
    end
end

它扩展为:

julia> using MacroTools
julia> MacroTools.@expand @small myexpr
quote
    let octopus = myexpr
        begin
            if octopus == 1
                1
            elseif octopus == 2
                2
            else
                -1
            end
        end
    end
end

并且既不卫生也不存在多重评估问题:

# Testing in a local scope introduced by `let`
# is a good way to check for hygiene issues
julia> let arg = [1]
           @small pop!(arg)
       end
1




现在我猜你原来问题的很多实质内容在 MWE 创建过程中已经丢失,因为所有这些本质上等同于:

julia> function small(val)
           if val == 1
               1
           elseif val == 2
               2
           else
               -1
           end
       end
small (generic function with 1 method)

julia> let args = [1]
           small(pop!(args))
       end
1

关于macros - 将宏变量传递给函数进行插值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61942618/

相关文章:

dataframe - 可在 Julia 的多个列中进行透视

dataframe - 在 Julia DataFrames 中拆分字符串会出错

c++ - default_delete 的部分特化

c++ - 在 C++ 中绑定(bind)到具有未知数量参数的函数

rust - 将新函数名称放入内部宏

macros - 语法规则中如何使用矢量模式?

macros - 使用线程宏时命名函数和匿名函数之间的奇怪区别

c - 在 C 中对条件编译的枚举进行字符串化

machine-learning - Julia ReverseDiff : how to take a gradient w. r.t.仅输入的子集?

使用参数 vector 调用函数的 C++ 模板