有限状态机处理字符
https://www.cnblogs.com/ssaylo/p/13130138.html ,先看这篇文章,了解 HTTP 请求是如何发送与接收的。
前言——如何在不适用正则与 JavaScript Api 接口的条件下查找字符数按所在的位置?
winter 前端进阶训练营第六周
有限状态机
-
每一个状态都是一个机器
-
每一个机器知道下一个状态
- moore(每个机器都有确定的下一个状态)
- mealy(根据输入决定下一个状态)(mealy 是个人名,不用去管他)
使用有限状态机处理字符串
-
v1.0
// 不用 api,不用正则,写一个 match function match(string) { for (let c of string) { if (c === 'a') { return true; } } return false; } match('I m groota');
-
v2.0
// 这里会有个问题, "a" 与 "b" 不相邻的时候也会判断为 true,怎么办呢??? function match(string) { let foundA = false; for (let c of string) { if (c === 'a') { foundA = true; } else if (foundA && c == 'b') { return true; } } return false; } console.log(match('I acbm groot')); // 很简单 function match2(string) { let foundA = false; for (let c of string) { if (c === 'a') { foundA = true; } else if (foundA && c === 'b') { return true; } else { //////////////////////在这里多加一句话////////////////////// foundA = false; } } return false; } console.log(match2('I acbm groot'));
-
v3.0
// 刚在写的是匹配 a,b 两个变量,那么如果要匹配三个变量呢?如果要匹配四个变量呢?在这里我们就使用到了有限状态机 // 首先我们定义五个状态,初始值为 false // 然后在查找过程找,对状态进行变更 // 字符串中存在字符,便变更对应的状态为 true // 倘若一旦有一个字符不匹配,重置所有的状态 function match(string) { let foundA = false; let foundB = false; let foundC = false; let foundD = false; let foundE = false; for (let c of string) { if (c === 'a') foundA = true; else if (foundA && c === 'b') foundB = true; else if (foundB && c === 'c') foundC = true; else if (foundC && c === 'd') foundD = true; else if (foundD && c === 'e') foundE = true; else if (foundE && c === 'f') return true; else { foundA = false; foundB = false; foundC = false; foundD = false; foundE = false; } } return false; } console.log(match('I abm groot')); console.log(match('I abcdef'));
-
v4.0
// 每个函数就是一个状态 // 函数参数就是输入 // 返回的是下一个状态 function state(input) { return next; } function match(string) { let state = start; for (let c of string) { console.log(c); state = state(c); } return state === end; } function start(c) { if (c === 'a') return foundA; else return start; } function end(c) { return end; } function foundA(c) { if (c === 'b') return foundB; else return start(c); } function foundB(c) { if (c === 'c') return end; else return start(c); } function foundC(c) { if (c === 'd') return foundD; else return start(c); } function foundD(c) { if (c === 'e') return foundE; else return start(c); } function foundE(c) { if (c === 'f') return end; else return start(c); } console.log(match('aacbc')); console.log(match('aacbcdefg'));
-
v5.0
function match(string) { let state = start; for (let c of string) { state = state(c); } return state === end; } function start(c) { if (c === 'a') return foundA; else return start; } function end(c) { return end; } function foundA(c) { if (c === 'b') return foundB; else return start; } function foundB(c) { if (c === 'c') return foundC; else return start; } function foundC(c) { if (c === 'a') return foundA2; else return start; } function foundA2(c) { if (c === 'b') return foundB2; else return start; } function foundB2(c) { if (c === 'x') return end; else if (c === 'c') return foundC; else return start; } console.log(match('abcabcabx'));
-
匹配“abababx”
-
可选: 撞击处理完全未知的pattern
-
参考: 字符串KMP算法
(这里先留个坑位了,算法刷完回来解决)
(参考链接)
-
-
HTML parser
第一步: 拆分文件
- 为了方便文件管理,把parse单独拆到文件中
- parser接受HTML文本作为参数,返回一颗DOM树
- 见parse-1.js
第二步: 创建状态机
- 使用FSM实现HTML分析
- 在HTML标签中,已经规定了HTML的状态
- 见parse-2.js
第三步: 解析标签
- 主要的标签有:开始标签,结束标签和自封闭标签
- 见parse-3.js
第四步: 创建元素
- 在状态机中,除了状态迁移,我们还要加入业务逻辑
- 在标签结束状态提交标签token
- 见parse-4.js
第五步: 处理属性
- 属性值分为单引号、双引号、无引号三种写法,因此需要较多状态处理
- 处理属性的方式和标签类似
- 属性结束时,把属性加到标签token上
- 见parse-5.js
第六步: 构建DOM树
- 从标签构建DOM树的基本技巧是使用栈
- 遇到开始标签时创建元素并入栈,遇到结束标签的时候出栈
- 自封闭标签可以视为入栈后立刻出栈
- 任何元素的父元素是它入栈前的栈顶
- 见parse-6.js
第七步: 处理文本节点
- 文本节点与自封闭标签处理类似
- 多个文本节点需要合并
- 见parse-7.js
CSS Computing
第一步: 收集CSS规则
- 遇到 style 标签时,我们把 CSS 规则保存起来
- 我们调用 CSS Parser 来分析 CSS 规则
- 我们必须要仔细研究此库分析 CSS 规则的格式
- 见computedCSS-1.js
第二步: 添加调用
- 当我们创建一个元素后,立即计算 CSS
- 理论上,当我们分析一个元素时,所有 CSS 规则已经收集完毕
- 在真实浏览器中,可能遇到写在 body 的 style 标签,需要重新 CSS 计算的情况,这里我们忽略
- 见computedCSS-2.js
第三步: 获取父元素序列
- 在 computeCss 函数中,我们必须知道元素的所有父元素才能判断元素与规则是否匹配
- 我们从上一步骤的 stack,可以获取本元素所有的父元素
- 因为我们首先获取的是”当前元素“,所以我们获得和计算父元素匹配的顺序是从内向外
- 见computedCSS-3.js
第四步: 拆分选择器
- 选择器也要从当前元素从外排列
- 复杂选择器拆成针对单个元素的选择器,用循环匹配父元素队列
- 见computedCSS-4.js
第五步: 计算选择器与元素匹配关系
- 根据选择器的类型和元素属性,计算是否与当前元素匹配
- 这里仅实现了三种基本选择器,实际的浏览器中要处理复合选择器
- 见computedCSS-5.js
第六步: 生成Computed属性
- 一旦选择匹配,就应用选择器到元素上,形成 computedStyle
- 见computedCSS-6.js
第七步: 确定覆盖关系
- CSS 规则根据 specificity 和后来优先规则覆盖
- specificity 是个四元组,越左边权重越高
- 一个 CSS 规则的 specificity 根据包含的简单选择器相加而成
- 见computedCSS-7.js