首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >函数的Haskell类型同义词

函数的Haskell类型同义词
EN

Stack Overflow用户
提问于 2013-01-05 07:20:30
回答 2查看 1.2K关注 0票数 0

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

代码语言:javascript
复制
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中?

EN

回答 2

Stack Overflow用户

发布于 2013-01-05 12:47:15

你想要一个类型为

代码语言:javascript
复制
addItem :: Gems -> Bag Gems -> Bag Gems

请注意,BagGems的定义与

代码语言:javascript
复制
addItem :: Gems -> (Gems -> Int) -> Gems -> Int

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

代码语言:javascript
复制
addItem gemToAdd bag gem = --some Int expression

好的,让我们考虑一下其中的逻辑

  1. 如果gem是不同于gemToaAdd的宝石,我们应该得到bag提供给我们的
  2. 如果它与我们应该得到的多一个

相同

因此

代码语言:javascript
复制
addItem gemToAdd bag gem = if gem == gemToAdd then (bag gem) + 1 else bag gem

你也可以写成

代码语言:javascript
复制
addItem gemToAdd bag gem | gem == gemToAdd = (bag gem) + 1
                         | otherwise       = bag gem

现在,这将产生一个错误,因为没有为Gems定义Eq。解决这个问题的最简单方法是定义

代码语言:javascript
复制
data Gems = Sapphire|Emerald|Diamond|Ruby deriving (Show, Eq)

你就完事了

票数 2
EN

Stack Overflow用户

发布于 2013-01-06 07:11:09

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

代码语言:javascript
复制
myBag Sapphire = 3

将始终为3。

所以你得退掉一个新包,而不是换一个。

此外,我认为创建一个Gem列表会比模式匹配更好。因此:

代码语言:javascript
复制
data Gems = Sapphire|Emerald|Diamond|Ruby deriving (Show, Eq)

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

您现在可以执行以下操作

代码语言:javascript
复制
[(Ruby, 30), (Sapphire, 20)] :: Bag

例如。

接下来,我们希望能够调整这个袋子。

代码语言:javascript
复制
removeGem :: Gems -> Bag -> Bag
removeGem _ [] = []
removeGem gem (x:xs) | gem == (fst x)  = removeGem gem xs
                     | otherwise = x : removeGem gem xs

这段代码可以让你从包里取出宝石。这非常简单,它只是遍历列表并检查每一项是否为所选的宝石。如果不是这样,它会将其添加到函数列表中。最后,它将返回一个不包含所选gem的新列表。

使用此函数,我们可以使用以下代码将gem添加到包中:

代码语言:javascript
复制
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)

这段代码可以让你添加新的gem到包中,如下所示:

代码语言:javascript
复制
(Diamond, 10) `addToBag` [(Ruby, 30), (Sapphire, 20)] :: Bag

将返回:

代码语言:javascript
复制
[(Diamond,10),(Ruby,20),(Sapphire,30)]

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

如果查找没有找到我们想要添加的gem,它会简单地将其附加到列表中。

如果它找到它,我们会将它拥有的宝石数量存储到"oldAmount“中,删除宝石,并将新数量和旧数量添加在一起,为您的包创建一个新的宝石。例如:

代码语言:javascript
复制
(Ruby,20) `addToBag` [(Diamond,10),(Ruby,20),(Sapphire,30)]

将返回:

代码语言:javascript
复制
[(Ruby,40),(Diamond,10),(Sapphire,30)]

而不是:

代码语言:javascript
复制
[(Ruby,20),(Diamond,10),(Ruby,20),(Sapphire,30)]

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

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

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

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/14166641

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档