我有点问题。我正在制作有限自动机检查器。 给定输入和DFA,它会以接受状态结束。
我的问题是从另一个目标创建一个新的DFA_State。
DFA_State state0, state1, curr_state, init_state, temp; //fine, I think
state0 = new DFA_State();
state1 = new DFA_State();
state0 = new DFA_State("State 0",true, state0, state1); //fine, I think
init_state = new DFA_State(state0); //fine, I think
但是,这个问题正在惹恼。
temp = new DFA_State(curr_state.nextState(arr1[i]));
*
*
curr_state = new DFA_State(temp);
感谢您的帮助, 戴夫
编辑: 上帝,当我这样做时,我很迟钝,AFAIK,我只是没有直接思考,添加了将值设置为DFA_State对象的方法。
//in DFA_State class
public void set(DFA_State on_0, DFA_State on_1, Boolean is_accepting, String name){
this.on_0 = on_0;
this.on_1 = on_1;
this.is_accepting = is_accepting;
this.name = name;
}
//in main
DFA_State state0, state1, curr_state;
state0 = new DFA_State();
state1 = new DFA_State();
state0.set(state0, state1, false, "State 0");
state1.set(state1, state0, true, "State 1");
curr_state = state0;//initial state
//iterate across string input changing curr_state depending on char c
curr_state = getNextState(c);
//at end
if(curr_state.isAccepting())
System.out.println("Valid, " + curr_state.getName() + " is accepting);
else
System.out.println("Invalid, " + curr_state.getName() + " is not accepting);
答案 0 :(得分:0)
在第一行中,您将变量state0
,state1
,curr_state
,init_state
和temp
声明为{{1}类型的变量}。但是,只声明它们,它们尚未初始化。接下来的几行都没关系。第二行创建一个没有任何内容的状态并将其分配给DFA_State
,state0
的第三行也是如此。第四行会使用具有实际内容的新state1
覆盖您之前的state0
作业。第五行创建DFA_State
作为DFA_State
的副本,并将其分配给state0
。
假设这个和第二个代码块的第一行之间没有任何内容,现在你会遇到问题。您正在为init_state
分配一个新的temp
,该DFA_State
使用带有依赖于curr_state
的参数的复制构造函数。但到那时,该变量尚未初始化。仅仅因为它被声明并不意味着它已经以某种方式已经在内存中构建。当你在上面调用nextState
时,根本就没有变量可以解决这个问题。不要期望得到类似指针的东西,这些指针最终会指向你放入curr_state
的部分内容。
我只是猜测,但从你的代码风格来看,我会说你有C或C ++的背景知识。研究这些语言与Java之间的差异。如果可能的话,我还建议你让DFA_State
类不可变,因为这样更可靠并且可以避免错误。这意味着摆脱no-args构造函数。这是对它的修改(实际上没有编译,可能包含错误):
package foundations.of.computing;
/**
*
* @author Kayotic
*/
class DFA_State {
private final String state;
private final DFA_State on_0;
private final DFA_State on_1;
private final boolean isAccepting;
//private DFA_State dummy;
public DFA_State(DFA_State arg) {
//this(arg.is_accepting(), arg.on0(), arg.on1());
state = arg.get_name();
isAccepting = arg.is_accepting();
on_0 = arg.on0();
on_1 = arg.on1();
}
public DFA_State(String name, Boolean accepting, DFA_State on0, DFA_State on1) {
state = name;
isAccepting = accepting;
on_0 = on0;
on_1 = on1;
}
public String get_name(){
return state;
}
public Boolean is_accepting() {
return isAccepting;
}
public DFA_State on0() {
return on_0;
}
public DFA_State on1() {
return on_1;
}
public DFA_State nextState(char i) {
if (i == '0') {
return on0();
} else if (i == '1') {
return on1();
} else {
System.out.println("Error with input");
return null;
}
}
}
即使你不能使实例变量最终,最好至少将它们设为私有,因为你已经有了获取它们的方法。
答案 1 :(得分:0)
与面向对象相比,DFA的内存表示更好。
您应该使用简单的查找表:
int[] table = new int[vocabularyCount][stateCount];
每个州和每个单词都有一个数字,从0开始。 使用状态转换填充表,如果没有转换,则填充-1。现在你只需要状态和单词的翻译方法。
这是一个通用的DFA算法:
public boolean checkSentence(String s, int[] finishes) {
// fill table
int state = 0; // assuming S0 is the start state
for (int i = 0; i < s.length(); i++) {
state = table[translate(s.charAt(i))][s];
}
for (int i = 0; i < finishes.length; i++) {
if (finishes[i] == state) {
return true;
}
}
return false;
}
答案 2 :(得分:0)
该程序编写得很糟糕。在FoundationsOfComputing.java中查看此内容:
state0 = new DFA_State();
state1 = new DFA_State();
state0 = new DFA_State("State 0",true, state0, state1);
你基本上创建了3个状态实例 - 两个未初始化的实例(代码中的前两行) - 它们的所有实例变量都为null。
然后创建第三个实例,指向前两个未初始化的实例,并将其分配给state0变量。请注意,此时,它只是变量的值,而不是您在DFA-State构造函数中传递的值!那么,你现在在state0中所拥有的是一个指向两个未初始化状态的状态。
现在让我们看一下FoundationsOfComputing.java中的代码:
while (i < arr1.length) {//loops through array
System.out.println(i + ". scan shows " + arr1[i]);
temp = new DFA_State(curr_state.nextState(arr1[i]));
System.out.println(" "+curr_state.get_name()+ " moves onto " + temp.get_name());
curr_state = new DFA_State(temp);
i++;
}
我猜这会引发NullPointerException - 该代码移动到state0的on_0状态 - 这是一个尚未初始化的状态(它的所有实例变量都为null),因此在循环的后续传递中,当它调用时curr_state.nextState(无论如何),它将返回null,并且您尝试将其传递给将导致NPE的复制构造函数。
答案 3 :(得分:0)
好的,所以我们知道这是功课。让我们这样做,而不是告诉你答案让我们试着自己解决它。如果您看到NullPointerException(NPE)。抓住异常的第二行:
java.lang.NullPointerException: null
at com.blah.blah.SomeObject.someMethod(SomeArgumentType):1234 <<< here
....
该1234是包含SomeObject的文件中的行号。如果您转到该行号,您可以准确地看到NPE的生成位置。例如,如果第1234行是:
this.foo = bar.indexOf("caramel");
您可以轻松推断出什么是null。没有线索?那么这个永远不会为空,所以 this.foo 不是问题。如果此可能为null,则无法进入该方法,因为 this 指向您当前所在的实例。因此,解除引用变量的唯一其他语句是bar,因此 bar 必须为null。我们来看看你的代码:
temp = new DFA_State(curr_state.nextState(arr1[i]));
假设您发现上面的行正在抛出异常。那么可能有几件事可能是空的。 curr_state可以为null,或者arr1可以为null,在这种情况下,这一行会爆炸。但是,如果arr1 [i]为null或curr_state.nextState()返回null,那么您将看不到指向此行的NPE,但如果有人尝试在该方法参数上调用方法,则会出现构造函数。
希望通过了解异常堆栈跟踪,这将为您提供跟踪应用程序中的问题所需的工具。