应一位程序员朋友要求,写了这篇fastm engine内部实现原理的技术文档。
递归分解-- Fastm Match Engine
Matching Rule
Fastm Template是一个树形结构。Dynamic下面包含Static Text, Variable, Dynamic三种节点。
Fastm Template里面除了静态文本,动态部分只有两类,Dynamic Block 和 Variable。
Fastm 的 Template DOM + Data的匹配过程,很类似于XSL的Pattern Match的递归组合。
Match ( Template Node, Current Data) {
Switch(Template Node)
Case Static Text: 直接显示
Case Variable: 从当前数据层根据Variable名称获取数据并显示
Case Dynamic: 从当前数据层根据Dynamic名称获取数据(下一层子数据),作为当前数据层,并且展开并且一个一个遍历当前数据,对于每一个数据项,Dynamic向下走一层,开始一个个遍历所有孩子节点。
(注意,这一步没有显示动作,只有非常复杂的数据展开和模板展开的动作。并进行n * m匹配的逻辑。n是数据子项的个数,m是Dynamic孩子的个数)
}
注意,重中之重。
从当前数据层根据Dynamic名称获取数据(下一层子数据),作为当前数据层,并且展开并且一个一个遍历当前数据,对于每一个数据项,Dynamic向下走一层,开始一个个遍历所有孩子节点。
这是整个匹配过程中,最核心的动作。这是理解整个匹配引擎的关键。
注意,这是3步动作。
第一步动作,遇到Dynamic,根据Dynamic Name从当前数据层,获取属性(子数据),然后把这个子数据作为当前数据。
第二步动作,就把子数据作为当前的数据展开:如果是Array, Collection,就一个一个展开。
第三步动作,对于其中每一个数据项,Dynamic的所有孩子都和分别这个数据项进行匹配。
这可以看作是两重循环。
Case Dynamic:
Property = get Property From (Current Data, Dynamic Name)
Current Data = Property
for each Item in the Current Data { // Current Data may be an array, a collection, etc
for each Template Node in the Current Dynamic Node {
match (Template Node, Item); // 递归调用Match
}
}
好了,我们来看整个递归过程。
Match ( Template Node, Current Data) {
Switch(Template Node)
Case Static Text: 直接显示
Case Variable: 从当前数据层根据Variable名称获取数据并显示
Case Dynamic:
Property = get Property From (Current Data, Dynamic Name)
Current Data = Property
for each Item in the Current Data { // Current Data may be an array, a collection, etc
for each Template Node in the Current Dynamic Node {
match (Template Node, Item); // 递归调用Match
}
}
}
简化起来看起来是这样。
Match(){
.. Match();
}
自己调用自己,形成了递归。
递归步骤分解
f() {
..f()..
}
叫做递归。
f() {
..g()..
}
g(){
f()
}
也叫做递归。所不同的只是,分成了2个函数。
f() {
..g()..
}
g(){
h()
}
h() {
..f()..
}
也叫做递归。所不同的只是,分成了3个函数。
我们把上面的Match ()分解。
分解步骤1:分出forEachDataItem
Match ( Template Node, Current Data) {
Switch(Template Node)
Case Static Text: 直接显示
Case Variable: 从当前数据层根据Variable名称获取数据并显示
Case Dynamic:
Property = get Property From (Current Data, Dynamic Name)
Current Data = Property
forEachDataItem(Current Dynamic Node, Current Data);
}
forEachDataItem(DynamicNode, Current Data){
for each Item in the Current Data { // Current Data may be an array, a collection, etc
for each Template Node in the Current Dynamic Node {
match (Template Node, Item); // 递归调用Match
}
}
}
现在的递归关系是
Match{
.. forEachDataItem()..
}
forEachDataItem{
..Match()..
}
分解步骤2:分出forEachDynamicChild
Match ( Template Node, Current Data) {
Switch(Template Node)
Case Static Text: 直接显示
Case Variable: 从当前数据层根据Variable名称获取数据并显示
Case Dynamic:
Property = get Property From (Current Data, Dynamic Name)
Current Data = Property
forEachDataItem(Current Dynamic Node, Current Data);
}
forEachDataItem(DynamicNode, Current Data){
for each Item in the Current Data { // Current Data may be an array, a collection, etc
forEachDynamicChildren(Dynamic Node, Item);
}
}
forEachDynamicChildren(Dynamic Node, Current Data){
for each Template Node in the Current Dynamic Node {
match (Template Node, Item); // 递归调用Match
}
}
现在的递归关系变成,
Match{
.. forEachDataItem()..
}
forEachDataItem{
..forEachDynamicChildren()..
}
forEachDynamicChildren{
..Match()..
}
递归步骤合并
递归步骤既然可以分解,自然可以合并。
我们把forEachDynamicChildren 和 Match合并。
forEachDataItem(DynamicNode, Current Data){
for each Item in the Current Data { // Current Data may be an array, a collection, etc
forEachDynamicChildren(Dynamic Node, Item);
}
}
forEachDynamicChildren(Dynamic Node, Current Data){
for each Template Node in the Current Dynamic Node {
Switch(Template Node)
Case Static Text: 直接显示
Case Variable: 从当前数据层根据Variable名称获取数据并显示
Case Dynamic:
Property = get Property From (Current Data, Dynamic Name)
Current Data = Property
forEachDataItem(Current Dynamic Node, Current Data);
}
}
现在的递归调用关系就变成
forEachDataItem{
..forEachDynamicChildren()..
}
forEachDynamicChildren{
.. forEachDataItem ()..
}
为什么我最后保留了这种递归形势。
首先是为了保证两层循环分别在不同的两个函数里面。其次为了尽量减少递归函数的个数。
数据分支代表逻辑分支
Fastm Template为什么不需要逻辑分支?
Fastm利用数据分支来表示逻辑分支。
0个数据,表示不显示当前Dynamic Block。(if false)
1个数据,表示显示当前Dynamic Block。(if true)
n个数据,重复n次表示显示当前Dynamic Block。(for, while).
这种分支动作只发生在Dynamic Block上面。
这里是理解的关键。
Case Dynamic:
Property = get Property From (Current Data,Dynamic Name)
Current Data = Property
forEachDataItem(Current Dynamic Node, Current Data);
程序根据Dynamic Name获取Property,
如果获取的Property为空(Null or Empty),forEachDataImte就什么都不做。这就相当于不显示当前分支;如果Property里面只有一个元素(或者Property不是集合或者数组类型,那么元素就是Property本身),就显示一次;如果Property里面有n( > 1)个元素(Property是元素个数为n的集合或者数组),那么就显示n次。
分享到:
相关推荐
标题“递归-----动态树实现递归”暗示我们将探讨如何利用递归方法来操作动态树。 首先,让我们理解什么是动态树。动态树,顾名思义,是一种可以随程序运行而改变结构的树形数据结构。与静态树不同,它的节点可以在...
java语法 method 递归 马克-to-win java视频 方法 重载
数据结构:栈与递归--含分治与回溯.ppt
- **递归的应用场景**:对于含有括号的复杂表达式,递归可以有效地分解问题,先计算最内层括号内的表达式,然后逐步向外扩展,最终得到整个表达式的值。 ##### 2. **C++基础语法** - **头文件的引入**:`#...
本资料“栈与递归--含分治与回溯”深入探讨了这两个主题,并将它们与分治策略和回溯法相结合,提供了一种高效解决问题的方法。 首先,栈是一种特殊的线性数据结构,遵循后进先出(LIFO)的原则。在计算机程序中,栈...
递归分治-1-阶乘.cpp
递归2-2.py
json-rules-engine是功能强大的轻量级规则引擎。 规则由简单的json结构组成,使其易于阅读且易于持久。 产品特点 以简单易读的JSON表示的规则 完全支持ALL和ANY布尔运算符,包括递归嵌套 默认情况下为快速,通过配置...
该问题可以分解为以下三个步骤: 1. 将n-1个圆盘从t1移动到t3(使用t2作为辅助):`move(n-1, t1, t3, t2)` 2. 将最大的圆盘从t1移动到t2 3. 将n-1个圆盘从t3移动到t2(使用t1作为辅助):`move(n-1, t3, t2, t1)` ...
递归关系则是指如何将大问题分解为更小的相似问题,然后用递归方式解决这些小问题。 例如,阶乘的计算可以使用递归来实现: ```cpp int factorial(int n) { if (n == 1) // 边界条件 return 1; else return n *...
数据结构(c语言) 对于汉诺塔的递归实现。在对学习数据结构递归的人,帮助他们对汉诺塔和递归思想的理解
在这个名为“turtle-递归美学-分形树-draw_branch.rar”的压缩包中,我们找到了一个名为`draw_branch.py`的文件,它展示了如何利用递归技术来创建美丽的分形树。分形是一种具有自相似性的几何形状,即无论在大尺度...
递归算法是一种解决问题的方法,它通过将问题分解成更小的子问题,直到找到解决方法为止。递归算法可以解决许多复杂的问题,如八皇后问题。 2. C语言的应用 C语言是一种广泛应用的编程语言,本文档展示了如何使用...
在VB.NET编程中,"树结点递归添加"是一个常见的操作,特别是在构建类似资源管理器的界面或处理文件系统目录结构时。递归是一种强大的编程技术,它允许函数调用自身来解决复杂的问题,特别适合处理具有层级关系的数据...
帮助新手理解C语言下移盘子问题,即Hanoi(汉诺)塔问题,更有助于新手对于C语言中对于递归函数的理解,强化程序逻辑问题。
n后问题---递归回溯法 n后问题---递归回溯法 n后问题---递归回溯法 n后问题---递归回溯法 n后问题---递归回溯法 n后问题---递归回溯法 n后问题---递归回溯法 n后问题---递归回溯法 n后问题---递归回溯法 n后问题---...
递归的概念基于函数调用自身的能力,这种自引用的过程可以解决一些复杂的问题,将它们分解为更小、更易于处理的部分。 在C++中,递归分为直接递归和间接递归。直接递归是指一个函数在其定义中直接调用自身,例如,...
递归情况则是将问题继续分解,直到达到基本情况为止。 1. **螺旋矩阵**: 螺旋矩阵是一种特殊的矩阵排列方式,元素按顺时针方向从外向内排列。在C++中,可以使用递归方法实现。首先,我们需要确定矩阵的边界条件,...