在 Julia 中创建一个同时 returns 两个值的生成器
Create a generator which returns two values at once in Julia
给定一个生成器:
myVec1 = rand(0:4, 2)
myVec2 = rand(0:4, 8)
myGen = (val1 + val2 for val1 in myVec1, val2 in myVec2)
这基本上是一个有 2 列的矩阵。使用collect(myGen)
.
可以看出
我如何创建一个每次调用产生两个值的生成器(基本上是一列)?
从概念上讲,相当于:
for myCol in eachcol(collect(myGen))
@show myCol;
end
只是没有对矩阵进行任何显式分配。
对于以下情况,我可以包装 myGen
吗:
for value1, value2 in myGen
dosomethingelse1(value1, value2)
end
换句话说,我正在寻找一种方法来创建一个生成器,该生成器一次 returns 2 个(或更多?)连续值,并且可以在循环中使用。
基本上,我们在生成器中创建了一个二维数组,我想一次访问整个切片。我可以用 eachcol
和 eachrow
来实现实际数组,但是生成器呢?
这是一个测试用例:
myVec1 = rand(0:4, 2);
myVec2 = rand(0:4, 800);
@btime begin
myMat = [val1 + val2 for val1 in myVec1, val2 in myVec2];
outVec = [sum(myCol) for myCol in eachcol(myMat)];
end
@btime begin
myGen = (val1 + val2 for val1 in myVec1, val2 in myVec2);
outVec = [sum(myCol) for myCol in Iterators.partition(myGen, 2)];
end
@Bogumił Kamiński 的解决方案确实有效,但在实践中,出于某种原因,它创建了更多分配,而动机是减少它。
在第二个循环中解构值的元组时,您基本上缺少括号。更详细地说,您可以在 dosomething
函数中只 return 两个值(一个元组)。例如:
function dosomething(element)
secondElement = element^2
element, secondElement
end
然后您可以通过解构 return 值来使用循环,如下所示:
for (value1, value2) in myGen
dosomethingelse(value1, value2)
end
如果你想要一个完整的工作示例:
myArray = [1, 2, 3]
function dosomething(element)
secondElement = element^2
element, secondElement
end
myGen = (dosomething(myElement) for myElement in myArray)
function dosomethingelse(value1, value2)
println("Value 1: $value1 \nValue 2: $value2 \n")
end
for (value1, value2) in myGen
dosomethingelse(value1, value2)
end
这是由 Julia 语法直接支持的。迭代元组的生成器就像迭代原子值一样。
例如,您可以尝试:
for (a,b) in ((x, 3x) for x in 1:4)
println("a=$a, b=$b")
end
我假设你想要这样的东西:
julia> for (x1, x2) in Iterators.partition(1:10, 2)
@show x1, x2
end
(x1, x2) = (1, 2)
(x1, x2) = (3, 4)
(x1, x2) = (5, 6)
(x1, x2) = (7, 8)
(x1, x2) = (9, 10)
如果这是您想要的,那么 Iterators.partition
是您可以使用的功能。
编辑:如果您有两个源流,请使用 zip
:
julia> for (x1, x2) in zip(1:5, 6:10)
@show x1, x2
end
(x1, x2) = (1, 6)
(x1, x2) = (2, 7)
(x1, x2) = (3, 8)
(x1, x2) = (4, 9)
(x1, x2) = (5, 10)
编辑 2:我的第一个解决方案已经适用于您的情况:
julia> collect(myGen)
2×8 Matrix{Int64}:
3 7 5 4 6 3 4 5
1 5 3 2 4 1 2 3
julia> for (x1, x2) in Iterators.partition(myGen, 2)
@show x1, x2
end
(x1, x2) = (3, 1)
(x1, x2) = (7, 5)
(x1, x2) = (5, 3)
(x1, x2) = (4, 2)
(x1, x2) = (6, 4)
(x1, x2) = (3, 1)
(x1, x2) = (4, 2)
(x1, x2) = (5, 3)
尽管根据 OP 在其编辑中添加的信息,其他答案在某些方面更为笼统,但内存效率更高的选项是使用嵌套生成器。类似于:
function solution_nested(v1, v2)
myGen = ((val1 + val2 for val1 in v1) for val2 in v2)
[sum(myCol) for myCol in myGen]
end
当您测试解决方案时,您应该避免使用全局变量,最好将解决方案包装在一个函数中,以便为 Julia 提供足够的机会来优化代码。
此解决方案仅给出一次分配的预期结果:
julia> @btime solution_nested(myVec1, myVec2);
1.856 μs (1 allocation: 6.38 KiB)
因此,虽然此解决方案不太符合标题,但它似乎符合您的描述。我们使用惰性列的惰性序列。 Iterators.partition
缓慢且内存效率低下的原因是它实际上在分区中分配值的中间向量:https://github.com/JuliaLang/julia/blob/dacf9d65aff4668b8fff25957d9aaa2cf03868c8/base/iterators.jl#L1232 .
给定一个生成器:
myVec1 = rand(0:4, 2)
myVec2 = rand(0:4, 8)
myGen = (val1 + val2 for val1 in myVec1, val2 in myVec2)
这基本上是一个有 2 列的矩阵。使用collect(myGen)
.
我如何创建一个每次调用产生两个值的生成器(基本上是一列)?
从概念上讲,相当于:
for myCol in eachcol(collect(myGen))
@show myCol;
end
只是没有对矩阵进行任何显式分配。
对于以下情况,我可以包装 myGen
吗:
for value1, value2 in myGen
dosomethingelse1(value1, value2)
end
换句话说,我正在寻找一种方法来创建一个生成器,该生成器一次 returns 2 个(或更多?)连续值,并且可以在循环中使用。
基本上,我们在生成器中创建了一个二维数组,我想一次访问整个切片。我可以用 eachcol
和 eachrow
来实现实际数组,但是生成器呢?
这是一个测试用例:
myVec1 = rand(0:4, 2);
myVec2 = rand(0:4, 800);
@btime begin
myMat = [val1 + val2 for val1 in myVec1, val2 in myVec2];
outVec = [sum(myCol) for myCol in eachcol(myMat)];
end
@btime begin
myGen = (val1 + val2 for val1 in myVec1, val2 in myVec2);
outVec = [sum(myCol) for myCol in Iterators.partition(myGen, 2)];
end
@Bogumił Kamiński 的解决方案确实有效,但在实践中,出于某种原因,它创建了更多分配,而动机是减少它。
在第二个循环中解构值的元组时,您基本上缺少括号。更详细地说,您可以在 dosomething
函数中只 return 两个值(一个元组)。例如:
function dosomething(element)
secondElement = element^2
element, secondElement
end
然后您可以通过解构 return 值来使用循环,如下所示:
for (value1, value2) in myGen
dosomethingelse(value1, value2)
end
如果你想要一个完整的工作示例:
myArray = [1, 2, 3]
function dosomething(element)
secondElement = element^2
element, secondElement
end
myGen = (dosomething(myElement) for myElement in myArray)
function dosomethingelse(value1, value2)
println("Value 1: $value1 \nValue 2: $value2 \n")
end
for (value1, value2) in myGen
dosomethingelse(value1, value2)
end
这是由 Julia 语法直接支持的。迭代元组的生成器就像迭代原子值一样。
例如,您可以尝试:
for (a,b) in ((x, 3x) for x in 1:4)
println("a=$a, b=$b")
end
我假设你想要这样的东西:
julia> for (x1, x2) in Iterators.partition(1:10, 2)
@show x1, x2
end
(x1, x2) = (1, 2)
(x1, x2) = (3, 4)
(x1, x2) = (5, 6)
(x1, x2) = (7, 8)
(x1, x2) = (9, 10)
如果这是您想要的,那么 Iterators.partition
是您可以使用的功能。
编辑:如果您有两个源流,请使用 zip
:
julia> for (x1, x2) in zip(1:5, 6:10)
@show x1, x2
end
(x1, x2) = (1, 6)
(x1, x2) = (2, 7)
(x1, x2) = (3, 8)
(x1, x2) = (4, 9)
(x1, x2) = (5, 10)
编辑 2:我的第一个解决方案已经适用于您的情况:
julia> collect(myGen)
2×8 Matrix{Int64}:
3 7 5 4 6 3 4 5
1 5 3 2 4 1 2 3
julia> for (x1, x2) in Iterators.partition(myGen, 2)
@show x1, x2
end
(x1, x2) = (3, 1)
(x1, x2) = (7, 5)
(x1, x2) = (5, 3)
(x1, x2) = (4, 2)
(x1, x2) = (6, 4)
(x1, x2) = (3, 1)
(x1, x2) = (4, 2)
(x1, x2) = (5, 3)
尽管根据 OP 在其编辑中添加的信息,其他答案在某些方面更为笼统,但内存效率更高的选项是使用嵌套生成器。类似于:
function solution_nested(v1, v2)
myGen = ((val1 + val2 for val1 in v1) for val2 in v2)
[sum(myCol) for myCol in myGen]
end
当您测试解决方案时,您应该避免使用全局变量,最好将解决方案包装在一个函数中,以便为 Julia 提供足够的机会来优化代码。
此解决方案仅给出一次分配的预期结果:
julia> @btime solution_nested(myVec1, myVec2);
1.856 μs (1 allocation: 6.38 KiB)
因此,虽然此解决方案不太符合标题,但它似乎符合您的描述。我们使用惰性列的惰性序列。 Iterators.partition
缓慢且内存效率低下的原因是它实际上在分区中分配值的中间向量:https://github.com/JuliaLang/julia/blob/dacf9d65aff4668b8fff25957d9aaa2cf03868c8/base/iterators.jl#L1232 .