Example: Color interval written with manual iterators

In some cases, the item you want to iterate across does not implement the ISequenceable interface. You cannot modify it to directly implement this interface because it is a Java class from a third-party library. Although you cannot use Gosu shortcuts for custom iterable intervals using sequenceable items, you can still implement an iterable interval.

The following example demonstrates creating a custom iterable interval. Suppose you want to create a new iterable interval that can iterate across a list of predefined and ordered color names with a starting and ending color value. Define an enumeration containing the possible color values in their interval:

package example.gosu.interval

enum Color {
  Red, Orange, Yellow, Green, Blue, Indigo, Violet
}

All Gosu enumerations automatically implement the java.lang.Comparable interface, which is a requirement for intervals.

Next, create a new class that extends the following type:

IterableInterval<Color, Integer, void, ColorInterval>

Next, implement the methods from the IIterableInterval interface. It is important to note that in this example the iterator classes are inner classes of the main ColorInterval class.

package example.gosu.interval

uses gw.lang.reflect.interval.IterableInterval

class ColorInterval extends IterableInterval<Color, Integer, java.lang.Void, ColorInterval> {
  construct(left : Color, right : Color, stp : Integer) {
    super(left, right, stp)
    //print("new ColorInterval, with 2 constructor args")
  }

  construct(left : Color, right : Color, stp : Integer, leftOpen : boolean, 
    rightOpen : boolean, rev: boolean) {
    super(left, right, stp, null, leftOpen, rightOpen, rev)
    //print("new ColorInterval, with 6 constructor args")
  }

  // Get the Nth item from the beginning (left) endpoint
  override function getFromLeft(i: int) : Color {
    return Color.AllValues[LeftEndpoint.Ordinal + i] 
  }

  // Get the Nth item from the right endpoint
  override function getFromRight(i : int) : Color {
    return Color.AllValues[RightEndpoint.Ordinal - i] 
  }

  // Return standard iterator 
  override function iterateFromLeft() : Iterator<Color> {
    var startAt = LeftEndpoint.Ordinal
    if (!LeftClosed) {
      startAt++
    }
    return new ColorIterator(startAt) 
  }

  // Return reverse order iterator 
  override function iterateFromRight() : Iterator<Color> {
    var startAt = RightEndpoint.Ordinal
    if (!LeftClosed)
      startAt--
    return new ReverseColorIterator(startAt) 
  }

  // DEFINE AN INNER CLASS TO ITERATE ACROSS COLORS -- NORMAL ORDER
  class ColorIterator implements Iterator<Color>{
    protected var _currentIndex : int;

    construct() {
      throw "required start at # -- use other constructor" 
    }

    construct(startAt : int ) {
      _currentIndex = startAt
    }

    override function hasNext() : boolean {
      return ((_currentIndex) <= (RightEndpoint.Ordinal - (RightClosed ? 0 : 1)))
    }

    override function next() : Color {
      var i = _currentIndex
      _currentIndex++
      return Color.AllValues[i]
    }

    override function remove() {
      throw "does not support removing values"
    }
  }

  // DEFINE AN INNER CLASS TO ITERATE ACROSS COLORS -- REVERSE ORDER
  class ReverseColorIterator extends ColorIterator {

    construct(startAt : int ) {
      super(startAt)
    }

    override function hasNext() : boolean {
      return ((_currentIndex) >= (RightEndpoint.Ordinal + (LeftClosed ? 0 : 1))) 
    }

    override function next() : Color {
      var i = _currentIndex
      _currentIndex--
      return Color.AllValues[i]
    }
  }
}

Note the parameterized element type using Gosu generics syntax. It enforces the property that elements in the interval are mutually comparable.

Finally, you can use your new intervals in for loop declarations:

uses example.gosu.interval.Color
uses example.gosu.interval.ColorInterval

 print("Red to Blue as a closed interval...")
var colorRange = new ColorInterval(
  Color.Red, Color.Blue, 1, true, true, false )

 for (i in colorRange) {
  print(i)
}

 print("Red to Blue as an open interval...")
var colorRangeOpen = new ColorInterval(
  Color.Red, Color.Blue, 1, false, false, false )

 for (i in colorRangeOpen) {
  print(i)
}

This code prints:

Red to Blue as a closed interval...
Red
Orange
Yellow
Green
Blue
Red to Blue as an open interval...
Orange
Yellow
Green

See also