Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell 将数据类型转换为映射_Haskell_Recursion_Map_Algebraic Data Types - Fatal编程技术网

Haskell 将数据类型转换为映射

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解决这个问题,但我似乎不能用

我想将我的数据类型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

在完成上述程序后,它似乎应该很简单,但我甚至不知道从哪里开始。注意:我希望使用尽可能多的递归。提前谢谢

您的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。