朱莉娅:含有关键字成素文字注入代码

问题描述

我有一个包含一个for循环的功能。我想向该函数添加一个参数,让我选择使用 Threads.@threads for i in ...。 我从而只需要在循环的前注入Threads.@threads。宏鸵鸟政策工作,因为他们不容处理关键字。 Alterntively我能有这样的事情

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 循环的 if else 函数,但我更喜欢其余的代码原样。

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

您可以开始定义两个高阶函数来抽象出 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
,

及时插入代码并不能很好地或轻松地工作。编译器不知道该做什么,它根本不会被优化,也不安全。有关类似问题,请参阅:Julia: inject code into function .

我会通过为 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)