javascript - 如何构建谓词/表达式/规则/ Action 引擎

标签 javascript php expression predicate

好吧,首先,对不起TL; DR ...

第二,关于我要实现的目标的一些背景信息。

我有一个脚本,可以处理给定的文件并根据一组定义的规则生成一个新文件。文件中的每一行都有一个值,脚本通常会评估该值并基于该值和规则生成其他值。生成的文件包含原始值和csv格式的生成值。

例如:

输入文件:

row1value
row2value


输出文件:

"row1value","generatedValue1","generatedValue2","generatedValue3"
"row2value","generatedValue1","generatedValue2","generatedValue3"


例如,假设源文件在每一行上都有一个以冒号分隔的值,并且目标是用冒号将值分开并将每个子值放入列中。这就是“之前”和“之后”的样子:

输入文件:

a:b:c
some:random:value


输出文件:

"a:b:c","a","b","c"
"some:random:value","some","random","value"


因此,要生成行,我可能需要:

function generateRow ($key) {
  // $key is the value for the current row being processed from the source file
  // $row is the array that will contain the columns to be inserted to the output file
  $row = array();

  // generate the column values
  $row = explode(":",$key);

  // make the original value ($key) the first column
  array_unshift($row, $key);

  // return the array. some other function will fputcsv it
  return $row;
}


另一个示例是,如果源文件包含如下所示的值行:

[prefix]:[url]

输出文件中的列的值为:


column1 =完整的原始值(永远是这个)
栏2 = [prefix]
column3 = [url]
column4 =解析[url]以获取网址域
column5 =解析文件扩展名的[url]


所以我写generateRow()来做这些事情。

好吧,我拥有了这一切。一切都很闪闪发亮,效果很好。我收到请求以使新的“进程”接收它们自己的文件,并根据定义的规则生成带有列的新文件。我只是想提供所有这些作为背景知识,以便将其置于上下文中,以便大家都可以更轻松地理解我的实际需求。

因此,“问题”在于,“进程”是由编码器(我)根据规范写出generateRow()来进行的。对于我自己或任何了解php的人来说,这并不是特别困难。但这并不是“用户友好的”,因为某些利益相关者希望自己负责创建这些内容,但他们不是编码人员。

因此,我现在的任务是基本上为generateRow()提供Web界面。因此,按照我的观察方式,我基本上需要创建一个表单,该表单根据先前选择的项目动态生成表单元素和可能的选项。换句话说,一个表达引擎。或谓词引擎。或规则构建器。坦率地说,我不确定这些术语中哪一个最准确(如果有的话),但是我一直在做大量的谷歌搜索和阅读工作,而到目前为止,我一直在想这些事情。

因此,例如,该表单首先要求用户创建条件或赋值表达式,

[下拉菜单:if | set]

IF:如果用户单击“ if”,则会显示另一个下拉列表,其中包含要检查的“变量”列表。例如,系统将提供对其他列,源文件中的任何列(包括键),导入文件名或用户先前创建的任何自定义变量的引用作为选择。然后,该表格将显示一个“操作”下拉菜单,其中将显示诸如“已设置”,“未设置”,“大于”,“正则表达式”等内容。如果用户选择“已设置”或“未设置”之类的内容,则不会输出其他字段。但是,如果用户选择了“大于”,则将显示另一个下拉列表,要求从“变量”列表中进行选择,或者在输入字段中输入值。例子:

IF column1 "is set"           /* check if column1 is set */
IF column1 contains "foobar"  /* check if column1 contains "foobar" */
If column1 regex "^[a-z]+$"   /* check if column1 contains only letters */


定义后,用户可以在其中添加“设置”表达式。为简单起见,我认为我不需要嵌套条件或使用AND | OR来构成复合条件。

设置:因此,如果用户选择此选项,则表单将指导他们构建赋值表达式。第一个下拉菜单将保存用户可以为值分配的变量,例如输出文件的列或临时变量,以便可以在其他表达式中引用它们。例子:

SET [column1] [=] ["foobar"]
SET [column2] [=] [column1]
SET [column1] [regex] [userVar1] ["^[^:]+"]
SET [userVar1] [explode] [key] [":"] 


好吧,我不确定这是呈现它的“最佳”方法,但希望您能理解。

然后,我将保存这些表达式,然后编写php代码以对其进行评估;基本上将它们翻译成实际的php代码。我认为我实际上在那方面还不错:我只会使用解释器模式。

但是我需要帮助的地方是整个表达式“映射” /“构建器”部分。实际上,如果我可以对可能的表达式进行“映射”,我什至可以摆动“ builder”部分。

是的,这就是我问题的核心所在:如何绘制可能的表达式。目前,我一直在尝试将其结构化/映射为xml,但是我似乎无法掌握如何做到这一点,除了仅对每种可能的路径进行硬编码。首先,对我来说,这似乎效率不高,就像应该有一种更聪明的方法来做到这一点。它不可能容易地扩展...假设“处理”#1的规则集有2个源文件列可供绘制,并生成了5个输出文件列。.“处理”#2的规则集为1和4?如何考虑用户可以设置的可变数量的用户定义变量。

那么,是否有人有提示或链接到tut,说明如何进行此类操作?或者更好的是预制(php)解决方案会很好,但是我还没有找到任何东西。

编辑:这是我现在的位置的示例,希望可以更好地理解我的问题。

例如,如果我仅对地图(xml)进行硬编码,这就是排序会下降的方式

