Iterator

Iterator pattern is to provide an interface to produce consistent values while iterating over a list like an array. It facilitates uniformity for the iterations and unlike the standard iteration, it allows stepping iteration to happen. Iterator pattern in C is also known as linked-list implementation, where the elements in the list is created dynamically.

When to Use (Problems)

  • The problem is to iterate over a list of data consisting of inconsistent data structure/types to obtain the value.
  • The problem is to unify an iterator interface over various data structure lists.
  • The problem is to list an undefined length of data.

Example (Solution)

Although impractical in Go due to the existence of slice data type, iterator pattern is still doable for academic purposes:

package main

import (
        "fmt"
)

/***************************************************************************
 * Iterator Interface                                                      *
 ***************************************************************************/
type ArrayIterator struct {
        array []interface{}
        index int
}

func NewArrayIterator() *ArrayIterator {
        return &ArrayIterator{
                array: []interface{}{},
                index: -1,
        }
}

func (a *ArrayIterator) Remove(index int) {
        if index >= len(a.array) || index < 0 {
                return
        }

        a.array = append(a.array[:index], a.array[index+1:]...)
        if len(a.array) == 0 {
                a.index = -1
                return
        }
        if index >= len(a.array) {
                a.index = len(a.array) - 1
                return
        }
}

func (a *ArrayIterator) Add(d interface{}) {
        a.array = append(a.array, d)
        if len(a.array) == 1 {
                a.index = 0
        }
}

func (a *ArrayIterator) Value() interface{} {
        if a.index == -1 {
                return nil
        }
        return a.array[a.index]
}

func (a *ArrayIterator) HasNext() bool {
        return a.index+1 == len(a.array)
}

func (a *ArrayIterator) Index(index int) {
        if index >= len(a.array) || index < 0 {
                return
        }
        a.index = index
}

func (a *ArrayIterator) First() {
        a.index = 0
}

func (a *ArrayIterator) Next() {
        if a.HasNext() {
                a.index++
        }
}

/***************************************************************************
 * Data Structure                                                          *
 ***************************************************************************/
type frequency struct {
        data float32
}

type TV struct {
        channel *ArrayIterator
}

func NewTV() *TV {
        return &TV{
                channel: NewArrayIterator(),
        }
}

func (t *TV) Save(freq float32) {
        t.channel.Add(&frequency{data: freq})
}

func (t *TV) Delete(channel uint) {
        t.channel.Remove(int(channel))
}

func (t *TV) Next() float32 {
        t.channel.Next()
        return t.getChannelValue()
}

func (t *TV) First() float32 {
        t.channel.First()
        return t.getChannelValue()
}

func (t *TV) Switch(channel uint) float32 {
        t.channel.Index(int(channel))
        return t.getChannelValue()
}

func (t *TV) getChannelValue() float32 {
        f := t.channel.Value()
        return f.(*frequency).data
}

/***************************************************************************
 * User                                                                    *
 ***************************************************************************/
func main() {
        tv := NewTV()
        tv.Save(32.432)
        fmt.Printf("First Channel: %v\n", tv.First())
        tv.Save(105.70)
        tv.Save(99.9)
        tv.Save(214.23)
        fmt.Printf("Third Channel: %v\n", tv.Switch(2))
        tv.Delete(2)
        fmt.Printf("Third Channel: %v\n", tv.Switch(2))
}

The example program is to emulate a conventional television channel control. Notice that user only tune and save channel once, then switch channel accordingly? He/she does not need to always remember the frequency. Also, those frequency is only known during runtime / use. This is where iterator pattern applies.

Expected Outcome (Consequences)

  • A unified list of different data structures
  • The list is mutable, encapsulated hidden information, and facilitate reusability.