Iterate over a range in a non standard way

I’m doing some exhaustive search loops and would like to cover say a range 10:100 but suspect (with quite some confidence) that the solution I look for is around 90. So I would like to loop over the range by starting at 90 and looking around to be possibly be able to break early: 90, 91,89,92,88,93,87,94,86,95,85,96,84,97,83,98,82,99,81,100,80, 79, … 10

I guess defining a custom iterator would be the best solution, but I’m not very familiar with it. Any suggestion what to be careful about if I launch myself into this?
Thanks in advance

You can create a struct and implement the iteration interface for it:

Alternatively, if you want a quick & simple solution, then something like this might work:

function myiterator(minval, maxval, guess)
    n = max(guess - minval, maxval - guess) + 1
    @assert n ≥ 0
    offset = [0; (repeat(1:n, inner=2) .* repeat([-1, 1], outer=n))];
    return filter(∈(minval:maxval), guess .- offset)
end
3 Likes

This seems to work without allocations

julia> struct RangeAround{R1,R2}
           lower::R1
           upper::R2
           function RangeAround(range::R, start) where R
               i = findfirst(==(start), range)
               i === nothing && error("Start point $start not in range $range")
               lower = reverse(range[begin:i])
               upper = range[i+1:end]
               new{typeof(lower), typeof(upper)}(lower, upper)
           end
       end

julia> Base.iterate(a::RangeAround) = (first(a.lower), 2)

julia> function Base.iterate(a::RangeAround, i)
           length_upper = length(a.upper)
           length_lower = length(a.lower)
           if i > length_upper + length_lower
               return nothing
           else
               shared_length = min(length_upper, length_lower)
               if i <= 2 * shared_length
                   return iseven(i) ? a.upper[i ÷ 2] : a.lower[(i + 1) ÷ 2], i + 1
               elseif length_upper > length_lower
                   return a.upper[i - shared_length], i + 1
               else
                   return a.lower[i - shared_length], i + 1
               end
           end
       end
julia> foreach(println, RangeAround(1:10, 8))
8
9
7
10
6
5
4
3
2
1
2 Likes

You can try the one liner:

julia> collect(Iterators.flatten((Iterators.flatten(zip(90:-1:81, 91:100)), 80:-1:1)))
100-element Vector{Int64}:
 90
 91
 89
 92
 88
 93
 87
 94
  ⋮
  7
  6
  5
  4
  3
  2
  1
2 Likes

Just writing explicit loops seems like it would be a lot easier than defining an iterator.

But even easier and more explicit would be to sort your range by how far the elements are from your target value of 90:

julia> sort(10:100, by=n->abs(n-90))
91-element Vector{Int64}:
 90
 89
 91
 88
 92
 87
 93
 86
  ⋮
 16
 15
 14
 13
 12
 11
 10
7 Likes