【问题标题】:Write Julia macro that returns a function编写返回函数的 Julia 宏
【发布时间】:2021-03-28 03:00:53
【问题描述】:

第一次发帖,感谢阅读!

问题:我有一个Vector{String} - 称之为A - 其中每个元素都是等式的一部分,例如A 的第一个元素是 "x[1] - (0.8*x[1])"。我想编写一个宏,它以 i) 一个 String 为参数 - 称它为 fn_name - 带有一个函数的名称,ii) 向量 A,并返回一个名为 fn_name 的函数,它看起来像

function fn_name(f, x)
  f[1] = x[1] - (0.8*x[1])
  f[2] = (exp(x[4]) - 0.8*exp(x[3]))^(-1.1) - (0.99*(exp(x[4]) - 0.8*exp(x[4]))^(-1.1)*(1.0 - 0.025 + 0.30*exp(x[1])*exp(x[2])^(0.30 - 1.0)))
  f[3] = exp(x[2]) - ((1.0 - 0.025)*exp(x[2]) + exp(x[1])*exp(x[2])^0.30 - exp(x[4]))
  f[4] = x[3] - (x[4])
end 

其中每个 rhs 是一个元素

A = ["x[1] - (0.8*x[1])", "(exp(x[4]) - 0.8*exp(x[3]))^(-1.1) - (0.99*(exp(x[4]) - 0.8*exp(x[4]))^(-1.1)*(1.0 - 0.025 + 0.30*exp(x[1])*exp(x[2])^(0.30 - 1.0)))", "exp(x[2]) - ((1.0 - 0.025)*exp(x[2]) + exp(x[1])*exp(x[2])^0.30 - exp(x[4]))", "x[3] - (x[4])"]

我尝试了什么:我解决问题的最佳尝试如下

macro make_fn(fn_name, A)
    esc(quote
        function $(Symbol(fn_name))(f, x)
            for i = 1:length($(A))
                f[$i] = Meta.parse($(A)[$i])
            end
        end
    end)
end

但这不起作用:当我运行 @make_fn("my_name", A) 时,我收到错误 LoadError: UndefVarError: i not defined

我发现很难完全理解 Julia 元编程,虽然我很乐意避免使用它,但我认为这个问题是不可避免的。

你能帮我理解我的错误在哪里吗?

谢谢

【问题讨论】:

  • 第一个问题非常好!请注意,这里并不认为问候和感谢是必要的,将它们排除在外并不被认为是不礼貌的 - 一个好问题不言自明。

标签: julia metaprogramming


【解决方案1】:

这种情况下的宏不仅是可以避免的,甚至是不适用的,除非A 在编译时是已知的。

我可以使用eval 和一些闭包来提供解决方案:

julia> function make_fn2(A)
           Af = [@eval(x -> $(Meta.parse(expr))) for expr in A]
           function (f, x)
               for i in eachindex(A, f)
                   f[i] = Af[i](x)
               end
               return f
           end
       end
make_fn2 (generic function with 1 method)

julia> fn_name = make_fn2(A)
#46 (generic function with 1 method)

julia> fn_name(zeros(4), [1,2,3,4])
4-element Array{Float64,1}:
  0.19999999999999996
 -0.06594092302655707
 49.82984401122239
 -1.0

有限制

  1. eval 将在定义 this 的模块的全局范围内评估表达式(因此它可能与调用函数的范围不同),并且
  2. 新创建的函数只有在您第一次返回全局范围时才会起作用(即,如果您尝试在创建它的函数中运行它,它将不起作用)。

但我真的建议考虑一种比字符串更好的输入格式。

【讨论】:

  • 我完全同意建议和回答。两个小的附加 cmets(特别说明了为什么这是不可取的):1)eval 将在模块的全局范围内进行评估(因此它与调用函数的范围不同),2)新创建的仅当您首先返回全局范围时,该函数才会起作用(即,如果您尝试在创建它的函数中运行它,它将不起作用)
  • 感谢@BogumiłKamiński,非常真实。我已经添加了这些点。 (事实上​​,通过改变看似无辜的事情,你很容易遇到世界时代的问题,比如不将Af 的定义从内部函数中提升出来。)
  • I might have a solution for point 1.,但是评论太长了,没有经过适当的测试。
  • @phipsgabler 我将在此处查看链接的解决方案,因为确实在全局范围内进行评估是一个大问题,但我同意理想情况下不会从一开始就使用字符串作为输入(我正在尝试找到一种解决方法,以便使用其他人编写的依赖于 txt 文件的include() 的包中的函数在工作区中生成一些其他函数)
  • 您可以使用 Base.invokelatest 来避免世界年龄问题,但这会阻止编译器优化。
猜你喜欢
  • 2020-06-22
  • 2017-04-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-01-16
相关资源
最近更新 更多