首页 > 解决方案 > 如何在 Haskell 中为唱片店建模

问题描述

我想在 Haskell 中为一家唱片店建模。实际上销售记录并不重要,我主要想关注如何建模库存管理和跟踪销售。我的问题描述是这样的:

  1. 记录给定艺术家姓名和曲目标题的曲目单次销售。
  2. 返回给定艺术家姓名和曲目标题的曲目总销量。
    (如果没有匹配的轨道,这应该返回 0。)
  3. 给定艺术家姓名和曲目标题,从销售记录中删除曲目。

我该如何开始?

标签: haskellhaskell-platform

解决方案


因此,首先您可能会检查您的问题描述并突出显示构成您的问题域的名词和动词:salestracktrack titleartistnumber of salesrecord of sales。然后,我可能会首先为似乎是最核心概念的数据类型定义一个数据类型:

import Data.Text (Text)

type Title = Text
type Artist = Text

data Track = Track
  { trackTitle :: Title
  , trackArtist :: Artist
  } deriving (Eq, Ord, Show)

bestTrack, okayTrack :: Track
bestTrack = Track
  { trackTitle = "it's is my life"
  , trackArtist = "jon bovi"
  }

okayTrack = Track
  { trackTitle = "living in preyer"
  , trackArtist = "jon bovi"
  }

您也可以以几乎相同的方式定义销售。

也许您想使用安全货币包来模拟价格。

import Money

type Price = Dense "USD"

data Sale = Sale
  { saleTrack :: Track
  , salePrice :: Price
  }

sales :: [Sale]
sales =
  [ Sale { saleTrack = bestTrack, salePrice = 399 % 100 }
  , Sale { saleTrack = okayTrack, salePrice = 284 % 100 }
  ]

那些是名词。动词,添加/删除销售记录,可以做成函数。如果 sales 是一个列表,那么添加和删除一个销售记录就变成了一个对列表进行操作的递归函数:

addSale :: Track -> Price -> [Sale] -> [Sale]
addSale soldTrack soldPrice existingSales = ...

removeSale :: Title -> [Sale] -> [Sale]
removeSale title existingSales = ...

在编写这些函数的过程中,您还可以通过制定有关它们的属性来测试它们:例如:将销售添加到现有的销售列表中,生成的列表应该是一个更大的销售。从空的销售列表中删除销售应该会产生错误,或者复制空的销售列表(决定)。从非空的销售列表中删除一个销售应该会产生一个少一个销售的列表。从包含相同曲目标题的销售列表中删除两次销售应该只删除其中一个(哪个?决定)。添加然后删除轨道应生成与添加之前相同的销售列表。

从这些属性中,您可以构建单元测试或属性测试。

当您为商店之类的东西建模时,您还可以更深入地了解良好实践,例如,为同一商品记录不同的价格可能是不好的,因此您可能想要发明一个产品代码。很好地为商店建模是一个兔子洞,所以如果你只是想制作一个愚蠢的概念验证来涵盖大多数可能的领域,例如最终获得 UI,那么这部分可能并不重要。


推荐阅读