布尔逻辑模拟器 - StackOverflowError

发布于 2024-12-14 02:13:02 字数 3016 浏览 0 评论 0原文

我正在创建一个布尔逻辑模拟器。我发布了一个有关应用程序的组织和一般设置的问题,它位于此处:组织简单的布尔逻辑模拟器 - Java。我认为这并不直接适用于其他问题,所以单独发布这个问题是可以的。请原谅我的代码,我对 Java 很陌生。

代码的组织实际上非常简单,但是当我尝试在分配语句中将变量分配给自身时遇到了问题。

例如,我们通常会使用这样的语句:

assign input1 * input2 + test3 * input2 to test3;

当input1和input2为高电平时,test3将变高。此时,input1 可能会退出,而 test3 仍会很高,因为它在语句中引用了自身。 (注意:* => AND,+ => OR)正如您在下面看到的,当变量在上面的赋值语句中引用自身时,我遇到了问题。那是因为当我尝试获取上面语句的值时,我得到:

input1 * input2 + (input1 * input2 + (input1 * input2 + test3 * input2) * input2) * input2

等。它只是尝试评估其语句反复。

public interface Argument {
    public boolean evaluate();
}

这是存储有关每个位的信息的“Bit”类。该位可以是“硬连线”真或假,也可以有一个分配给它的语句。

public class Bit implements Argument {

    private String name;
    private boolean value;
    private Argument assign;
    private boolean assigned;

    Bit( String name, boolean value ) {
        this.name = name;
        this.value = value;
    }

    @Override
    public boolean evaluate() {
        if ( assigned == true ) {
            return assign.evaluate();
        } else {
            return value;
        }
    }

    public void setValue( boolean value ) {
        this.value = value;
    }

    public void setAssign( Argument assign ) {
        this.assign = assign;
        this.assigned = true;
    }

}

这是Operation类的一个例子;其他的都非常相似。

public class OrOperation implements Argument {

    private Argument argument1, argument2;

    public OrOperation( Argument arg1, Argument arg2 ) {
        this.argument1 = arg1;
        this.argument2 = arg2;
    }

    @Override
    public boolean evaluate() {
        return argument1.evaluate() || argument2.evaluate();
    }

}

解析器类:我只包含了其中的一部分,因为实际解析并不那么相关。 请注意,当我将变量分配给自身时,会产生错误。在下面的第二个测试中,我将 test3 分配给 test3。我可以推断这是因为 test3 尝试确定自身的值,一遍又一遍地调用第二个语句。

public class Parser {

    public HashMap<String, Bit> bits = new HashMap<String, Bit>();

    Parser(String file) {

        bits.put( "test1", new Bit( "test1", true ) );
        bits.put( "test2", new Bit( "test2", true ) );
        bits.put( "test3", new Bit( "test3", false ) );
        bits.put( "test4", new Bit( "test4", true ) );

        // Works great
        bits.get("test3").setAssign( parseStatement("test1 * ~test2 + test4 * test2") );
        System.out.println( bits.get("test3").evaluate() );

        // Produces error
        bits.get("test3").setAssign( parseStatement("test1 * test2 + test3 * test2") );
        System.out.println( bits.get("test3").evaluate() );

    }
}

解析器基本上创建如下语句:(取自上一个问题)

