函数的Haskell类型同义词

时间:2013-01-04 23:20:30

标签: function haskell types

假设我们在Haskell中有以下内容,

type Bag a = a -> Int

data Gems = Sapphire|Emerald|Diamond|Ruby deriving (Show)

myBag :: Bag Gems
myBag Sapphire = 3
myBag Diamond = 2
myBag Emerald = 0 

emptyBag :: Bag Gems
emptyBag Sapphire = 0
emptyBag Diamond = 0
emptyBag Emerald = 0

我们如何定义一个函数addItem,以便addItem x b将一个项x出现添加到bag b?

2 个答案:

答案 0 :(得分:2)

您需要类型

的功能
addItem :: Gems -> Bag Gems -> Bag Gems

注意BagGems的定义,这与

相同
addItem :: Gems -> (Gems -> Int) -> Gems -> Int

因此,我们期望addItem的定义开始

addItem gemToAdd bag gem = --some Int expression

好的,所以让我们考虑一下逻辑

  1. 如果gemgemToaAdd不同,我们应该知道bag给我们的内容
  2. 如果它是相同的,我们应该再多一次
  3. 从而

    addItem gemToAdd bag gem = if gem == gemToAdd then (bag gem) + 1 else bag gem
    

    你也可以写

    addItem gemToAdd bag gem | gem == gemToAdd = (bag gem) + 1
                             | otherwise       = bag gem
    

    现在,由于Eq未定义Gems,因此会产生错误。解决这个问题的最简单方法是定义

    data Gems = Sapphire|Emerald|Diamond|Ruby deriving (Show, Eq)
    

    你完成了

答案 1 :(得分:0)

首先,你不能在Haskell中改变任何东西,一切都是不可改变的。所以你现在想做的事情是错误的。

myBag Sapphire = 3

永远是3。

所以你必须退回一个新包而不是更换它。

另外,我认为创建一个Gems列表而不是模式匹配会更好。就这样:

data Gems = Sapphire|Emerald|Diamond|Ruby deriving (Show, Eq)

type Gem = (Gems, Int)
type Bag = [Gem]

您现在可以执行

等操作
[(Ruby, 30), (Sapphire, 20)] :: Bag

例如。

接下来我们希望能够调整这个包。

removeGem :: Gems -> Bag -> Bag
removeGem _ [] = []
removeGem gem (x:xs) | gem == (fst x)  = removeGem gem xs
                     | otherwise = x : removeGem gem xs

此代码可让您从包中删除宝石。它非常简单,它只是通过列表并检查每个项目是否是选定的宝石。如果不是这样,它会将其添加到功能列表中。最后,它将返回一个没有选定gem的新列表。

使用此功能,我们可以使用以下代码将宝石添加到包中:

addToBag :: Gem -> Bag -> Bag
addToBag item@(gem,amount) bag = 
  case lookup gem bag of 
    Nothing -> item : bag
    _       -> let (Just oldAmount) = lookup gem bag
               in (gem, (amount + oldAmount)) : (removeGem gem bag)

此代码可让您将新宝石添加到包中,如下所示:

(Diamond, 10) `addToBag` [(Ruby, 30), (Sapphire, 20)] :: Bag

将返回:

[(Diamond,10),(Ruby,20),(Sapphire,30)]

“lookup”是一个在元组列表中查找“键”的函数。关键是第一个元组值,在我们的例子中是Gems。

如果查找找不到我们要添加的gem,它只会将其附加到列表中。

如果确实找到了它,我们会将其拥有的宝石数量存储到“oldAmount”中,删除宝石并将新金额和旧金额一起添加,以便为您的包创建一个新的宝石。例如:

(Ruby,20) `addToBag` [(Diamond,10),(Ruby,20),(Sapphire,30)]

将返回:

[(Ruby,40),(Diamond,10),(Sapphire,30)]

而不是:

[(Ruby,20),(Diamond,10),(Ruby,20),(Sapphire,30)]

因此,它会累计金额,而不是一遍又一遍地添加相同的宝石名称。

如果你想从包中找到宝石和金额,你可以简单地使用“查找”功能。

我希望这能回答你的问题。