请考虑以下代码摘录:
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)
而不必担心记忆。
答案 0 :(得分:3)
如果我理解正确,最简单的方法是使用闭包:
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是一种更灵活的选择。
答案 1 :(得分:2)
我不确定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)