将宏变量传递给函数进行插值
Passing macro variable to function for interpolation
我正在尝试编写一个宏来计算一个表达式,然后将它与一些值进行比较。
为此,我已将问题简化为一个较小的示例 post。
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 not defined
免责声明:我知道 @match
宏已经在 Match.jl 包中实现,我正在重新实现它的一个子集作为学习练习
编辑 2:
我明白了。在使用了 François Févotte 的建议后,我现在不得不更改 use_val
的真实版本,它实际上是 $(esc(val))
而不是 $val
.
我的错误是没有包括那个细节。将更新要点以反映这一点
如果我明白你想要什么,这应该可行:
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
我正在尝试编写一个宏来计算一个表达式,然后将它与一些值进行比较。 为此,我已将问题简化为一个较小的示例 post。
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 not defined
免责声明:我知道 @match
宏已经在 Match.jl 包中实现,我正在重新实现它的一个子集作为学习练习
编辑 2:
我明白了。在使用了 François Févotte 的建议后,我现在不得不更改 use_val
的真实版本,它实际上是 $(esc(val))
而不是 $val
.
我的错误是没有包括那个细节。将更新要点以反映这一点
如果我明白你想要什么,这应该可行:
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