转换前缀表示法中给出的表达式,识别公共子表达式和依赖项
我在ANSI文本文件中的前缀表示法中给出了一堆表达式。我想生成另一个ANSI文本文件,其中包含对这些表达式的逐步评估。例如:
- + ^ x 2 ^ y 2 1
应该变成
t1 = x^2
t2 = y^2
t3 = t1 + t2
t4 = t3 - 1
t4 is the result
我还必须识别常见的子表达式。举个例子
expression_1: z = ^ x 2
expression_2: - + z ^ y 2 1
expression_3: - z y
我必须生成一个输出,说x出现在表达式1,2和3(通过z)中。
我必须识别依赖:expression_1仅依赖于x,expression_2依赖于x和y等。
最初的问题比上面的例子更困难,我无法控制输入格式,它的前缀表示法比上面的方法复杂得多。
我已经在C ++中有一个工作实现,但是在C ++中做这些事情是很痛苦的。
哪种编程语言最适合这些类型的问题?
你能推荐我可以开始的教程/网站/书吗?
我应该寻找哪些关键字?
更新:基于答案,上面的例子有点不幸,我在输入中有一元,二元和n元运算符。 (如果你想知道,exp
是一元算子,sum
在一个范围内是一个n-ary算子。)
没有找到相关结果
已邀请:
7 个回复
矾醒忻
输出与示例输出相同。 除了这个例子,我认为你列出的任何高级语言几乎都适合这项任务。
到街客核
python包执行符号代数,包括公共子表达式消除和生成一组表达式的评估步骤。 请参阅:http://docs.sympy.org/dev/modules/rewriting.html(请参阅页面底部的
方法)。
结乳
这可能有点消化所有的一次,但再次,这确实相当多。 首先,它完全是防弹的(注意大量使用
结果可能会失败)。如果你扔垃圾,它将返回
。 (通过更多的工作,你可以让它以一个信息丰富的方式抱怨问题 - 基本上
然后
嵌套两次可以存储结果并打印出一个信息性的错误消息,如果操作没有得到两个同样,
可能会抱怨拖尾价值而不是仅仅扔掉
。) 其次,当你完成它时,你有一个完整的表达式树。请注意,
打印出您想要的临时变量,
列出特定表达式中使用的变量,一旦解析多行,您可以使用它们扩展为完整列表。 (如果你的变量不仅仅是
到
,你可能需要稍微摆弄正则表达式。)另请注意,表达式树以正常的中缀表示法打印出来。我们来看一些例子:
现在,你可以在没有太多额外工作的情况下在Python中完成此操作,并且除了之外还有许多其他语言。我更喜欢使用Scala,但您可能更喜欢使用Scala。无论如何,如果您希望保留最大的灵活性来处理棘手的情况,我建议您创建完整的表达式树。
席酱
这将生成如下输出:
这可以很容易地扩展。例如,要处理多个表达式语句,您可以使用:
产量:
唤副埂侧壬
这是你得到的结果:
n-Ary运算符
旗低饶彤
表达式的打印对我没用,你已经有几个Scala例子,所以我会跳过它。) 首先,我们需要提取语言的各个部分。我会选择正则表达式,但也可以使用解析器组合器:
非常直截了当。我们定义可能出现的各种事物。 (我已经确定用户定义的变量只能是一个小写字母,并且由于你有
函数,这些数字可以是浮点数。)最后的
表示这是一个正则表达式,它会给出我们在括号中的东西。 现在我们需要代表我们的树。有很多方法可以做到这一点,但我会选择一个带有特定表达式作为案例类的抽象基类,因为这样可以简化模式匹配。此外,我们可能想要很好的打印,所以我们将覆盖
。但是,大多数情况下,我们将使用递归函数来完成繁重的工作。
大多数情况下这是非常沉闷的 - 每个案例类都会覆盖基类,而
和
会自动填入
。请注意,我已经确定
是一个可能的n-ary函数,并且它将打印出行号。 (原因是如果您有多行输入,有时将它们作为一个表达式一起使用会更方便;这使它们成为一个函数。) 一旦定义了我们的数据结构,我们就需要解析表达式。将要解析的东西表示为令牌列表很方便;当我们解析时,我们将返回一个表达式和我们尚未解析的剩余标记 - 这对于递归解析来说是一个特别有用的结构。当然,我们可能无法解析任何东西,所以最好还包括在
中。
请注意,我们使用模式匹配和递归来完成大部分繁重工作 - 我们选择了部分列表,找出了我们需要多少个参数,然后递归传递它们。 N-ary操作不太友好,但是我们创建了一个小的递归函数,它将一次解析N个东西,将结果存储在缓冲区中。 当然,这对使用起来有点不友好,所以我们添加一些包装函数,让我们很好地与它进行交互:
好的,现在,简化怎么样?我们可能想做的一件事是数值简化,我们预先计算表达式并用原始表达式替换原始表达式。这听起来像某种递归操作 - 找到数字,并将它们组合起来。首先,我们得到一些辅助函数来对数字进行计算:
然后我们做简化:
再次注意模式匹配的大量使用,以便在我们处于良好情况时查找,并分派适当的计算。 现在,代数替换肯定要困难得多!实际上,您需要做的就是注意表达式已被使用,并指定一个变量。由于我上面定义的语法允许就地变量替换,我们实际上只需修改表达式树以包含更多变量赋值。所以我们这样做(如果用户没有,则编辑为仅插入变量):
就是这样 - 一个功能齐全的代数替换程序。请注意,它构建了所见的表达式集,并保持特殊的跟踪哪些是重复的。感谢案例类的神奇之处,所有的等式都是为我们定义的,所以它才有效。然后我们可以替换任何重复项,因为我们通过递归来找到它们。请注意,替换例程被拆分为一半,并且在未替换的树版本上匹配,但使用替换版本。 好的,现在让我们添加一些测试:
它是怎么做的?
所以我不知道这对你有用,但结果证明对我有用。这就是我在C ++中非常犹豫不决的事情,因为应该很容易的各种事情最终会变得痛苦。 编辑:以下是使用此结构打印临时分配的示例,只是为了证明此结构完全可以执行此类操作。 码:
选定结果(从
):
第二次编辑:找到依赖关系也不算太糟糕。 码:
如果我们添加新的测试用例
和
并用
显示答案,我们得到(选择的结果):
所以我认为除了这种结构之外,所有的个性化需求都可以通过一到两行Scala代码来满足。 编辑:这里是表达式评估,如果你给出了从变量到值的映射:
并且在测试例程中使用它是这样的:
给出这样的结果(输入
和
):
另一个挑战 - 选择尚未使用的变量 - 只是从依赖项ѭ58ѭ列表中减去。
是集合减法的名称。
剑哎