Ranges with zero step

This is my first post here and English is not my native language so please excuse some (hopefully minor) mistakes.

I ran across a problem when trying to iterate over some cubic space. I want to give a function two corners of a space and a resolution for each axis. Then I want to iterate over each axis in nested loops to do something at every point in that space. My code looks something like this:

corner1 = [0.0, 1.0, 5.0]
corner2 = [1.0, 2.0, 5.0]
res = [100, 100, 1]
steps = abs.((corner1 .- corner2))./res
for x=corner1[1]:steps[1]:corner2[1]
     for y=corner1[2]:steps[2]:corner2[2]
          for z=corner1[3]:steps[3]:corner2[3]
                  # do something with x, y and z
          end
     end
end

But, if you try to run that, julia will give me an error that I cannot create a range with 0 step. Under normal circumstances this would be reasonable. Here however the length of the range is zero anyway.

I came up with a very quick and hacky fix. The fucntion (:)(start::T, step::T, stop::T) where T<:Union{Float16,Float32,Float64} in twiceprecision.jl raised the error.
My function now looks like:

function (:)(start::T, step::T, stop::T) where T<:Union{Float16,Float32,Float64}
    step == 0 && start - stop != 0 && throw(ArgumentError("range step cannot be zero for non-zero length range"))
    if step == 0
        return 0.0:0.0
    end
    # rest of the function
end

My problem is solved by that. But I don’t think a proper fix is that easy.

Perhaps my approach on this problem was totally wrong and I have to tackle it different.

In any case I am happy about feedback and further advice.

How about

for x = range(corner1[1], corner2[1], length=100)
  for y = range(corner1[2], corner2[1], length=100)
    for z = range(corner1[3], corner2[3], length=1)
    #
    end
  end
end

?

3 Likes

Thank you. Didn’t know about the length feature. Just gets a bit messy since I have to use an enumerate, too. But it seems like the most elegant way to do it.

1 Like

The range() function is really nice of you’re doing anything complicated. The colon syntax is lovely for simple things, but I find that the function itself is more clear and easier when things start to get messy.

help?>
search: range LinRange UnitRange StepRange StepRangeLen

  range(start[, stop]; length, stop, step=1)

  Given a starting value, construct a range either by
  length or from start to stop, optionally with a
  given step (defaults to 1, a UnitRange). One of
  length or stop is required. If length, stop, and
  step are all specified, they must agree.

  If length and stop are provided and step is not, the
  step size will be computed automatically such that
  there are length linearly spaced elements in the
  range (a LinRange).

  If step and stop are provided and length is not, the
  overall range length will be computed automatically
  such that the elements are step spaced (a
  StepRange).

  stop may be specified as either a positional or
  keyword argument.

  │ Julia 1.1
  │
  │  stop as a positional argument requires at
  │  least Julia 1.1.

  Examples
  ≡≡≡≡≡≡≡≡≡≡

  julia> range(1, length=100)
  1:100

  julia> range(1, stop=100)
  1:100

  julia> range(1, step=5, length=100)
  1:5:496

  julia> range(1, step=5, stop=100)
  1:5:96

  julia> range(1, 10, length=101)
  1.0:0.09:10.0

  julia> range(1, 100, step=5)
  1:5:96
2 Likes