首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >状态机回放的流式数据

状态机回放的流式数据
EN

Stack Overflow用户
提问于 2010-09-02 05:03:17
回答 3查看 233关注 0票数 0

我有一个需要支持回放的状态机设计。我们有执行动作的状态,有时需要生成随机数。如果程序在FSM执行过程中关闭,程序需要使用与之前相同的随机数回放整个FSM。

举一个基本的例子,假设我有三个状态: A、B和C。FSM将调用一个状态的Execute()函数。在函数结束时,状态将发布一个事件,FSM将确定下一步转到哪个状态。在状态A中,它将调用rand()。如果数字是偶数,它将发布一个事件以转到状态B,否则状态C应该是下一个状态。

代码语言:javascript
复制
void StateA::Execute(IEventQueue& rQueue)
{
    int num = rand();
    if( num % 2 == 0 )
    {
        rQueue.PostEvent("GoToStateB");
    }
    else
    {
        rQueue.PostEvent("GoToStateC");
    }
}

如果随机数是69,那么它应该转到状态C。而在状态C中,程序可能会退出。当程序再次启动时,它应该回放状态机。显然,为了正确工作,它不能生成一个全新的随机数,它需要再次使用69才能准确回放。

我有一个文件流接口,可以用来将数据保存到文件中,但代码有点难看:

代码语言:javascript
复制
void StateA::Execute(IEventQueue& rQueue, IFileStream& rStream)
{

    int num = 0;

    // fails if there's no more data to read
    bool bSuccess = rStream.ReadInt(num);
    if (!bSucess)
    {
        num = rand();
        rStream.WriteInt(num);
    }

    // same code as before
}

这个解决方案的唯一问题是,我不需要先检查流中的数据,然后再有条件地写入相同的流。

我想像这样隐藏它:

代码语言:javascript
复制
void StateA::Execute(IEventQueue& rQueue, IStream& rStream)
{

    int num = 0;

    num = rand();
    rStream & num;

    // same code as before
}

在IStream内部,操作符&(可能不是重载的最佳用法)实际上会尝试从流中读取一个int。如果该流是空的,那么它将改为写入该流。和以前一样,行为是:先读到流的末尾,然后开始追加。

所以我想我的问题是:对于这种类型的回放,有没有一种我可能忽略的通用习惯用法?有没有人有其他的建议?我觉得我开始让设计变得有点复杂了。

谢谢!

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2010-09-02 07:35:03

为什么状态要与文件流直接交互?单一责任意味着我们应该有一个类,它的工作是基于某种逻辑提供适当的数字。

代码语言:javascript
复制
struct INumberSource {
    virtual int GenNextNumber() = 0;
}

// My job is to provide numbers from an RNG
struct RNGNumberSource : public INumberSource {
    virtual int GenNextNumber() {
        return rand();
    }
}

// My job is to write any numbers sourced through me to a file
// I delegate to another source to get an actual number
class FileStreamTrackingNumberSource : INumberSource {
public:
    FileStreamTrackingNumberSource(INumberSource& source, IFileStream& stream)
        : altSource(source), fileStream(stream) { }

    virtual int GenNextNumber() {
        int num = altSource.GenNextNumber();
        fileStream.WriteInt(num);
        return num;
    }
private:
    INumberSource altSource;
    IFileStream& fileStream;
}

// My job is to source numbers from a file stream delegating to an
// alternate source when I run out
class FileStreamNumberSource : public INumberSource {
public:
    FileStreamNumberSource(INumberSource& source, IFileStream& stream)
        : altSource(source), fileStream(stream), failedRead(false) { }

    virtual int GenNextNumber() {
        int num = 0;

        if(failedRead || !(failedRead = fileStream.ReadInt(num))) {
            num = altSource.GenNextNumber();
        }

        return num;
    }

private:
    INumberSource& altSource;
    IFileStream& fileStream;
    bool failedRead;
}

因此,在您的示例中,您需要将IFileStreamRNGNumberSource提供给FileStreamTrackingNumberSource,并将该IFileStream和相同的IFileStream提供给FileStreamNumberSource。该FileStreamNumberSource就是您要为州的INumberSource参数提供的内容。

假设您只需要数字来选择下一个州,那么您的州代码可能如下所示:

代码语言:javascript
复制
void StateA::Execute(IEventQueue& rQueue, INumberSource& numberSource)
{
    if( numberSource.GenNextNumber() % 2 == 0 )
    {
        rQueue.PostEvent("GoToStateB");
    }
    else
    {
        rQueue.PostEvent("GoToStateC");
    }
}
票数 1
EN

Stack Overflow用户

发布于 2010-09-02 05:13:32

我怀疑您应该有两个文件:一个记录您正在播放的事件,另一个用于读取“重放”事件。如果重放文件比“录制”文件长,那么这就是你用来重放的文件。

我也不会像你建议的那样使用运算符重载。也许只需要使用一个三元运算符。

票数 0
EN

Stack Overflow用户

发布于 2010-09-02 05:14:15

我不确定我是否理解“回放”背后的原理,但你不能简单地将“随机数或从文件中读取”的整个逻辑包装在一个类或函数后面吗?

更新

关于“回放”的主题和你的总体设计,我不确定FSM生成自己的刺激(即触发状态转换的随机数)是否正常。通常,刺激是从外部提供的。如果你重新考虑这一点,那么你就不再有这个混乱的问题了!

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/3622134

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档