我想将我的数据类型Exp转换为一个映射,其中函数名称(Add,Subtract等)是键,值是它们在表达式中出现的次数。这是我的数据声明:
data Exp = Number Int
| Add Exp Exp
| Subtract Exp Exp
| Multiply Exp Exp
| Divide Exp Exp
deriving Show
我可以用BST解决这个问题,但我似乎无法使用不同的数据类型完成此任务。如果有帮助,这是我的BST解决方案:
import Data.Map
data Tree a = Empty | Node a (Tree a) (Tree a) deriving (Show)
leaf x = Node x Empty Empty
foldt :: (a -> b -> b) -> b -> Tree a -> b
foldt f a Empty = a
foldt f a (Node x xl xr) = f x ar
where al = foldt f a xl
ar = foldt f al xr
insert' :: Ord a => a -> Map a Int -> Map a Int
insert' a = insertWith (+) a 1
toMap :: Ord a => Tree a -> Map a Int
toMap = foldt insert' empty
在完成上述程序后似乎应该很简单,但我甚至不知道从哪里开始。注意:我想尽可能多地使用递归。提前谢谢!
答案 0 :(得分:3)
您的树函数使用包含a
的树来生成b
类型的值,但您的Exp
数据类型除了要合并的表达式之外不包含任何内容(或计数) )。让我们制作第二种数据类型,我们可以计算它的出现次数。它最好是Ord
,因此我们需要Eq
,而Show
会对输出有益:
data Term = NumberTerm | AddTerm | SubtractTerm | MultiplyTerm | DivideTerm
deriving (Eq, Ord, Show)
每个代表Exp
类型的术语。
我已将您的insert'
重命名为inc
:
inc :: Ord a => a -> Map a Int -> Map a Int
inc a = insertWith (+) a 1
不,我们已准备好计算:
countExp :: Exp -> Map Term Int
Number
只有一个字词(没有子字符),因此我们将以empty
开头并增加NumberTerm
的数量:
countExp (Number _) = inc NumberTerm empty
Add
条款更复杂。每个表达式都有自己的计数,因此我们在每个子项上递归使用countExp
,然后我们unionWith (+)
对计数求和。之后,我们inc AddTerm
将当前Add
字词包含在总计中。
countExp (Add e1 e2) = inc AddTerm $ unionWith (+) (countExp e1) (countExp e2)
对Subtract
我们几乎可以完全相同:
countExp (Subtract e1 e2) = inc SubtractTerm $ unionWith (+) (countExp e1) (countExp e2)
你现在明白我的想法,所以你可以完成。
答案 1 :(得分:1)
这是一个选项,这是AndrewC的回答略有不同。您可以将表达式表示为更简单的基本类型上的自由monad,而不是将表示Exp
类型的构造函数的单独数据类型创建为数字。例如,如果基类型是
import Control.Monad.Free
import Data.Map
data ExpT a = Number a
| Add a a
| Subtract a a
| Multiply a a
| Divide a a
deriving (Eq,Ord,Show)
然后您的表达式可以定义为ExpT
上的免费monad,其中Int
为根类型
type Exp = Free ExpT Int
现在你在AndrewC的帖子中写inc
inc :: Ord a => a -> Map a Int -> Map a Int
inc a = insertWith (+) a 1
并且countExp
功能再次非常相似
countExp :: Exp -> Map (ExpT ()) Int
countExp (Free (Number _)) = inc (Number ()) empty
countExp (Free (Add a b)) = inc (Add () ()) $ unionWith (+) (countExp a) (countExp b)
等等。您可能想要定义一些用于创建表达式的便利函数
number :: Int -> Exp
number n = Free (Number (Pure n))
add a b = Free (Add a b)
sub a b = Free (Subtract a b)
mul a b = Free (Multiply a b)
divide a b = Free (Divide a b)
,最终结果是
>>> countExp (add (number 1) (number 2))
fromList [(Number (),2),(Add () (),1)]