对于haskell练习,我想实现一个游戏,学生/学生应该开玩笑地学习代数。
作为基本数据类型,我想使用树:
现在我要定义类似
的内容data Tree = Leaf {l :: Label, val :: Expression}
| Node {l :: Label, f :: Fun, lBranch :: Tree, rBranch :: Tree}
data Fun = "one of [(+),(*),(-),(/),(^)]"
-- type Fun = Int -> Int
会起作用
接下来我想到的是制作树的“等价” - 因为乘法/加法是可交换的,并且可以简化乘法等的加法,以及整组代数运算。 我还必须在树上搜索 - 我认为最好的标签,这是一个很好的方法。
任何想法要查找的标签/短语以及如何解决“数据乐趣”。
答案 0 :(得分:7)
对Edward Z. Yang的答案进行扩展:
在这里定义运算符的最简单方法可能是作为数据类型,以及叶节点中的原子值类型和表达式树的整体类型:
data Fun = Add | Mul | Sub | Div | Exp deriving (Eq, Ord, Show)
data Val a = Lit a | Var String deriving (Eq, Ord, Show)
data ExprTree a = Node String Fun (ExprTree a) (ExprTree a)
| Leaf String (Val a)
deriving (Eq, Ord, Show)
然后,您可以将ExprTree a
定义为Num
和以下内容的实例:
instance (Num a) => Num (ExprTree a) where
(+) = Node "" Add
(*) = Node "" Mul
(-) = Node "" Sub
negate = Node "" Sub 0
fromInteger = Leaf "" . Lit
...允许以非常自然的方式创建未标记的表达式:
*Main> :t 2 + 2
2 + 2 :: (Num t) => t
*Main> 2 + 2 :: ExprTree Int
Node "" Add (Leaf "" (Lit 2)) (Leaf "" (Lit 2))
另请注意上面有关数据定义的deriving
条款,尤其是Ord
;这告诉编译器自动在该类型的值上创建排序关系。这使您可以对它们进行一致排序,这意味着您可以在子表达式上定义规范排序,以便在重新排列交换操作时不会陷入循环。给定规范顺序中的一些规范缩减和子表达式,在大多数情况下,您将能够使用Eq
给出的自动相等关系来检查子表达式等价。
请注意,标签会影响此处的排序和相等性。如果不需要,您需要为Eq
和Ord
编写自己的定义,就像我为Num
提供的定义一样。
之后,您可以编写一些遍历和缩减函数,以执行诸如应用运算符,执行变量替换等操作。
答案 1 :(得分:3)
看起来你想构建一个符号代数系统。关于这个主题有大量不同的文献。
您不希望将运算符表示为Int -> Int
,因为您无法检查任何给定函数实现的操作,然后实现简化等事情的窥孔优化。因此,简单的枚举数据类型将做这个技巧,然后编写实际评估你的树的函数eval
。