Charlie Crown
Charlie Crown

Reputation: 1089

Filtering a range in Julia

I have some MWE below. What I want is to have a subsection of a range, interact with the rest of the range, but not itself.

For instance if the range is 1:100, I want to have a for loop that will have each index in 4:6, interact with all values of 1:100 BUT NOT 4:6.

I want to do this using ranges/filters to avoid generating temporary arrays.

In my case the total range is the number of atoms in the system. The sub-range, is the atoms in a specific molecule. I need to do calculations where each atom in a molecule interacts with all other atoms, but not the atoms in the same molecule.

Further

I am trying to avoid using if statements because that messes up parallel codes. Doing this with an if statement would be

for i=4:6
    for j = 1:100
        if j == 4 || j==5 || j==6
            continue
        end
        println(i, " ", j)
    end
end

I have actual indexing in my code, I would never hardcode values like the above... But I want to avoid that if statement.

Trials

The following does what I want, but I now realize that using filter is bad when it comes to memory and the amount used scales linearly with b.

a = 4:6
b = 1:100
for  i in a
    for j in filter((b) -> !(b in a),b) 
        print(i, " ", j) 
    end
end

Is there a way to get the double for loop I want where the outer is a sub-range of the inner, but the inner does not include the outer sub-range and most importantly is fast and does not create alot of memory usage like filter does?

Upvotes: 0

Views: 678

Answers (2)

Przemyslaw Szufel
Przemyslaw Szufel

Reputation: 42214

What about creating a custom iterator? Note that example below needs some adjustments depending on how you define the exception lists (for example for long list with non continues indices you should use binary search).

struct RangeExcept
       start::Int
       stop::Int
       except::UnitRange{Int}
end

function Base.iterate(it::RangeExcept, (el, stop, except)=(it.except.start > 1 ? it.start : it.except.stop+1, it.stop, it.except))
    new_el = el+1
    if new_el in except
        new_el = except.stop+1
    end 
    el > stop && return nothing
    return (el, (new_el, stop,except))
end

Now let us test the code:

julia> for i in RangeExcept(1,10,3:7)
           println(i)
           end
1
2
8
9
10

Upvotes: 2

Bill
Bill

Reputation: 6086

If memory usage is really a concern, consider two for loops using the range components:

systemrange = 1:50
moleculerange = 4:12
for i in systemrange[1]:moleculerange[1]-1
    println(i)
end
for i in moleculerange[end]+1:systemrange[end]
    println(i)
end

You might be able to do each loop in its own thread.

Upvotes: 3

Related Questions