Haskell 转换为其他数据类型

Haskell 转换为其他数据类型,haskell,types,Haskell,Types,我有以下数据类型: data Food = Butter Int | Apple Int Food | Meat Double Food deriving (Show) data Basket = Basket { butter, apple :: Int, meat :: (Int,Double) } deriving (Show) 篮子里有黄油和苹果的数量,肉的数量和它的总重量(双倍) 我正在尝试编写一个函数food2basket::Food->Basket,它允许我将Food类型的值转

我有以下数据类型:

data Food = Butter Int | Apple Int Food | Meat Double Food deriving (Show)

data Basket = Basket { butter, apple :: Int, meat :: (Int,Double) } deriving (Show)
篮子里有黄油和苹果的数量,肉的数量和它的总重量(双倍)

我正在尝试编写一个函数food2basket::Food->Basket,它允许我将Food类型的值转换为Basket类型的值

因此,如果我宣布这一食品实例:

     andrew = Apple 1 ( Meat 0.60 ( Meat 0.70 ( Apple 1 ( Butter 1 ))))
通过应用该函数,我将得到以下结果

 food2basket andrew == {butter = 1, apple= 2, meat = (2,1.30)}
到目前为止,我的代码如下所示:

emptyBasket = Basket {butter = 0, apple= 0, meat = (0,0)}
food2basket (Butter i)      = emptyBasket {butter = i}
food2basket (Apple i b)     = anotherBasket{apple = i + (apple anotherBasket)}
    where anotherBasket     = food2basket b
food2basket (Meat d b)      = auxFood (Meat d b) 0
    where
         auxFood(Meat d b) n= emptyBasket { meat = (n+1,d)} -- missing recursion
这是工作到“苹果”的一部分,甚至艰难,我发现很难使用括号内的递归。这就是为什么我使用了另一个篮子和where子句,这非常令人困惑。 正如我担心的那样,我不能用肉来做同样的事情。(它还没有递归)


我想用模式匹配来解析整个函数。

代码看起来有点混乱。您可能需要定义一个将两个篮子相加的函数:

addBaskets b1 b2 = Basket {butter = butter b1 + butter b2, apple = ...
然后你可以做一些类似的事情

food2basket (Butter i)   = emptyBasket {butter = i}
food2basket (Apple  i b) = addBaskets (emptyBasket {apple = i}) (food2basket b)
food2basket (Meat   i b) = addBaskets (emptyBasket {meat  = i}) (food2basket b)
> food'ToBasket (butter' 1 >> apple' 1 >> meat' 0.5)
Basket { butter = 1, apple = 0, meat = (0, 0.0)}

代码看起来有点混乱。您可能需要定义一个将两个篮子相加的函数:

addBaskets b1 b2 = Basket {butter = butter b1 + butter b2, apple = ...
然后你可以做一些类似的事情

food2basket (Butter i)   = emptyBasket {butter = i}
food2basket (Apple  i b) = addBaskets (emptyBasket {apple = i}) (food2basket b)
food2basket (Meat   i b) = addBaskets (emptyBasket {meat  = i}) (food2basket b)
> food'ToBasket (butter' 1 >> apple' 1 >> meat' 0.5)
Basket { butter = 1, apple = 0, meat = (0, 0.0)}

这里有一个更完整的解决方案:

addBaskets :: Basket -> Basket -> Basket
addBaskets b1 b2 =
    Basket (butter b1 + butter b2) (apple b1 + apple b2) ((m1 + m2), (w1 + w2))
    where (m1, w1) = meat b1
          (m2, w2) = meat b2

food2basket :: Food -> Basket
food2basket (Butter x)  = emptyBasket { butter = x }
food2basket (Apple x f) = addBaskets emptyBasket { apple = x } (food2basket f)
food2basket (Meat x f)  = addBaskets emptyBasket { meat = (1, x) } (food2basket f)

这里有一个更完整的解决方案:

addBaskets :: Basket -> Basket -> Basket
addBaskets b1 b2 =
    Basket (butter b1 + butter b2) (apple b1 + apple b2) ((m1 + m2), (w1 + w2))
    where (m1, w1) = meat b1
          (m2, w2) = meat b2

food2basket :: Food -> Basket
food2basket (Butter x)  = emptyBasket { butter = x }
food2basket (Apple x f) = addBaskets emptyBasket { apple = x } (food2basket f)
food2basket (Meat x f)  = addBaskets emptyBasket { meat = (1, x) } (food2basket f)

扩展MathematicalArchid的答案,这个模式看起来很像
幺半群

import Data.Monoid

instance Monoid Basket where
    mempty = Basket 0 0 (0, 0)  -- Same as emptyBasket
    mappend (Basket b1 a1 (m1, d1)) (Basket b2 a2 (m2, d2))
        = Basket (b1 + b2) (a1 + a2) (m1 + m2, d1 + d2)
如果你把你的
食物
写成

data FoodItem
    = Butter Int
    | Apple Int
    | Meat Double
    deriving (Eq, Show)
那你就可以了

type Food = [FoodItem]

