Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating a recursive iterator

I am trying to write an iterator for a hierarchy of classes that collectively make up the components of a song. All classes are implementations of the abstract MusicComponent base class and inherit a getChildren() function. The abstract MusicTime subclass knows the actual note/chord to play, and all its implementations (e.g. quaver, crotchet) return null for getChildren().

The other components are MusicComponent which holds a collection of MusicTimes e.g. a bar at a time, and Section which holds the MusicComponents. Song holds the Sections that make up the song e.g. verses, choruses, sections with different tempos/time signatures.

What I need is an iterator that will iterate through all Sections in a Song, then all MusicComponents in the Section and only when it finds a MusicTime descendant, play the note for the length of time based on its note type, and the time signature and tempo of its containing Section.

Sorry if too much info, but was the only way I could explain what I'm trying to do. So do I need to handle this with a stack, recording which MusicComponents I've visted or is there a way to do this just using recursion?

like image 957
Niall Avatar asked Feb 11 '14 17:02

Niall


People also ask

How do you make a recursive function iterative?

Initialize the accumulator before the while-loop. Use the negation of the base-case condition as the loop's condition. Use the recursive function's body (except the recursive call) as the body of the while-loop. After the loop, apply the base-case update of the accumulator and return its value.

What is a recursive iterator?

Iteration and recursion are key Computer Science techniques used in creating algorithms and developing software. In simple terms, an iterative function is one that loops to repeat some part of the code, and a recursive function is one that calls itself again to repeat the code.

Which is better recursion or iteration?

Iteration is faster and more efficient than recursion. It's easier to optimize iterative codes, and they generally have polynomial time complexity. They are used to iterate over the elements present in data structures like an array, set, map, etc.

What is the difference between iterative and recursive?

A program is called recursive when an entity calls itself. A program is call iterative when there is a loop (or repetition).


1 Answers

You can write an iterator which "concatenates" the iterators of its children, even lazily. Calling next() on a Song's iterator would then drill down through the Section and MusicComponent iterators and finally deliver the next MusicTime.

Guava makes this easy. Make MusicComponent an Iterable<MusicTime> and implement iterator() as:

@Override
public Iterator<MusicTime> iterator() {
    return Iterables.concat(getChildren()).iterator();
}

Since all children are MusicComponents and thus implement Iterable<MusicTime> themselves, Song's iterator will be a concatenation of Section iterators, which are themselves concatenations of MusicTime iterators.

This last iterator is a special case. A MusicTime iterator should only return itself once:

@Override
public Iterator<MusicTime> iterator() {
    return Iterators.singletonIterator(this);
}

Alternatively, Section's iterator could be replaced with:

@Override
public Iterator<MusicTime> iterator() {
    return getChildren().iterator();
}

With this, iterating becomes as easy as:

for (MusicTime time : song) {
    player.play(time);
}

You can now do any kind of operation (playing, counting the total duration,...) without re-implementing the recursion.

There are alternative solutions for your problem though, but it all comes down to design choices. For example, you could have a play method on MusicComponent which Song and Section would implement by calling play on all of their children. This is a straightforward recursive implementation, but you must repeat the recursion for all operations you intend to add on MusicComponent (such as play, getTotalDuration, ...).

If you need more flexibility, you could use the Visitor design pattern and make your play operation a visitor (e.g. PlayVisitor). This has the advantage that you can decide to control the iteration order from within the visitor, but makes it harder to add new MusicComponent implementations.

like image 131
Mattias Buelens Avatar answered Oct 14 '22 14:10

Mattias Buelens