Haskell 将数据类型转换为映射
我想将我的数据类型Exp转换成一个映射,其中函数名Add、Subtract等是键,值是它们在表达式中出现的次数。这是我的数据声明:Haskell 将数据类型转换为映射,haskell,recursion,map,algebraic-data-types,Haskell,Recursion,Map,Algebraic Data Types,我想将我的数据类型Exp转换成一个映射,其中函数名Add、Subtract等是键,值是它们在表达式中出现的次数。这是我的数据声明: data Exp = Number Int | Add Exp Exp | Subtract Exp Exp | Multiply Exp Exp | Divide Exp Exp deriving Show 我可以用BST解决这个问题,但我似乎不能用
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
在完成上述程序后,它似乎应该很简单,但我甚至不知道从哪里开始。注意:我希望使用尽可能多的递归。提前谢谢 您的tree函数使用包含a的树来生成类型b的值,但是Exp数据类型除了要合并或计数的表达式之外不包含任何内容。让我们创建第二个数据类型,我们可以计算发生的次数。最好是Ord,所以我们需要Eq,并且显示将有利于输出:
data Term = NumberTerm | AddTerm | SubtractTerm | MultiplyTerm | DivideTerm
deriving (Eq, Ord, Show)
每一个都代表一个Exp类型的术语
我已将您的插入重命名为inc:
不,我们准备数数:
countExp :: Exp -> Map Term Int
一个数字只有一个项,没有子项,因此我们将从空开始并递增NumberTerms的数量:
添加术语更复杂。每个表达式都有自己的计数,因此我们在每个子项上递归使用countExp,然后使用+来求和计数。之后,我们加入AddTerm,将当前的AddTerm包括在总数中
countExp (Add e1 e2) = inc AddTerm $ unionWith (+) (countExp e1) (countExp e2)
我们可以做几乎完全相同的减法:
我希望您现在就明白了,这样就可以结束了。您的树函数使用包含a的树来生成类型b的值,但是您的Exp数据类型除了要合并或计数的表达式之外不包含任何内容。让我们创建第二个数据类型,我们可以计算发生的次数。最好是Ord,所以我们需要Eq,并且显示将有利于输出:
data Term = NumberTerm | AddTerm | SubtractTerm | MultiplyTerm | DivideTerm
deriving (Eq, Ord, Show)
每一个都代表一个Exp类型的术语
我已将您的插入重命名为inc:
不,我们准备数数:
countExp :: Exp -> Map Term Int
一个数字只有一个项,没有子项,因此我们将从空开始并递增NumberTerms的数量:
添加术语更复杂。每个表达式都有自己的计数,因此我们在每个子项上递归使用countExp,然后使用+来求和计数。之后,我们加入AddTerm,将当前的AddTerm包括在总数中
countExp (Add e1 e2) = inc AddTerm $ unionWith (+) (countExp e1) (countExp e2)
我们可以做几乎完全相同的减法:
我希望你现在就明白了,这样你就可以结束了。这里有一个选择,就是对AndrewC的答案稍加修改。与其创建一个单独的数据类型,将Exp类型的构造函数表示为数字,不如将表达式表示为一个自由的monad,而不是简单的基类型。例如,如果基类型为
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 :: 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)]
这里有一个选择,这是AndrewC答案的一个微小变化。与其创建一个单独的数据类型,将Exp类型的构造函数表示为数字,不如将表达式表示为一个自由的monad,而不是简单的基类型。例如,如果基类型为
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 :: 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)]
谢谢你的回答!我能够让它工作,现在我只是把它分解,并试图了解发生了什么。再次感谢!谢谢你的回答!我能够让它工作,现在我只是把它分解,并试图了解发生了什么。再次感谢!再看一遍,你实际上并不需要免费的——你可以使用同样好的Fix。再看一遍,你实际上不需要免费的——你可以使用同样好的Fix。