Julia - 有没有办法让这个就地赋值函数看起来更好
Julia - Is there a way to make this in-place assignment function look better
考虑这段代码摘录:
q = zeros(5,5);
x = rand(5,5);
function testFunc!(n, q, x)
for i = 1:n
q .= x.^2;
end
end
我初始化 q
并通过就地赋值函数更新 q
中的值。无论我如何选择 n
,分配的数量都是恒定的,因为我没有创建任何新东西。
但是,有没有办法编写一个名为 testFunc2
的函数,如下所示:
q = zeros(5,5)
x = rand(5,5);
q = testFunc2(n, x)
这样内存分配对于n
是不变的。
我知道这看起来很傻,但在我的实际代码中,我有许多类似于 q
的变量,并且我有一个循环在每次迭代时更新它们。我不希望代码占用过多的内存。如果我写一个看起来像 testFunc2(n, x, q1, q2, q3, q4)
的函数,它在语法上看起来会很麻烦。因此,我希望能够拥有 q1, q2, q3, q4 = testFunc2(n, x)
而不必担心内存。
我不确定 q1, q2, q3, q4 = testFunc2(n, x)
看起来是否比 testFunc2!(n, x, q1, q2, q3, q4)
好得多,但您可以使用这样的宏进行这种类型的转换:
macro wrap(ex)
outputs = ex.args[1].args
ex.args[2].args[1] = Symbol(ex.args[2].args[1], "!")
append!(ex.args[2].args, outputs)
esc(ex)
end
扩展
@wrap q1, q2, q3, q4 = testFunc2(n, x)
进入
(q1, q2, q3, q4) = testFunc2!(n, x, q1, q2, q3, q4)
如果我没理解错的话,最简单的方法就是使用闭包:
let
q1 = similar(q)
q2 = similar(q)
global function testFunc2(n, x)
for i = 1:n
q1 .= x.^2
q2 .= x.^2
end
return q1, q2
end
end
julia> @btime testFunc2(10, $x)
786.000 ns (0 allocations: 0 bytes)
julia> @btime testFunc2(100, $x)
7.476 μs (0 allocations: 0 bytes)
正如@Liso 在下面的评论中所指出的,如果我们不尝试在调用之间共享相同的内存,则应该返回副本,例如return copy(q1), copy(q2)
而不是 return q1, q2
,那么我们仍然会得到常量内存分配:
@btime testFunc2(10, $x)
821.684 ns (3 allocations: 704 bytes)
@btime testFunc2(100, $x)
7.326 μs (3 allocations: 704 bytes)
反正选择哪种方法要看对应的用例,functor是比较灵活的选择。
考虑这段代码摘录:
q = zeros(5,5);
x = rand(5,5);
function testFunc!(n, q, x)
for i = 1:n
q .= x.^2;
end
end
我初始化 q
并通过就地赋值函数更新 q
中的值。无论我如何选择 n
,分配的数量都是恒定的,因为我没有创建任何新东西。
但是,有没有办法编写一个名为 testFunc2
的函数,如下所示:
q = zeros(5,5)
x = rand(5,5);
q = testFunc2(n, x)
这样内存分配对于n
是不变的。
我知道这看起来很傻,但在我的实际代码中,我有许多类似于 q
的变量,并且我有一个循环在每次迭代时更新它们。我不希望代码占用过多的内存。如果我写一个看起来像 testFunc2(n, x, q1, q2, q3, q4)
的函数,它在语法上看起来会很麻烦。因此,我希望能够拥有 q1, q2, q3, q4 = testFunc2(n, x)
而不必担心内存。
我不确定 q1, q2, q3, q4 = testFunc2(n, x)
看起来是否比 testFunc2!(n, x, q1, q2, q3, q4)
好得多,但您可以使用这样的宏进行这种类型的转换:
macro wrap(ex)
outputs = ex.args[1].args
ex.args[2].args[1] = Symbol(ex.args[2].args[1], "!")
append!(ex.args[2].args, outputs)
esc(ex)
end
扩展
@wrap q1, q2, q3, q4 = testFunc2(n, x)
进入
(q1, q2, q3, q4) = testFunc2!(n, x, q1, q2, q3, q4)
如果我没理解错的话,最简单的方法就是使用闭包:
let
q1 = similar(q)
q2 = similar(q)
global function testFunc2(n, x)
for i = 1:n
q1 .= x.^2
q2 .= x.^2
end
return q1, q2
end
end
julia> @btime testFunc2(10, $x)
786.000 ns (0 allocations: 0 bytes)
julia> @btime testFunc2(100, $x)
7.476 μs (0 allocations: 0 bytes)
正如@Liso 在下面的评论中所指出的,如果我们不尝试在调用之间共享相同的内存,则应该返回副本,例如return copy(q1), copy(q2)
而不是 return q1, q2
,那么我们仍然会得到常量内存分配:
@btime testFunc2(10, $x)
821.684 ns (3 allocations: 704 bytes)
@btime testFunc2(100, $x)
7.326 μs (3 allocations: 704 bytes)
反正选择哪种方法要看对应的用例,functor是比较灵活的选择。