Swift: The Lone Range-r

In Swift, you build ranges using .. and … operators. The two dot version denotes a “half closed” range that spans between the first argument up to but not including the second. The “closed” operator uses three dots. In its default format closed ranges go from the first argument up to and including the second. The first of the following examples prints out 1 through 4, the second 1 through 5.

for i in 1..5 {println("\(i)")}
for i in 1...5 {println("\(i)")}

So what happens when you reverse those parameters and try this with 5..1?

for i in 5..1 {println("\(i)")}

What you get is an infinite loop, as i goes from 5 to 6 to 7 and so forth. To get the behavior you expect, use  .by(), a function built into the range structure. This takes one argument, a step differential applied at each iteration.

for i in (5..1).by(-1) {println("\(i)")}

When you run this example, the half closed arguments produce the sequence 5, 4, 3, and 2. So what happens when you use full closed arguments? Not what you expect. The … operator adjusts the final argument to its next value. So when you run

for i in (5...1).by(-1) {println("\(i)")}

you get 5, 4, 3 and not 5, 4, 3, 2, 1. Kevin B tells me that that’s because a…b is defined as a..advance(b,1).

One last fun thing. Try using the built-in generate() function to establish a range generator from your range and then call it a few times. You’ll see the generator move through the values in your range at each call, returning nil once it passes the range’s end index.

range = 1..4
var generator = range.generate()
generator.next()
generator.next()
generator.next()
generator.next()
generator.next()

The generate() function returns a RangeGenerator. If you want to try a flipped range out, skip generate(). The by() function returns a StridedRangeGenerator. So you don’t call range.by(-1).generate(), as you’ve already created a generator.

5 Comments

  • you can also use ReverseRange to get a range that will enumerate backwards

    for i in ReverseRange(range: 1…5) {
    println(i)
    }

    prints: 5, 4, 3, 2, 1

  • Erica: a trick I think I did not see anywhere: ReverseRange an a ton of useful “undocumented objects” in swift can be found by right clicking on ‘false’ in a swift file and selecting Jump to Definition. It will open the swift framework “header” and let’s have access to the full list of predefined enum, struc, class, operator, func, protocols.

  • There is no need to say println(“(i)”). Just say println(i).

  • […] quick look at Ranges by Erica […]