朱莉娅:把代码注入功能

use*_*893 3 macros metaprogramming julia

我想将代码注入函数中.为了具体,请考虑一个简单的模拟器:

function simulation(A, x)
    for t in 1:1000
        z = randn(3)
        x = A*x + z
    end
end
Run Code Online (Sandbox Code Playgroud)

有时我想每十个时间步记录x的值,有时是每20个时间步的z的值,有时我不想记录任何值.当然,我可以将一些标志作为函数的参数,并有一些if-else语句.但我想保持模拟代码干净,只注入一段代码

if t%10 == 0
    append!(rec_z, z)
end
Run Code Online (Sandbox Code Playgroud)

在我需要的时候进入函数的特定位置.为此,我想编写一个宏来监视一个特定的值

@monitor(:z, 10)
simulation(A, x)
Run Code Online (Sandbox Code Playgroud)

这可能与Julia的Metaprogramming功能有关吗?

Mat*_* B. 5

不,你不能使用元编程将代码注入已经编写的函数中.元编程只能做你可以直接在宏编写宏的位置自己编写的东西.这意味着一个声明如下:

@monitor(:z, 10); simulation(A, x)
Run Code Online (Sandbox Code Playgroud)

甚至无法修改simulation(A, x)函数调用.它只能扩展到simulation调用之前运行的一些正常的Julia代码.或许,您可以将模拟函数调用作为宏的参数包括在内,例如@monitor(:z, 10, simulation(A, x)),但现在所有宏都能做的就是更改函数调用本身.它仍然无法"返回"并将新代码添加到已编写的函数中.

但是,您可以仔细而精心地制作一个宏,它接受函数定义体并修改它以添加调试代码,例如,

@monitor(:z, 10, function simulation(A, x)
    for t in 1:1000
        # ...
    end
end)
Run Code Online (Sandbox Code Playgroud)

但是现在你必须在遍历函数体中的代码的宏中编写代码,并将调试语句注入正确的位置.这不是一件容易的事.而以更强大的方式编写更难以打破您修改实际模拟代码的那一刻.

遍历代码并插入代码对于您自己使用编辑器来说是一件更容易的任务.调试语句的常用习惯是使用单行,如下所示:

const debug = false
function simulation (A, x)
    for t in 1:1000
        z = rand(3)
        x = A*x + z
        debug && t%10==0 && append!(rec_z, z)
    end
end
Run Code Online (Sandbox Code Playgroud)

这里真正酷的是,通过标记debug为常量,Julia能够完全优化调试代码false- 它甚至不会出现在生成的代码中!所以当你没有调试时没有开销.但是,它确实意味着您必须重新启动Julia(或重新加载它所在的模块)才能更改debug标志.即使debug没有标记为const,我也无法测量这个简单循环的任何开销.而且很有可能,你的循环将比这个更复杂.所以在你真正仔细检查它是否有效之前,不要担心这里的表现.