Operand op = new AndOperand( register1, new OrOperand( register2, register3 );
boolean output = op.evaluate(); // this is register1 && (register2 || register3 )

希望这足够清楚。如果对问题所在或我正在尝试做什么有任何疑问,请告诉我。提前致谢。

I'm creating a Boolean logic simulator. I've posted a question regarding the organization and general setup of the application and it's located here: Organizing Simple Boolean Logic Simulator - Java. I figured this doesn't apply directly to the other question so it would be OK posting this separately. Please excuse my code, I'm very new to Java.

The organization of the code is actually quite simple, but I've run into a problem when I try to assign a variable to itself in an assign statement.

For example, we'll commonly use statements like this:

assign input1 * input2 + test3 * input2 to test3;

When input1 and input2 are high, test3 will become high. At that point, input1 could drop out and test3 would still be high because it refers to itself in the statement. (Note: * => AND, + => OR) As you can see below, I'm having problems when a variable refers to itself in its assign statement like above. That's because when I attempt to get the value of the statement above I'm getting:

input1 * input2 + (input1 * input2 + (input1 * input2 + test3 * input2) * input2) * input2

Etc. It just attempts to evaluate its statement over and over again.

public interface Argument {
    public boolean evaluate();
}

Here is the "Bit" class that stores information about each bit. The bit can either be a "hard-wired" true or false, or it can have a statement assigned to it.

public class Bit implements Argument {

    private String name;
    private boolean value;
    private Argument assign;
    private boolean assigned;

    Bit( String name, boolean value ) {
        this.name = name;
        this.value = value;
    }

    @Override
    public boolean evaluate() {
        if ( assigned == true ) {
            return assign.evaluate();
        } else {
            return value;
        }
    }

    public void setValue( boolean value ) {
        this.value = value;
    }

    public void setAssign( Argument assign ) {
        this.assign = assign;
        this.assigned = true;
    }

}

This is an example of the Operation class; the others are very similar.

public class OrOperation implements Argument {

    private Argument argument1, argument2;

    public OrOperation( Argument arg1, Argument arg2 ) {
        this.argument1 = arg1;
        this.argument2 = arg2;
    }

    @Override
    public boolean evaluate() {
        return argument1.evaluate() || argument2.evaluate();
    }

}

Parser class: I've only included part of this, since the actually parsing isn't all that relevant. Note that when I assign a variable to itself the error is produced. In the second test below, I assigned test3 to test3. I can infer that this is because test3 attempts to determine the value of itself, calling that second statement over and over again.

public class Parser {

    public HashMap<String, Bit> bits = new HashMap<String, Bit>();

    Parser(String file) {

        bits.put( "test1", new Bit( "test1", true ) );
        bits.put( "test2", new Bit( "test2", true ) );
        bits.put( "test3", new Bit( "test3", false ) );
        bits.put( "test4", new Bit( "test4", true ) );

        // Works great
        bits.get("test3").setAssign( parseStatement("test1 * ~test2 + test4 * test2") );
        System.out.println( bits.get("test3").evaluate() );

        // Produces error
        bits.get("test3").setAssign( parseStatement("test1 * test2 + test3 * test2") );
        System.out.println( bits.get("test3").evaluate() );

    }
}

The parser basically creates the statements like so: (taken from previous question)

Operand op = new AndOperand( register1, new OrOperand( register2, register3 );
boolean output = op.evaluate(); // this is register1 && (register2 || register3 )

Hopefully this was clear enough. Let me know if there's any question as to what the problem is or what I'm trying to do. Thanks in advance.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

半山落雨半山空 2024-12-21 02:13:02

假设这是一个模拟器/评估器(而不是方程求解器),那么您需要将系统建模为随时间变化的状态。因此,语句:

assign test1 * input1 to test1;

意味着 test1t+1 的状态是 test1t * input1t

您的代码所做的不是区分 test1t 和 test1t+1 ...,因此导致无限递归。

一种解决方案是将状态(变量的值)与方程分开表示,并且使用不同的数据结构来表示 t 时刻的状态和 t + 时刻的状态1..

Assuming that this is a simulator / evaluator (and not an equation solver), then you need to model the system as states that change over time. Thus, the statement:

assign test1 * input1 to test1;

means that the state of test1t+1 is test1t * input1t.

What your code is doing is not distinguishing between test1t and test1t+1 ... and as a result is causing infinite recursion.

One solution is to represent the state (the values of the variables) separately from the equations, and furthermore to use different data structures to represent the state at time t and the state at time t + 1.

全部不再 2024-12-21 02:13:02

您所描述的是电路中的反馈回路。它们确实存在于现实中,在较低级别上它们用于例如存储元件(触发器和锁存器),在较高级别上您可以将其用于具有确实需要反馈的操作的电路(例如IIR滤波器)。

在这个更高级别的“应用程序”中,您在不同(逻辑)操作之间有寄存器。
这就是你如何将循环纳入你的逻辑中。然后,您必须计算结果作为时间的函数。

您可以从使用复位值(通常为 0)循环的寄存器/输入的内容开始。然后开始计算。您可以在中间的寄存器处中断循环,并通过赋值语句根据前一个周期的值计算一个周期的值。这也意味着即使输入不改变,输出也会随着时间步长的变化而变化。对于每个输出,您也拥有的不是单个输出值,而是输出序列(随时间变化)。

另一种选择(我建议您使用一个简单的模拟器)是禁止此类循环,并在识别它们时抛出错误消息。

如果这是用于生产,我只能建议:不要再次发明轮子。这不是一个新问题。例如,有硬件描述语言(例如 Verilog 或 VHDL),其中存在一些模拟器(例如,如果您想要免费的,可以使用来自 synopsys 的 vcs 或 verilator)。

What you describe is a feedback loop in the circuit. They exist indeed in reality, on a lower level they are used e.g. for memory elements (flip flops and latches), on higher level you can use it for circuits that have operations that require indeed a feedback (e.g. IIR filter).

In this higher level "applications" you have registers between different (logic) operations.
This is how you get cycles into your logic. You then have to compute your results as function of time.

You start with the contents of the registers/inputs that loop with a reset value (usually 0). And then begin the computation. You can break the loop at the registers in between, and compute the values for a cycles by your assign statements from the values of the previous cycle. This also means that a output can change from time step to time step even when the input do not change. You have also for every output not a single output value, but sequence of outputs (changing over time).

Another option (which I would suggest as you have a simple simulator) is to prohibit such loops and throw an error message when you recognize them.

If this is for production I can only recommend: To NOT invent the wheel again. This is not a new problem. There are e.g. hardware description languages out there (e.g. Verilog or VHDL), where a couple of simulators exist (e.g. vcs from synopsys or verilator if you want a free one).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文