Defining an array with random output in Julia

This question is an extension if I asked it earlier . Basically, I'm trying to write an array comprehension in Julia that calls a function f(x)

whose output is a random number. When a random number less than 0.5 is reached, I want it to kill the function. I was able to write the following code:

X = [f (i) for i in 1: 1: 100 if (j = f (i); j <0.5≀ false: j> 0.5)]

The problem is that this calls two separate instances f(x)

, but because it f(x)

is random each time, the above will not kill the for loop on the correct instance. I tried

X = [J = f (i) for i in 1: 1: 100 if (J <0.5≀ false: J> 0.5)]

As an attempt to store that particular random number, but then it tells me that J is undefined. Is there a way to store this particular random number to fulfill my understanding of the array?

+3


source to share


5 answers


Insisting on a one-line solution, and inspired by @TasosPapastylianou, a quick solution would be:

X = ( r=Vector{Float64}() ; 
  any(i->(v=f(i) ; v>0.5 ? ( push!(r,v) ; false) : true), 1:100) 
  ; r )

      

[one line divides by three because it's a little long;)]

Since f

missing, to test this copy paste this version with rand

:

(r=ones(0); any(i->(v=rand(); v>0.5 ? (push!(r,v); false) : true), 1:10); r)

      

It compares about 10% slower than the Fengyang function. The smart bit uses any

a short circuit implementation.

ADDITION . To summarize here, this is Fengyang's version takewhile

to divert the answer to this question:



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

      

We can now implement the answer above (with joker

how f

):

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

also type resistant if Julia passes a f

return type.

EDIT: Using @tim's suggested return type inference method f

without pulling the item itr

and without the risk of unstable f

error generation, the new one collectwhilecond

is:

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

      

+6


source


What you are trying to do is a simple operation filter

:

filter(x -> x >= 0.5, [f(i) for i in 1:10])

      

This is basically what we used to rely on before the part if

was implemented in lists in julia prior to v0.5




EDIT: As Dan pointed out, you can keep all elements as long as the first element, which is <0.5 instead, like this:

L = [f(i) for i in 1:10]; L[1 : findfirst(L.<0.5) - 1]

      

However, in this case, as others have pointed out, you could go for a normal cycle as well. List comprehension will always process the entire list first, so it won't be any faster. You can use a generator, but then you have to create your own custom mechanism to make it stop in the correct state (as suggested by Fengyang with help takewhile

).

So, to answer the question in the comment, the fastest thing you could do in this case is normal for a loop that breaks accordingly. Also, it is best included in a function rather than globally evaluated, and it will speed up even more if you specify the type of the variables.

+5


source


You can do this with what is commonly called takewhile

on a generator

X = collect(takewhile(x -> x β‰₯ 0.5, Generator(f, 1:100)))

      

which requires an implementation takewhile

, for example in this blog post or your own (it's not too hard to do). takewhile

has a name that is easy to read and as succinct as possible.

However, I think it is often more readable and more convenient to write a loop:

X = Float64[]
for i = 1:100
    j = f(i)
    if j < 0.5
        break
    else
        push!(X, j)
    end
end

      

+4


source


As expected, using a loop is the right way to go. But if you are trying to use "other" solutions, then short and confusing and educational pipes that are rarely mentioned in Stack Overflow:

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))

      

Replace rand()

with f(i)

if necessary. And BTW don't use this solution because it is 1000x slower than a simple loop. Possibly value if Channel is a RemoteChannel and f(i)

is a big stochastic simulation.

+3


source


If you want to squeeze the best performance possible, I would say that there are two options, depending on which is the bottleneck.

  • f

    is very fast, but selection is a problem. The following code evaluates f

    twice, but retains part of the allocation (since it push!

    sometimes reallocates memory, see here ):

i = findfirst(t -> f(t) > 0.5, 1:100) w = f.(1:(i-1))

  1. f

    very slow and is too expensive to compute twice. Then just cycle with push!

    as already recommended. You can take a look sizehint!

    to improve performance.

In general, you don't have to ask which is slower and which is faster: ypu can simply compare your particular use case against the excellent BenchmarkTools .

+2


source







All Articles