Macros 朱莉娅:将包含关键字的代码注入到scipt中

Macros 朱莉娅:将包含关键字的代码注入到scipt中,macros,julia,keyword,Macros,Julia,Keyword,我有一个包含for循环的函数。我想为该函数添加一个参数,让我选择使用 线程。@i的线程在…中。 因此,我只需要在循环前面注入线程。@Threads。宏无法工作,因为它们无法处理关键字。 或者我可以有类似的东西 if parrallel inject("Threads.@threads for i in 1:n") else inject("for i in 1:n") end loop content.... end 我找不到任

我有一个包含for循环的函数。我想为该函数添加一个参数,让我选择使用
线程。@i的线程在…
中。 因此,我只需要在循环前面注入
线程。@Threads
。宏无法工作,因为它们无法处理关键字。 或者我可以有类似的东西

if parrallel
    inject("Threads.@threads for i in 1:n")
else
    inject("for i in 1:n")
end

    loop content....
end
我找不到任何方法插入这样的代码。怎么办


当然,可以选择将整个循环放在一个函数中,然后在函数上使用包含for循环的if-else,但我更希望代码的其余部分保持原样。

及时插入代码并不容易。编译器不知道该做什么,也不会进行任何优化,也不会安全。有关类似问题,请参见:

我可以通过为for循环编写一个单独的函数(在我看来是更简洁的方式),或者将for循环留在第一个函数中,然后将其加倍编写来编写代码

比如:

function forloopcontent()
    println(Threads.threadid())
end

function f(parallel::Bool)
    if parallel
        Threads.@threads for i in 1:10
            forloopcontent()
        end
    else for i in 1:10
            forloopcontent()
        end
    end
end
否则,您还可以为并行版本编写另一个函数,为普通版本编写两个不同的方法。即:

function f(;parallel::Bool=false)
    parallel ? (return par_f()) : (return f())
end 

function f()
    for i in 1:10
        println(Threads.threadid())
    end
end

function par_f()
    Threads.@threads for i in 1:10
        println(Threads.threadid())
    end
end
对于并行版本,此版本可以称为
f(;parallel=true)
,对于非并行版本,此版本可以称为
f(;parallel=false)

当然,可以选择将整个循环放在一个函数中,并在函数上使用包含for循环的if-else,但我更喜欢代码的其余部分

请注意,高阶函数和语法糖类使此类解决方案相对简单,易于开发和阅读:

您可以开始定义两个抽象for循环的高阶函数

# This one is basically `Base.foreach`
function sequential_for(f, iterable)
    for i in iterable
        f(i)
    end
end

# A thread-parallel version
function parallel_for(f, iterable)
    Threads.@threads for i in iterable
        f(i)
    end
end
然后,您的函数可以动态决定要使用哪个版本的for循环:

function my_fun(n; parallel=false)
    for_loop = parallel ? parallel_for : sequential_for
    
    x = zeros(Int, n)

    # The do syntax avoids having to either
    # - define the loop body as a named function elsewhere, or
    # - put an hard-to-read lambda directly as argument to `for_loop`
    for_loop(1:n) do i
        x[i] = Threads.threadid()
        sleep(0.1)  # Let's make sure we see the effect of parallelism :-)
    end
    return x
end
示例用法:

julia> @time my_fun(10)
  1.025307 seconds (299 allocations: 17.109 KiB)
10-element Array{Int64,1}:
 1
 1
 1
 1
 1
 1
 1
 1
 1
 1

julia> @time my_fun(10, parallel=true)
  0.235430 seconds (18.44 k allocations: 979.714 KiB)
10-element Array{Int64,1}:
 1
 1
 2
 2
 3
 4
 5
 6
 7
 8

及时注入代码听起来不是一种好的或简单的方法。这里有一个类似的问题:。我读到了这一点,但我认为提出的问题不适用,因为我希望在函数内而不是从函数外部执行。您希望像这样插入代码的原因是什么?在我看来,这是一种非常奇怪的编程方式。它无法正确编译,很难做到,也没有任何明显的优势。我错了吗?也许是不完整;)我确实认为这可能不是最优的,但基本上我只是在寻找一种尽可能多地保留代码的方法。