<expressions>
  <expression type='if'>
    <variable name='column1'>
      <operator type='isset'></operator>
      <operator type='notset'></operator> 
      <operator type='equals'>
        <variable name='column1' />
        <variable name='column2' />
        <variable name='column3' />
      </operator>
      <operator type='greaterThan'>
        <variable name='column1' />
        <variable name='column2' />
        <variable name='column3' />
      </operator>
      <operator type='lessThan'>
        <variable name='column1' />
        <variable name='column2' />
        <variable name='column3' />
      </operator>
    </variable>
    <variable name='column2'>
      <operator type='equals'>
        <variable name='column1' />
        <variable name='column2' />
        <variable name='column3' />
      </operator>
      <operator type='greaterThan'>
        <variable name='column1' />
        <variable name='column2' />
        <variable name='column3' />
      </operator>
      <operator type='lessThan'>
        <variable name='column1' />
        <variable name='column2' />
        <variable name='column3' />
      </operator>
    </variable>
</expressions>


夫妻注意事项:


有任意数量的variable节点。每个源文件列将有一个,每个输出文件列名称将有一个,用户即时创建的每个变量将有一个,等等。
operator类型的数量可变。我只展示了3个示例,但到目前为止,我已经想到了20个示例
请注意,某些operator类型将如何要求在表单上输出其他表单字段。例如,“ if [column1] [isset]” vs.“ if [column1] [greaterThan] [column2]”
expression节点显示“ if”表达式的外观。还将有一个类型为“ set”的expression节点,该节点具有不同的结构,但原则上是相同的。例如,可以有一个表达式“ [set] [column2] [equals] [column1]”或“ [set] [column1] [regex] [sourceColumn1] [输入值(regex)]”


因此,现在我可以沿着树上的可用路径动态构建菜单。

但是正如您所看到的,似乎有很多重复,而且将新事物投入组合(例如新运算符)也是皮塔饼。所以这里的问题是,我将如何更好地组织这种结构?还是只是不可能,而唯一的解决方案是仅对每种可能性进行硬编码?

最佳答案

哇,这是个好问题!我将首先回答您的直接问题,然后再探讨一般性问题。

如果您想为此购买现成的产品,则可以使用诸如3dfacto或Drools之类的“产品配置”系统,它们采用一组规则(在这种情况下,是有关系统规则构造方式的规则),并可以创建动态表单来仅允许输入有效规则。实际上,它与您上面编写的XML并没有太大区别。让我们看看发生了什么:

首先,您要定义一种语言来表达系统中的规则。您的语言结构称为语法,我们通常使用BNF来描述语法。例如:

command := <if> | <set>
if := "if" <boolexpr> "then" <command>
boolexpr := <column> isset |
            <column> notset | 
            <column> "greaterThan" <column> |
            <column> "lessThan" <column>
<column> := "column1" | "column2" | ... | "column_n"
set := <column> <equals> <value> | ...
value := <column> | <number>


请注意,语法的某些部分可能需要包含代码。例如,“列”部分是动态的,具体取决于实际列数。您可以为Drools规则引擎take a look at the grammar,尽管其中混入了很多代码。

语法的BNF表示形式与XML映射之间的主要区别在于:前进和后退链接。两者完全相同,但是BNF比XML更紧凑。这是因为可以引用子结构,而不是照原样复制。例如,我们可以在任何地方使用<value>规则,无论它是列还是文字数字。另一种思考的方式是通过规则的路径比规则多。

我们如何在代码中表示语法?一种方法是使用数组。

$column = a_function
$boolexpr = Array("or", Array("match", $column, "isset"), Array("match", $column, "notset"), Array("match", $column, "greatherthan", $column), Array("match", $column, "lessthan", $column))
$command_grammar = Array("match", "If", $boolexpr, "then", $command)


您的表单构建器系统实际上将对该语法进行抽象解释。您在问:“到此为止,接下来的有效输入是什么?”然后,通过使用到目前为止输入的规则解释语法,然后查看下一步。您提到了“解释器”模式,所以我认为您对此很熟悉。

想到它的另一种方法是创建一个解析器。通常,解析器会尝试通过语法规则尝试匹配输入的所有可能路径,并在以下任何时间拒绝任何路径:


到目前为止,输入和路径有所不同
路径已完成,但还有更多输入剩余。
输入已完成,但路径上还有其他非空语法规则。


在您的系统中,(1)和(2)永远不会发生,因为您不允许输入无效的规则。 (3)之所以会发生,是因为用户尚未完全指定规则,因此您可以查看接下来要使用哪些语法规则来更新表单。

让我们退后一步,看看我们要做什么:允许非程序员对计算机进行编程。一个经常出现的问题是,尝试使此类系统具有灵活性,导致它们成为自己的编程语言。通常,它们比现有的语言差,然后您需要成为一名程序员才能使用它。我认为您说一些正确的主意,例如“或”会太复杂。限制复杂性可以避免这种麻烦。

避免该问题的另一种方法是利用现有语言,例如PHP。您可以定义使任务更轻松的函数,并让最终用户以您的示例为指导直接编写PHP。持续反馈在这里有帮助。请查看Processing作为此方法的示例。

关于javascript - 如何构建谓词/表达式/规则/ Action 引擎,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19201326/

相关文章:

javascript - 当 start 未定义时,数组的 lastIndexOf 返回 -1

php - joomla BASE HREF 使用 HTTP 而不是 HTTPS

php - json_encode 和 highcharts

java - Querydsl 4 StringTemplate 创建

SQL 脚本到 SSIS 表达式

php - 构建这个复杂图的最佳方法是什么

javascript - 无法在 Nodejs 中执行 FileUpload 程序

php - 如何获取不同表的数据?

c# - 如何在 C# 中使用表达式创建动态 lambda 连接查询?

javascript - 如何使用 Gulp 编译一堆 sass 文件并将其移动到另一个文件夹?