给定代表商店的以下类型集:
type Article = string
type Amount = int
type Price = int
type Desc = Amount*Price
type Stock = (Article*Desc) list
type Order = Article * Amount
type Status<'a> = Result of 'a | Error of string
我想做一个函数,它需要一个Order
和一个Stock
并返回一个Status<Price * Stock>
。 get (a,k) st
以下定义函数的值是Result (p,st')
,其中p
是k
的a
片的价格,而st'
是从st
通过去除k
片a
获得的新股票。
例:
let rec get (a,k) st =
match st with
| (a',(n',p'))::rest when a'=a -> Result (p'*k,(a',(n'-k,p'))::rest)
| (a',(n',p'))::rest ->
match get (a,k) rest with
| Result (price,stock) -> Result (price,(a',(n',p'))::stock)
| Error e -> Error e
get ("a2", 10) st
val it : int * (string * (int * int)) list =
Result (200, [("a1", (100, 10)); ("a2", (40, 20)); ("a3", (25, 40))])
现在,如果我服用Order list
,我将如何做到这一点?
如get [("a2",10);("a1",10)] st
将返回Result (300, [("a1", (90, 10)); ("a2", (40, 20)); ("a3", (25, 40))])
type Article = string
type Amount = int
type Price = int
type Desc = Amount * Price
type Stock = (Article * Desc) list
type Order = Article * Amount
type Status<'a> = Result of 'a | Error of string
首先,由于你要求实现get
函数,让我们将现有函数get
重命名为getArticle
。
let rec getArticle (a, k) st =
match st with
| (a', (n', p')) :: rest when a' = a -> Result (p' * k, (a', (n' - k, p')) :: rest)
| (a', (n', p')) :: rest ->
match getArticle (a, k) rest with
| Result (price, stock) -> Result (price, (a', (n', p')) :: stock)
| Error e -> Error e
| [] -> Error "Article not in stock"
我还添加了一个额外的案例来摆脱“不完整模式匹配”警告。
你的问题的答案是List.fold
。
List.fold
的第一个参数是'State -> 'ElementType -> 'State
类型的文件夹函数。在这个问题上,'ElementType
是Order
。
getArticle
几乎完成了文件夹功能应该做的事情:在遍历一个元素时计算下一个状态 - 在这个问题中:列表中的一个顺序。但不完全:
getArticle
将Stock
作为输入状态,但作为输出状态返回Status<Price * Stock>
,其中额外的Price
是检索到的文章的总价格(到目前为止)。 List.fold
期望文件夹功能的输入和输出状态是相同的类型。getArticle
将列表中的元素作为第一个参数 - 在这个问题中的类型为Order
-,第二个参数为输入状态。 List.fold
期望一个文件夹函数,它将这两个参数交换掉。因此,让我们构建一个文件夹功能,其中累计(添加)到目前为止检索到的文章的总价格:
let folder status order =
match status with
| Result (totalPriceSoFar, stock) ->
match getArticle order stock with
| Result (price, st) -> Result (totalPriceSoFar + price, st)
| Error e -> Error e
| Error f -> Error f
我们现在几乎已经完成了。
qazxsw poi的第二个论点是最初的状态 - 在这个问题中:一个qazxsw poi,初始股票和总价格到目前为止初始化为List.fold
。
Result
的第三个参数是迭代的列表 - 在这个问题中:订单列表。
0
结语
您选择将List.fold
类型定义为let get orders initialStock =
let initialStatus = Result (0, initialStock)
List.fold folder initialStatus orders
let initialStock = [("a1", (100, 10)); ("a2", (50, 20)); ("a3", (25, 40))]
get [("a2", 10); ("a1", 10)] initialStock
。鉴于您使用此列表基本上作为查找地图,其中文章不能有2个价格或2个金额,为什么不选择Stock
?