创建递归迭代器

时间:2014-02-11 17:30:27

标签: java android recursion

我正在尝试为类的层次结构编写迭代器,这些类共同构成歌曲的组成部分。所有类都是抽象MusicComponent基类的实现,并继承了getChildren()函数。抽象MusicTime子类知道要播放的实际音符/和弦,其所有实现(例如quaver,crotchet)都会为null返回getChildren()

其他组件为MusicComponent,其中包含MusicTimes的集合,例如一次一个栏,Section,其中包含MusicComponentsSong保留构成歌曲的Sections,例如诗句,合唱,具有不同节奏/时间签名的部分。

我需要的是一个迭代器,它将遍历Sections中的所有Song,然后遍历MusicComponents中的所有Section,并且仅在找到{{1}时} descendant,根据音符类型以及包含MusicTime的时间签名和速度播放时间长度的音符。

对不起,如果信息太多,但这是我能解释我正在尝试做什么的唯一方法。那么我是否需要使用堆栈来处理这个问题,记录Section我已经访问过哪一个,或者只是使用递归来实现这一点?

3 个答案:

答案 0 :(得分:3)

你可以编写一个迭代器来“连接”子代的迭代器,甚至是懒惰的。然后在next()的迭代器上调用Song将深入查看SectionMusicComponent迭代器,最后传递下一个MusicTime

Guava让这很容易。将MusicComponent设为Iterable<MusicTime>并将iterator()实施为:

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

由于所有子节点都是MusicComponent并因此自己实现Iterable<MusicTime>Song的迭代器将是Section迭代器的串联,它们本身是{MusicTime的连接。 1}}迭代器。

这最后一个迭代器是一个特例。 MusicTime迭代器应该只返回一次:<​​/ p>

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

或者,Section的迭代器可以替换为:

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

有了这个,迭代就变得如此简单:

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

您现在可以执行任何类型的操作(播放,计算总持续时间......),而无需重新实现递归。

虽然有针对您的问题的替代解决方案,但这一切都归结为设计选择。例如,您可以在play上使用MusicComponent方法SongSection通过在所有子项上调用play来实现。这是一个简单的递归实现,但您必须为要在MusicComponent上添加的所有操作重复递归(例如playgetTotalDuration,...)。

如果您需要更多灵活性,可以使用Visitor design pattern并将播放操作设为访问者(例如PlayVisitor)。这样做的好处是,您可以决定从访问者中控制迭代顺序,但会增加新的MusicComponent实现更加困难。

答案 1 :(得分:0)

你需要保留自己的筹码。迭代器在找到它时必须返回下一个值。如果你可以递归地写它,比如说recursiveMethod调用自己,然后再调用它自己,然后在第三次调用它,它找出要返回的值,调用栈看起来像

recursiveMethod
recursiveMethod
recursiveMethod
theIterator.next
the client that's using the iterator

当最里面的recursiveMethod返回时,它必须将值返回给客户端,然后客户端必须继续运行。这意味着必须弹出调用堆栈上的前四个条目。然后,当客户端想要下一个值,并再次调用theIterator.next()时,必须重建调用堆栈上面的三个 recursiveMethod条目,以便进程可以继续。 Java没有办法做到这一点(至少通过Java 7;如果Java 8这样做,我不会知道它,因为我不熟悉所有新功能)。

我知道有多个调用堆栈的唯一方法是拥有多个线程。我不建议为此目的使用线程,但也许其他人已经成功使用它。

您可能需要阅读Wikipedia article on coroutines

答案 2 :(得分:0)

由于所有组件都继承自同一个抽象类MusicComponent,因此我将在该类中实现默认方法play()。假设MusicComponent类也具有属性protected List<MusicComponent> children,它将如下所示:

public void play(){

    for(MusicComponent component: children){
        component.play();
    }

}

MusicTime课程中,我会覆盖play()方法以实际播放如下音乐:

@Override
public void play(){

    //music playing logic
}

没有必要在其他类中重写此方法,因为它们只是无法播放音乐的复合类。

创建完整的组件树后。要播放所有音乐,您需要在最顶层的组件play()上运行Song方法:

song.play()

它会遍历所有孩子,最终到达播放音乐的最底层MusicTime个对象。这个答案是基于我对你的问题的解释以及我对Composite Design Pattern的理解,我认为这是解决问题的可行方法。