我有一个代表产品的元组:
类型 Product = (String, Int, Float)
String 是名称,Int 是金额,Float 是价格或值。
现在我想做以下事情: 我有一个产品列表,我想在该列表中添加一个新产品。 但如果产品已经在列表中,我只想增加数量。 (现在我不关心如果价格/值(value)不一样会发生什么)
“如果已在列表中,则增加该产品的数量。否则将其添加到列表中”
输入示例:("啤酒", 9, 3) [("啤酒", 1, 3),("牛奶", 3, 3),("茶", 10, 3)]
预期输出:[("啤酒", 10, 3),("牛奶", 3, 3),("茶", 10, 3)]
或
输入示例:("Apple", 2, 1) [("Beer", 1, 3),("Milk", 3, 3),("Tea", 10, 3)]
预期输出:[("啤酒", 1, 3),("牛奶", 3, 3),("茶", 10, 3),("苹果", 2, 1)]
(列表没有顺序,因此新产品在列表中的位置并不重要)
addProduct::产品 -> [产品] -> [产品]
听起来很简单,实际上我有一个可以做到这一点的工作方法:
-- check if a name is inside a List
nameInList :: String -> [Product] -> Bool
nameInList n [(a,b,c)] = if (a == n) then True else False
nameInList n ((a,b,c):xs) = if (a == n) then True else nameInList n xs
-- add a product to a list if its not already in that list else just increase amount of that product
addProduct :: Product -> [Product] -> [Product]
addProduct (a,b,c) xs = if nameInList a xs then newList else (a,b,c) : newList
where newList = [ (a2,b2+b,c2) | (a2,b2,c2) <- xs, a2 == a ] ++ [ (a2,b2,c2) | (a2,b2,c2) <- xs, a2 /= a]
但我想知道是否可以使用模式匹配和递归以某种方式实现相同的目的。我也觉得没有其他函数进行一些检查应该是可能的。
最佳答案
如果您知道您的产品列表永远不会有重复的名称,那么使用纯递归这确实非常简单:
addProduct :: Product -> [Product] -> [Product]
addProduct p [] = [p]
addProduct product@(name, amount, price) (product'@(name', amount', price') : ps)
| name == name' = (name, amount' + amount, price') : ps
| otherwise = product' : addProduct product ps
(请注意,如果以与列表中当前版本不同的价格添加产品,则会保留旧价格 - 我将根据您的业务需求来决定这是否可能以及如果可以的话应该发生什么。)
注意 as patterns 的使用允许 product
元组进行模式匹配,同时仍然保留在递归情况下引用整个事物的方式。这不是必需的,但可以使代码更具可读性。而且我还使用了守卫而不是 if-then-else - 这更多是个人喜好,但我认为 Haskellers 通常更喜欢这种方式。
关于您自己的代码的一些小注释:
- 请使用易于理解的变量名称。你会看到我已经替换了
(a, b, c)
与(name, amount, price)
因为这可以清楚地向读者解释这些值实际代表什么 -
if (a == n) then True else False
是多余的,因为它与简单的a == n
完全相同,所以您应该使用更短、更清晰的版本。
关于Haskell - 将元组添加到元组列表(如果尚不存在),否则会增加元组的现有值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68290078/