从朱莉娅(Julia)中生成的功能中调用宏

发布于 2025-02-12 18:07:42 字数 878 浏览 1 评论 0 原文

我一直在朱莉娅(Julia)中遇到生成的功能,并且遇到了一个奇怪的问题,我不完全理解:我的最终目标将涉及从生成的函数中调用宏(更具体地说 @tullio ) (执行一些取决于输入张量的张量收缩)。但是我一直在遇到问题,我缩小了从生成的函数中调用宏。

为了说明问题,让我们考虑一个非常简单的示例,也会失败:

macro my_add(a,b) 
    return :($a + $b)
end

function add_one_expr(x::T) where T
    y = one(T)
    return :( @my_add($x,$y) )
end

@generated function add_one_gen(x::T) where T
    y = one(T)
    return :( @my_add($x,$y) )
end

使用这些声明,我发现 eval(add_one_expr(2.0)) 效果按预期和返回和表达式工作

:(@my_add 2.0 1.0)

,并正确评估 3.0

但是评估 add_one_gen(2.0)返回以下错误:

MethodError: no method matching +(::Type{Float64}, ::Float64)

进行一些研究,我发现 @generated 实际上产生了两个代码,而在一个类型中,变量的类型只能被使用。我认为这是这里正在发生的事情,但我根本不了解发生了什么。它必须是宏和生成功能之间的一些怪异相互作用。

有人可以解释和/或提出解决方案吗?谢谢你!

I have been messing around with generated functions in Julia, and have come to a weird problem I do not understand fully: My final goal would involve calling a macro (more specifically @tullio) from within a generated function (to perform some tensor contractions that depend on the input tensors). But I have been having problems, which I narrowed down to calling the macro from within the generated function.

To illustrate the problem, let's consider a very simple example that also fails:

macro my_add(a,b) 
    return :($a + $b)
end

function add_one_expr(x::T) where T
    y = one(T)
    return :( @my_add($x,$y) )
end

@generated function add_one_gen(x::T) where T
    y = one(T)
    return :( @my_add($x,$y) )
end

With these declarations, I find that eval(add_one_expr(2.0)) works just as expected and returns and expression

:(@my_add 2.0 1.0)

which correctly evaluates to 3.0.

However evaluating add_one_gen(2.0) returns the following error:

MethodError: no method matching +(::Type{Float64}, ::Float64)

Doing some research, I have found that @generated actually produces two codes, and in one only the types of the variables can be used. I think this is what is happening here, but I do not understand what is happening at all. It must be some weird interaction between macros and generated functions.

Can someone explain and/or propose a solution? Thank you!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

温柔嚣张 2025-02-19 18:07:42

我发现将生成的函数视为具有两个组件是有帮助的: body 和任何生成的代码 Quote> Quote..end Quote..End 中的内容)。在编译时对身体进行评估,并且不“知道”值,而仅“知道”类型。因此,对于以 X :: T 为参数的生成功能,人体中对 x 的任何引用实际上都将指向类型 t 。这可能会很困惑。为了使事情变得更清晰,我建议只有 指的是类型,而不是价值。

这是一个示例:

julia> @generated function show_val_and_type(x::T) where {T}
           quote
               println("x is ", x)
               println("\$x is ", $x)
               println("T is ", T)
               println("\$T is ", $T)
           end
       end
show_val_and_type

julia> show_val_and_type(3)
x is 3
$x is Int64
T is Int64
$T is Int64

插值 $ x 是指“从身体上拿出 x (涉及 t ),然后将其拼写

。您遵循永不参考身体中值的方法,您可以通过删除@generated 来测试生成的功能,如下:

julia> function add_one_gen(x::T) where T
           y = one(T)
           quote
               @my_add(x,$y)
           end
       end
add_one_gen

julia> add_one_gen(3)
quote
    #= REPL[42]:4 =#
    #= REPL[42]:4 =# @my_add x 1
end

看起来很合理,但是当我们测试时,我们会得到什么

julia> add_one_gen(3)
ERROR: UndefVarError: x not defined
Stacktrace:
 [1] macro expansion
   @ ./REPL[48]:4 [inlined]
 [2] add_one_gen(x::Int64)
   @ Main ./REPL[48]:1
 [3] top-level scope
   @ REPL[49]:1

,所以让我们看看什么宏给人我们

julia> @macroexpand @my_add x 1
:(Main.x + 1)

指向 main.x ,它不存在。代码>。

julia> macro my_add(a,b) 
           return :($(esc(a)) + $(esc(b)))
       end
@my_add

julia> @generated function add_one_gen(x::T) where T
           y = one(T)
           quote
               @my_add(x,$y)
           end
       end
add_one_gen

julia> add_one_gen(3)
4

I find it helpful to think of generated functions as having two components: the body and any generated code (the stuff inside a quote..end). The body is evaluated at compile time, and doesn't "know" the values, only the types. So for a generated function taking x::T as an argument, any references to x in the body will actually point to the type T. This can be very confusing. To make things clearer, I recommend the body only refer to types, never to values.

Here's a little example:

julia> @generated function show_val_and_type(x::T) where {T}
           quote
               println("x is ", x)
               println("\$x is ", $x)
               println("T is ", T)
               println("\$T is ", $T)
           end
       end
show_val_and_type

julia> show_val_and_type(3)
x is 3
$x is Int64
T is Int64
$T is Int64

The interpolated $x means "take the x from the body (which refers to T) and splice it in.

If you follow the approach of never referring to values in the body, you can test generated functions by removing the @generated, like this:

julia> function add_one_gen(x::T) where T
           y = one(T)
           quote
               @my_add(x,$y)
           end
       end
add_one_gen

julia> add_one_gen(3)
quote
    #= REPL[42]:4 =#
    #= REPL[42]:4 =# @my_add x 1
end

That looks reasonable, but when we test it we get

julia> add_one_gen(3)
ERROR: UndefVarError: x not defined
Stacktrace:
 [1] macro expansion
   @ ./REPL[48]:4 [inlined]
 [2] add_one_gen(x::Int64)
   @ Main ./REPL[48]:1
 [3] top-level scope
   @ REPL[49]:1

So let's see what the macro gives us

julia> @macroexpand @my_add x 1
:(Main.x + 1)

It's pointing to Main.x, which doesn't exist. The macro is being too eager, and we need to delay its evaluation. The standard way to do this is with esc. So finally, this works:

julia> macro my_add(a,b) 
           return :($(esc(a)) + $(esc(b)))
       end
@my_add

julia> @generated function add_one_gen(x::T) where T
           y = one(T)
           quote
               @my_add(x,$y)
           end
       end
add_one_gen

julia> add_one_gen(3)
4
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文