Arrays Julia中随机输出的数组理解
。基本上,我试图在Julia中编写一个数组理解,它调用一个输出为随机数的函数Arrays Julia中随机输出的数组理解,arrays,random,julia,list-comprehension,Arrays,Random,Julia,List Comprehension,。基本上,我试图在Julia中编写一个数组理解,它调用一个输出为随机数的函数f(x)。当达到小于0.5的随机数时,我希望它终止函数。我能够编写以下代码: X=[f(i)表示1:1:100中的i,如果(j=f(i);j0.5)] 这样做的问题是,它调用了f(x)的两个独立实例,并且由于f(x)每次都是随机的,因此上述操作不会在正确的实例中终止for循环。我试过了 X=[J=f(i)表示1:1:100中的i,如果(J0.5)] 试图保存那个特定的随机数,但它告诉我J没有定义。有没有办法保存这个特定的
f(x)
。当达到小于0.5的随机数时,我希望它终止函数。我能够编写以下代码:
X=[f(i)表示1:1:100中的i,如果(j=f(i);j<0.5?错误:j>0.5)]
这样做的问题是,它调用了f(x)
的两个独立实例,并且由于f(x)
每次都是随机的,因此上述操作不会在正确的实例中终止for循环。我试过了
X=[J=f(i)表示1:1:100中的i,如果(J<0.5?false:J>0.5)]
试图保存那个特定的随机数,但它告诉我J没有定义。有没有办法保存这个特定的随机数来执行数组理解?您可以使用生成器上通常称为takewhile
的方法来执行此操作
X = collect(takewhile(x -> x ≥ 0.5, Generator(f, 1:100)))
这需要实现takewhile
,例如中的或您自己的(这并不难做到)takewhile
有一个相当容易阅读的名称,并且尽可能简洁
但是,我认为编写循环通常更具可读性,也更方便:
X=Float64[]
对于i=1:100
j=f(i)
如果j<0.5
打破
其他的
推(X,j)
结束
结束
正如建议的那样,使用循环是正确的方法。但是,如果尝试“其他”解决方案,以下是简短、混乱且具有教育意义的关于堆栈溢出中很少提及的通道的内容:
collect(Channel(c->begin
i=1
while true
v = rand()
if v<0.5 || i>100 return else put!(c,v) end
i+=1
end
end, ctype=Float64))
collect(通道c->begin
i=1
虽然是真的
v=兰德()
如果v100返回,否则放置!(c,v)结束
i+=1
结束
结束,ctype=64)
将rand()
替换为f(i)
,视情况而定。
顺便说一句,不要使用这个解决方案,因为它比一个简单的循环慢1000倍。如果通道是一个远程通道,
f(i)
是一个大型随机模拟,那么它可能很有价值。您试图做的基本上是一个简单的过滤操作:
filter(x -> x >= 0.5, [f(i) for i in 1:10])
这基本上是我们在之前首先依赖的,如果部分是在julia v0.5之前的列表理解中实现的
编辑:正如丹指出的,您可能会在保留所有元素后,直到第一个元素坚持一行解决方案,并受到@TASOSPAPSTYLEANOU的启发,快速解决方案是:
X = ( r=Vector{Float64}() ;
any(i->(v=f(i) ; v>0.5 ? ( push!(r,v) ; false) : true), 1:100)
; r )
[一行被拆分为三行,因为它有点长;)]
由于缺少f
,要测试此副本,请使用rand
粘贴此版本:
(r=ones(0); any(i->(v=rand(); v>0.5 ? (push!(r,v); false) : true), 1:10); r)
它比凤阳的功能慢10%。聪明的一点是利用任何的短路实现
附录:这里概括一下凤阳的takewhile
,以抽象出这个问题的答案:
collectwhilecond(f,cond,itr) = begin
r=Vector{typeof(f(first(itr)))}()
all(x->(y=f(x); cond(y) ? (push!(r,y);true):false),itr)
return r
end
现在,我们可以将上面的答案实现为(使用joker
asf
):
如果Julia推断出f
的返回类型,则collectwhilecond
的类型也是稳定的
编辑:使用@tim建议的方法推断f
的返回类型,而不拉动itr
元素,也不冒不稳定f
产生错误的风险,新的collectwhilecond
是:
collectwhilecond(f,cond,itr) = begin
t = Base.promote_op(f,eltype(itr)) # unofficial and subject to change
r = Vector{t}()
all( x -> ( y=f(x) ; cond(y) ? (push!(r,y) ; true) : false), itr )
return r
end
如果您想获得最大可能的性能,我想说有两种选择,这取决于哪个是瓶颈
f
速度非常快,但分配是一个问题。以下代码计算f
两次,但保存在分配部分(因为push!
有时会重新分配内存,请参阅):
i=findfirst(t->f(t)>0.5,1:100)
w=f.(1:(i-1))
f
非常慢,计算两次太贵。然后用push做一个循环代码>已推荐。您可能想看看sizehint代码>以进一步提高性能
一般来说,您不需要问什么慢,什么快:ypu可以简单地用优秀的软件包对您的特定用例进行基准测试。为什么不编写一个循环呢?这是一个非常简单的循环。这实际上是对数组理解的滥用。写一个循环。如何确定数组的元素类型?应该使用Base.promote\u op(f,Int)
?在生成器上使用takewhile会比使用循环更快吗?我正在处理的数组包含超过一百万个元素,我希望代码运行得尽可能快。不,当然for循环总是最快的,尽管takewhile
的良好实现可能会有较低的开销。@tim一个更好的方法是Base.\u返回类型(f,Tuple{Int})
,但这需要v0.6,这是一个修改过的过滤器,因为它在第一个元素丢失后停止添加元素。过滤器会比常规循环快吗?我正在尝试编写代码,以使其尽可能快地运行,并且我正在处理非常大的数组(大约几百万个元素)。@DanGetz啊,谢谢。如果没有相关的问题,细节一点也不明显。(假设链接的问题前提仍然适用)。如果f
是随机的(如建议的),重新计算可能会有问题。我不认为运行n
次的任何不完全平凡的函数都会比内存分配快。如果您担心内存分配,请执行sizehint在按下按钮之前执行code>
s更好。这也是我的想法,但后来我进行了基准测试,为了简单起见f
(我使用了平方浮动),第一个解决方案的速度是原来的两倍(即使第二个解决方案使用了sizehint!
)。这可能不仅仅是分配(我在这里找到了一些讨论,比较了push!
和填充预分配数组,以防您好奇:)。不过,我还是同意推代码>似乎是目前为止最新的nat
collectwhilecond(f,cond,itr) = begin
r=Vector{typeof(f(first(itr)))}()
all(x->(y=f(x); cond(y) ? (push!(r,y);true):false),itr)
return r
end
julia> joker(i) = 1.0 + 4*rand() - log(i)
julia> collectwhilecond(joker, x->x>=0.5, 1:100)
3-element Array{Float64,1}:
4.14222
3.42955
2.76387
collectwhilecond(f,cond,itr) = begin
t = Base.promote_op(f,eltype(itr)) # unofficial and subject to change
r = Vector{t}()
all( x -> ( y=f(x) ; cond(y) ? (push!(r,y) ; true) : false), itr )
return r
end