【发布时间】:2021-05-03 11:19:39
【问题描述】:
我正在研究一个符号代数系统。我目前正在研究如何从二叉解析树执行多项式加法/乘法。我稍后会认为这是一个通用的戒指。
解析与此处不相关——这旨在成为解析的输出。形成的分析树。如果这里有什么可以改进的地方,我当然也很高兴知道这一点。
我“感觉”这个树形结构可以折叠/压碎,但我不太清楚如何去做。我相信我可以一起破解一些东西,但是由于我还在学习 Haskell,所以我想了解更高级的用户会做什么。
以下是相关代码背景。
我的两个相关数据声明是:
data Op = Add | Mul
data Tree a = Leaf a | Node Op [Tree a] [Tree a]
这是我的一个测试示例:
-- 3*(x+2)*(x+(5*4))
test = Node Mul [
Node Mul
[Leaf "3"]
[Node Add
[Leaf "x"]
[Leaf "2"]
]
]
[Node Add
[Leaf "x"]
[Node Mul
[Leaf "5"]
[Leaf "4"]
]
]
这是一种典型的递归树类型,一个节点包含一个操作以及左右树的列表。计算通过以下操作进行。注意:它们现在都是字符串操作。我需要它们尽可能通用(但稍后会添加进一步的结构,例如乘法交换性)。
prod :: [Tree [a]] -> [Tree [a]] -> [Tree [a]]
prod ls rs = [Leaf (l ++ r) | (Leaf l) <- ls, (Leaf r) <- rs]
add :: [Tree [a]] -> [Tree [a]] -> [Tree [a]]
add l r = l ++ r
opOnLeaf :: Op -> [Tree [a]] -> [Tree [a]] -> [Tree [a]]
opOnLeaf op l r
| op == Add = add l r
| op == Mul = prod l r
operate :: Tree [a] -> [Tree [a]]
operate (Node op [Node op2 l2 r2] [Node op3 l3 r3]) = operate (Node op (operate (Node op2 l2 r2)) (operate (Node op3 l3 r3)))
operate (Node op [Node op2 l2 r2] r) = operate (Node op (operate (Node op2 l2 r2)) r)
operate (Node op l [Node op2 l2 r2]) = operate (Node op l (operate (Node op2 l2 r2)))
operate (Node op l r) = opOnLeaf op l r
我认为我在operate 中的变量名很可怕,但我想确保它首先起作用。让它变得丑陋也使它更加突出。它实际上是在对我大喊大叫,以找到更好的方法。
现在,我想使用 fold 或 foldMap,但我遇到了一个问题,即我在每个操作中都有不同的幺半群(即 (*) 和 (+)。这只是开始——我将添加其他几个操作。所以我需要类似“变量幺半群”的东西,或者可能是其他一些地图,其操作是首先获取幺半群。我不确定。
查询:给定这棵二叉树,以及每个节点上的多个操作,如何继续折叠结构?有没有更好的方法把它写成可折叠的实例或其他结构?我还打算添加其他几个二进制操作。理想情况下,我想要一个可以无限扩展的结构。我假设一旦解决了 2,我们就可以有效地解决 n。
【问题讨论】:
-
你为什么使用
Node构造函数的列表,Node Op (Tree a) (Tree a)不应该更有意义吗? -
很有可能。到目前为止,这是我的理由。 (1) 在每个操作应用程序(例如,Add)之后,每个子树都归结为一个列表。我想在前进时将 [Leaf] 类型向上携带; (2)在某些情况下,解析后,我希望有多个值开始,比如:[Leaf 2, Leaf 3, Leaf 4],如果我想表示 2 + 3 + 4。所以我'我使用叶子列表,我使用树列表。然而,我认为这个理由不太令人信服(我正在写这篇临时文章);如果它们总是二进制的,那么没有节点列表是有意义的。也许我需要更多类型。
-
在较高级别的
foldr中,您将一个运算符(例如:)更改为另一个运算符,并且所有叶子 都具有匹配的值。所以在这里你需要像foldTree :: ([acc] -> [acc] -> Op -> acc) -> acc -> Tree a -> acc(left-accs,right-accs,operator -> new acc)这样的东西 - 我想你可以实现这个,但我不确定这是否有帮助 -
(1) 所以把它简化为一个列表——而不是一个树列表。写
prod :: Tree a -> Tree a -> [[a]]等,而不是你这种疯狂的类型。 (2) 将其解析为Node Add (Leaf 2) (Node Add (Leaf 3) (Leaf 4))。 -
如果要将运算符与列表相关联,则为每个
Node一个列表而不是两个。
标签: parsing haskell fold parse-tree