使用DFA做文本编辑器的自动提示
之前看龙书的时候,龙书提到可以在编译器里用动态的生成的NFA自动机来动态匹配自己的输入串,NFA的简单实现其实写起来非常简单,但是我是实际凭感觉写完之后,却觉得并不是非常的好用,在处理自己已经输入过的串,如果还要处理空串和一个符号对应多种路径就势必涉及回溯,所以我就动态生成了一个DFA,应该不是最简的,但是也能满足需求。
DFA状态
package sample;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
/**
* Dfa 状态
*
* @author liufengkai
* Created by liufengkai on 16/7/10.
*/
public class DfaState implements Comparable<DfaState> {
private static int DFA_ID_COUNT = 0;
/**
* state id
*/
private int stateId;
/**
* transition set
* char / set of dfaState
*/
private Map<Integer, DfaState> transitionSet;
private DfaState parentState;
private Integer parentInput;
/**
* 构造方法
*
* @param input 输入串
* @param parentState 父节点
*/
public DfaState(Integer input, DfaState parentState) {
this.parentInput = input;
this.parentState = parentState;
this.stateId = DFA_ID_COUNT++;
this.transitionSet = new HashMap<>();
}
/**
* 添加一条转移语句
*
* @param input 输入字符
* @param state 下一个状态
* @return 返回添加状态
*/
public DfaState addTransition(int input, DfaState state) {
if (!transitionSet.containsKey(input)) {
transitionSet.put(input, state);
}
return state;
}
public DfaState getTransitionInput(int input) {
return getTransitionSet().get(input);
}
public int getStateId() {
return stateId;
}
public static int getTotalNumber() {
return DFA_ID_COUNT;
}
public Map<Integer, DfaState> getTransitionSet() {
return transitionSet;
}
public DfaState getParentState() {
return parentState;
}
@Override
public int compareTo(DfaState o) {
return 0;
}
public int getParentInput() {
return parentInput;
}
/**
* 打印状态
*/
public void printState() {
System.out.println("state : " + getStateId());
for (Integer integer : transitionSet.keySet()) {
System.out.println("symbol: " +
(char) integer.intValue() + " to :" +
transitionSet.get(integer).getStateId());
transitionSet.get(integer).printState();
}
}
/**
* 返回结束状态
*
* @param list 传入结束状态
*/
public void returnEndList(ArrayList<DfaState> list) {
for (Integer key : transitionSet.keySet()) {
DfaState cur = transitionSet.get(key);
if (cur.getTransitionSet().isEmpty()) {
list.add(cur);
} else {
cur.returnEndList(list);
}
}
}
}
DFAState定义了很多基础的方法,比如每个状态都有唯一的ID值与之对应,虽然是一个DFA但是插入的过程是发现下一个节点不同才分叉,由此看来每个状态都应该有唯一的父节点与之对应,使用了一个Map来记录我们有哪些路径,还有一个方法来递归查找终结节点,使用这个方法来倒序查找预测分析出的字符串,这并不是一个效率很高的方法,之后也会被替换。
DFABuilder创建DFA
package sample;
import java.util.ArrayList;
import java.util.HashMap;
/**
* Created by liufengkai on 16/7/10.
*/
public class DfaBuilder {
/**
* NFA 状态机的起始状态
*/
public DfaState startState = null;
/**
* 状态机的当前状态
*/
public DfaState currentState = null;
/**
* 接受状态
*/
public HashMap<Integer, DfaState> acceptState;
private static final int RETURN_ID = 13;
private static final int CHANGE_LINE_ID = 10;
private static final int TAB_ID = 9;
private static final int SPACE_ID = 32;
private ArrayList<Integer> endIdList;
private DfaCallBack dfaCallBack = null;
public DfaBuilder() {
// parent is null
this(new DfaState(null, null));
}
public DfaBuilder(DfaState startState) {
this.startState = startState;
this.currentState = startState;
initial();
}
/**
* 添加接受状态
*/
public void addAcceptState(int input, DfaState accept) {
if (!acceptState.containsKey(input)) {
acceptState.put(input, accept);
}
}
private void initial() {
this.acceptState = new HashMap<>();
this.endIdList = new ArrayList<>();
initialEndIdList();
}
private void initialEndIdList() {
endIdList.add(RETURN_ID);
endIdList.add(CHANGE_LINE_ID);
endIdList.add(TAB_ID);
endIdList.add(SPACE_ID);
}
public DfaState input(int input) {
// parser 了所有特殊情况 对于单词的提示
// 一个单词内是不会出现空格制表符和换行的
// System.out.println(input + "sss");
if (endIdList.contains(input)) {
this.currentState = startState;
return null;
}
// 处理了当输入串还在起始状态的情况
if (currentState.getStateId() == startState.getStateId()) {
return startInput(input);
}
// 说明状态不在起始状态
DfaState tempCurrent = currentState.getTransitionInput(input);
if (tempCurrent == null) {
tempCurrent = new DfaState(input, currentState);
currentState.addTransition(input, tempCurrent);
} else {
if (dfaCallBack != null) dfaCallBack.onMultipleSetBack(tempCurrent, tempCurrent.getTransitionSet());
}
currentState = tempCurrent;
return currentState;
}
/**
* 处理还在输入串起始状态的情况
*
* @param input 输入
* @return current状态
*/
public DfaState startInput(int input) {
DfaState current;
// 转入第一个起始状态
if (!acceptState.containsKey(input)) {
current = new DfaState(input, currentState);
this.addAcceptState(input, current);
} else {
current = acceptState.get(input);
if (dfaCallBack != null) dfaCallBack.onMultipleSetBack(current, current.getTransitionSet());
}
this.currentState = current;
return current;
}
public void setDfaCallBack(DfaCallBack dfaCallBack) {
this.dfaCallBack = dfaCallBack;
}
public void printDfa() {
for (Integer integer : acceptState.keySet()) {
System.out.println("接受状态 " + acceptState.get(integer).getStateId());
acceptState.get(integer).printState();
}
}
/**
* 重设startState
*/
public void resetStartState() {
this.currentState = startState;
}
}
DFABuilder定义了DFA的开始状态和现在的匹配的状态还定义了一些如何继续处理节点的方法。
重点应该看如下的这个方法:
public DfaState input(int input) {
// parser 了所有特殊情况 对于单词的提示
// 一个单词内是不会出现空格制表符和换行的
// System.out.println(input + "sss");
if (endIdList.contains(input)) {
this.currentState = startState;
return null;
}
// 处理了当输入串还在起始状态的情况
if (currentState.getStateId() == startState.getStateId()) {
return startInput(input);
}
// 说明状态不在起始状态
DfaState tempCurrent = currentState.getTransitionInput(input);
if (tempCurrent == null) {
tempCurrent = new DfaState(input, currentState);
currentState.addTransition(input, tempCurrent);
} else {
if (dfaCallBack != null) dfaCallBack.onMultipleSetBack(tempCurrent, tempCurrent.getTransitionSet());
}
currentState = tempCurrent;
return currentState;
}
endIdList里面包含一些提示匹配方法结束的标志,比如空格,换行,回车,制表符,每次匹配到这个的时候就把状态切换回初始状态。如果输入串还在初始状态即第一次输入,就添加一个接受状态,其余的就找到对应的DFAState步进或是插入就可以了。另外还定义了一个接口用于返回数据进行处理。
package sample;
import java.util.Map;
/**
* Created by liufengkai on 16/7/10.
*/
public interface DfaCallBack {
void onMultipleSetBack(DfaState current, Map<Integer, DfaState> states);
}
返回当前节点和接续的状态集。
DfaBuilder builder = new DfaBuilder();
builder.setDfaCallBack((current, states) -> {
System.out.println("current list " + getCurrentString(current));
ArrayList<DfaState> list = new ArrayList<>();
for (Integer key : states.keySet()) {
states.get(key).returnEndList(list);
}
for (DfaState state : list) {
System.out.println("prediction list " + getCurrentString(state));
}
});
public static String getCurrentString(DfaState currentState) {
String tempString = "";
DfaState tempState = currentState;
while (tempState.getParentState() != null) {
tempString = (char) tempState.getParentInput() + tempString;
tempState = tempState.getParentState();
}
return tempString;
}
使用的时候使用类似这样的方式就能实现简单的预测提示了。
效果图
我打算看看javaFx,写一个带gui的,不过现在有点懒,还没写完。

