0

假设我们在 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 xb 将单个出现的项目 x 添加到包 b 中?

4

2 回答 2

2

你想要一个类型的函数

addItem :: Gems -> Bag Gems -> Bag Gems

注意BagGemsthis 的定义与

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

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

addItem gemToAdd bag gem = --some Int expression

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

  1. If gemis a different gem from gemToaAddwe should get out what ever give us 如果是一个不同的宝石,我们应该拿出曾经bag给我们的东西
  2. 如果它是一样的,我们应该再买一个

因此

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)

你完成了

于 2013-01-05T04:47:14.777 回答
0

First of all, you can't change anything in Haskell, everything is immutable. So what you're trying to do right now is wrong.

myBag Sapphire = 3

Will always be 3.

So you will have to return a new bag instead of changing it.

Also, I think it would be better to create a list of Gems instead of pattern matching. As such:

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

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

You can now do things such as

[(Ruby, 30), (Sapphire, 20)] :: Bag

for example.

Next we want to be able to adjust this bag.

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

This code will let you remove a gem from a bag. It's very simple, it just goes through the list and checks each item if it's the selected gem. If that's not the case it will add it to the function list. In the end it will return a new list without the selected gem.

With this function we can add gems to a bag with the following code:

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)

This code will let you add new gems to a bag like so:

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

Will return:

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

"lookup" is a function that looks up a "key" in a list of tuples. The key being the first tuple value, in our case the Gems.

If the lookup doesn't find the gem we want to add, it will simply append it to the list.

If however it does find it, we will store the amount it has of that gem into "oldAmount", delete the gem and add the new amount and old amount together to create a new gem for your bag. For example:

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

Will return:

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

instead of:

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

So it adds up the amount instead of adding the same gem name over and over.

If you want to find a gem and amount from your bag, you can simply use the "lookup" function.

I hope this answers your question.

于 2013-01-05T23:11:08.763 回答