foodItemToBasket :: FoodItem -> Basket
foodItemToBasket (Butter i) = mempty { butter = i }
foodItemToBasket (Apple  i) = mempty { apple = i }
foodItemToBasket (Meat   d) = mempty { meat = (1, d) }
然后,您只需使用
map foodItemToBasket
mconcat

foodToBasket :: Food -> Basket
foodToBasket = mconcat . map foodItemToBasket
现在,将每个项目转换为一个篮子要简单得多,并且不包含递归,将这些篮子组合在一起的行为由
mconcat
处理,这是
Data.Monoid
提供的一个更通用的函数

然而,如果您确实想要一个递归数据结构,您实际上可以通过使用
Free
monad使事情变得非常复杂。我将忽略细节,但它允许您这样做(您需要
DeriveFunctor
来编译此文件):

(权重不精确是由于IEEE浮点格式舍入错误造成的,相关信息散布在互联网上)

你为什么要走这条路是愚蠢的,我只是觉得有趣的是你对
食物的定义符合这样做的模式。它确实免费为您提供了一个很好的monad实例,您可以使用do表示法简单地列出一个人拥有的不同项目,然后
food'ToBasket
将monad结构“解释”到一个篮子中。这确实意味着

food2basket (Butter i)   = emptyBasket {butter = i}
food2basket (Apple  i b) = addBaskets (emptyBasket {apple = i}) (food2basket b)
food2basket (Meat   i b) = addBaskets (emptyBasket {meat  = i}) (food2basket b)
> food'ToBasket (butter' 1 >> apple' 1 >> meat' 0.5)
Basket { butter = 1, apple = 0, meat = (0, 0.0)}

因此,不必在编译时检查
Butter'
是否是结构中的最后一项,只要遇到
Butter'
,就会发生短路,就像
没有任何东西会使
计算短路一样。

扩展MathematicalArchid的答案,此模式看起来很像
幺半群

import Data.Monoid

instance Monoid Basket where
    mempty = Basket 0 0 (0, 0)  -- Same as emptyBasket
    mappend (Basket b1 a1 (m1, d1)) (Basket b2 a2 (m2, d2))
        = Basket (b1 + b2) (a1 + a2) (m1 + m2, d1 + d2)
如果你把你的
食物
写成

data FoodItem
    = Butter Int
    | Apple Int
    | Meat Double
    deriving (Eq, Show)
那你就可以了

type Food = [FoodItem]

foodItemToBasket :: FoodItem -> Basket
foodItemToBasket (Butter i) = mempty { butter = i }
foodItemToBasket (Apple  i) = mempty { apple = i }
foodItemToBasket (Meat   d) = mempty { meat = (1, d) }
然后,您只需使用
map foodItemToBasket
mconcat

foodToBasket :: Food -> Basket
foodToBasket = mconcat . map foodItemToBasket
现在,将每个项目转换为一个篮子要简单得多,并且不包含递归,将这些篮子组合在一起的行为由
mconcat
处理,这是
Data.Monoid
提供的一个更通用的函数

然而,如果您确实想要一个递归数据结构,您实际上可以通过使用
Free
monad使事情变得非常复杂。我将忽略细节,但它允许您这样做(您需要
DeriveFunctor
来编译此文件):

(权重不精确是由于IEEE浮点格式舍入错误造成的,相关信息散布在互联网上)

你为什么要走这条路是愚蠢的,我只是觉得有趣的是你对
食物的定义符合这样做的模式。它确实免费为您提供了一个很好的monad实例,您可以使用do表示法简单地列出一个人拥有的不同项目,然后
food'ToBasket
将monad结构“解释”到一个篮子中。这确实意味着

food2basket (Butter i)   = emptyBasket {butter = i}
food2basket (Apple  i b) = addBaskets (emptyBasket {apple = i}) (food2basket b)
food2basket (Meat   i b) = addBaskets (emptyBasket {meat  = i}) (food2basket b)
> food'ToBasket (butter' 1 >> apple' 1 >> meat' 0.5)
Basket { butter = 1, apple = 0, meat = (0, 0.0)}

因此,不必在编译时检查
Butter'
是否是结构中的最后一项,只要遇到
Butter'
,就必须短路,就像
Nothing
将短路
计算一样。

在哪里定义了
auxFood
?语法
(Meat d b)n=…
无效。什么是
n
应该在这里?我忘了auxFood,它现在在里面。N应该是每次发现“肉”时增加1的计数器(在递归中)。在哪里定义了
auxFood
?语法
(Meat d b)n=…
无效。什么是
n
应该在这里?我忘了auxFood,它现在在里面。N应该是每次发现“肉”时增加1的计数器(在递归中)。是的,这更有意义。原始海报上食物顺序的表示令人困惑。不错,我不擅长幺半群,所以我用模式匹配和递归来限制自己。我不知道如何调用递归,因为我必须“服从”某个类型。@user3086819我的回答是为了向您提供有关问题的一般结构的更多信息,而不是作为您所拥有问题的完整解决方案。我认为很可能是给了您一个食物类型,但我想强调的是,使用不同的数据结构,利用Haskell的一些更通用的特性来解决问题,而不是滚动您自己的解决方案,会更有意义。我自己在大学里也做过很多类似的练习,我只是发现它们对好的设计有反作用。是的,这更有意义。原来海报上的食品订单令人困惑