使用DFA做文本编辑器的自动提示的更多相关文章
- 用VC++MFC做文本编辑器(单文档模式)
用VC++MFC做文本编辑器(单文档模式) 原来做过一个用对话框实现的文本编辑器,其实用MFC模板里面的单文档模板也可以做,甚至更加方便,适合入门级的爱好者试试,现介绍方法如下: < xmlna ...
- ueditor取消文本编辑器的自动拉伸高度、宽度。
1.首先引入富文本编辑器 <script type="text/javascript" src="<%=basePath%>js/ueditor/ued ...
- 百度UEditor富文本编辑器去除自动追加p标签
本篇文章还原了我在遇到这个问题时的解决过程: 找到ueditor.all.js文件,搜索 me.addInputRule(function(root){ 或者直接搜索 //进入编辑器的li要套p标签 ...
- jquery 仿文本编辑器(智能提示输入文字)
1.前台代码 <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="InputAu ...
- [No0000139]轻量级文本编辑器,Notepad最佳替代品:Notepad++
在详细介绍Notepad++之前,先来解释一下,为何要选择Notepad++,即把常见的一些文本编辑器和Notepad++比较,看看其有哪点好: 常见的文本编辑器有很多,此处,只提及Notepad,N ...
- 关于layui富文本编辑器和form表单提交的问题
今天下午因为要做一个富文本编辑器上传文件给后台,所以看了一下layui的富文本编辑器,折腾了半天,终于把这玩意搞定了. 首先需要先创建layui的富文本编辑器 <textarea id=&quo ...
- Jquery 搜索框自动提示
为文本框增加自动提示下拉功能,比如输入 1,则从后台数据库查询出包含1 的字段,在文本框增加下拉列表供用户选择 ajax 返回数据为搜索查询字段的json集合 <script src=" ...
- ASP.NET输入文本框自动提示功能
在ASP.NET Web开发中会经常用到自动提示功能,比如百度搜索.我们只要输入相应的关键字,就可以自动得到相似搜索关键字的提示,方便我们快速的输入关键字进行查询. 那么在ASP.NET中,如果我们需 ...
- 放弃WebView,使用Crosswalk做富文本编辑器
版权声明: 欢迎转载,但请保留文章原始出处 作者:GavinCT 出处:http://www.cnblogs.com/ct2011/p/4100132.html 为什么放弃WebView Androi ...
随机推荐
- 【.NET深呼吸】元组数据(Tuple)
各位观众,大家好,欢迎收看由火星电视台直播的<老周吹牛>节目,注意:本节目没有任何技术含量,如果您没有兴趣,请砸掉电视机. 今天说一下System命名空间下的一个数据类型——Tuple,翻 ...
- Linux ERRNO
摘自Linux-3.18.20的头文件include/uapi/asm-generic/errno-base.h和include/uapi/asm-generic/errno.h: #define E ...
- C语言之链表list
#include <stdio.h> #include <stdlib.h> #include <stdbool.h> #include <string.h& ...
- 你真的会玩SQL吗?简单的数据修改
你真的会玩SQL吗?系列目录 你真的会玩SQL吗?之逻辑查询处理阶段 你真的会玩SQL吗?和平大使 内连接.外连接 你真的会玩SQL吗?三范式.数据完整性 你真的会玩SQL吗?查询指定节点及其所有父节 ...
- 代码的坏味道(11)——霰弹式修改(Shotgun Surgery)
坏味道--霰弹式修改(Shotgun Surgery) 霰弹式修改(Shotgun Surgery) 类似于 发散式变化(Divergent Change) ,但实际上完全不同.发散式变化(Diver ...
- php内核分析(七)-扩展
这里阅读的php版本为PHP-7.1.0 RC3,阅读代码的平台为linux. 我们研究下反射这个扩展. 反射这个扩展目录是存在在:ext/reflection.其实里面的代码很简单.一个.h文件,一 ...
- 基于NodeJS微信公众号
最近重新研究了微信公众号的高级接口,原来也利用C#或JAVA写过微信公众号,主要是消息的基础接口. 由于当时不知道微信公众号可以申请测试公众号,微信测试公众号基本上没有任何限制,对于开发来说是一个不错 ...
- Redis简单案例(三) 连续登陆活动的简单实现
连续登陆活动,或许大家都不会陌生,简单理解就是用户连续登陆了多少天之后,系统就会送一些礼品给相应的用户.最常见的 莫过于游戏和商城这些.游戏就送游戏币之类的东西,商城就送一些礼券.正值国庆,应该也有不 ...
- C# 之 EXCEL导入导出
以下方式是本人总结的一些经验,肯定有很多种方法,在此先记下,留待以后补充... 希望朋友们一起来探讨相关想法,请在下方留言. A-1:EXCEL模板导出 非常简单,将EXCEL模板上传到项目中后,将其 ...
- pwm 占空比 频率可调的脉冲发生器
module xuanpin #(parameter N=25)(clk,clr,key_in_f,key_in_z,f_out);input clk,clr,key_in_f,key_in_z;ou ...