看看微软.NET的System.Linq.Expressions命名空间中如何建模表达式树是很有趣的。我认为这里重要的基本概念是,所有东西都继承自基本的Expression类 - 所以几乎所有东西都是一个表达式,无论是二元操作、参数、常量等。
此外,NuGet包Serialize.Linq演示了如何将这些表达式树序列化为XML。
长解决方案
这只是供参考。我认为XML模型甚至可以更简单。我会像这样建模你的例子:
<expr returns="boolean">
<params>
<param name="x" type="boolean">
<param name="y" type="boolean">
<param name="p" type="boolean">
<param name="q" type="boolean">
</params>
<body op="bor">
<left op="band">
<left op="param">x</left>
<right op="param">y</left>
</left>
<right op="band">
<left op="param">p</left>
<right op="param">q</left>
</right>
</body>
<expr>
简短解决方案
根据您的实现方式,您可以省略<params>
元素和type
属性,并在解释表达式时以更“隐含”的方式处理这些内容(有点像JavaScript、PowerShell等)。此外,如果XML输出的大小/长度是一个问题,您还可以缩短元素和类型名称。
因此,这里是一个高度缩短版本的示例:
<e t="bor">
<l t="band">
<l t="prm">x</l>
<r t="prm">y</r>
</l>
<r t="band">
<l t="prm">p</l>
<r t="prm">y</r>
</r>
</e>
另一个例子
为了完整起见,这里有另一个例子,展示如何以这种方式建模更复杂的表达式。
伪代码 Lambda/箭头表达式:
(float b, int e) => e == 0 ? 1 : (e == 1 ? b : pow(b, e))
XML 表示:
<expr returns="float">
<params>
<prm name="b" type="float" />
<prm name="e" type="int" />
</params>
<body op="if">
<cond op="eq">
<left op="param">e</left>
<right op="const" type="int">0</right>
</cond>
<true op="const" type="float">1</true>
<false op="if">
<cond op="eq">
<left op="param">e</left>
<right op="const" type="int">1</right>
</cond>
<true op="param">b</true>
<false op="call" name="pow">
<args>
<arg op="param">p</arg>
<arg op="param">e</arg>
</args>
</false>
</false>
</body>
</expr>
>
或<
,左右的位置是很重要的。在我看来,能够“分组”多个信号是一个不错的便利功能,但它们仍然需要两两处理。 - marsze