如何从这个函数输出单个[Int]?

时间:2014-07-20 16:05:46

标签: haskell

import Data.Char

type Games = (String, String, Int)

test :: [Games]
test =
    [
    ("Minecraft","mojang",100),
    ("GTA V","rockstar",500),
    ("Portal","valve",200),
    ("GTA IV","rockstar",100)
    ]

-- give the total number of sales for a studio
studioSales :: String -> [Games] -> [Int]
studioSales studioName [] = []
studioSales studioName ((name,studio,quantitySold):xs)
   | studioName == studio = quantitySold: studioSales studioName xs 
   | otherwise = studioSales studioName xs

当调用函数“studioSales”rockstar“test”时,返回的值为“[500,100]”。

如何调整此值,以便在调用“studioSales”rockstar“test”时,返回的值为“[600]”,其中两个Int值相加。

另外,我如何将所有销售额加起来?所以一个函数会返回所有加起来的整数吗?

2 个答案:

答案 0 :(得分:3)

第一次通过我:

fst3 (x, _, _) = x
snd3 (_, y, _) = y
thrd (_, _, z) = z
studioSales studio = sum . map thrd . filter ((studio ==) . snd3)

我认为你的代码可以通过一些更好的命名来实现,不过

data Game = Game { title :: String, studio :: String, cntSold :: Int }
type Games = [Game]

test =
    [ Game "Minecraft" "mojang"   100
    , Game "GTA V"     "rockstar" 500
    , Game "Portal"    "valve"    200
    , Game "GTA IV"    "rockstar" 100
    ]

sumSold :: Games -> Int
sumSold = sum . map cntSold

singleStudio :: String -> Games -> Games
singleStudio s = filter ((s ==) . studio)

sumSoldByStudio = (sumSold .) . singleStudio
-- or: sumSoldByStudio s = sumSold . singleStudio s

顺便说一句,如果你真的想要一个[Int]Int s列表)而不是一个Int,你可以使用(:[])return将单个值放入列表(第二个由于列表monad)。像这样:

sumSold :: Games -> [Int]
sumSold = return . sum . map cntSold

答案 1 :(得分:2)

你可以做的是对函数的输出进行折叠以总结结果,如下所示:

foldl (+) 0 $ studioSales "rockstar" test

使用上述想法,我们可以通过添加您当前返回的Int列表的元素来更改函数本身以返回单个Int值:

sumByStudio:: String -> [Games] -> Int
sumByStudio studioName [] = 0
sumByStudio studioName xs = foldl (\x acc -> if fst acc == studioName then x + snd acc else x) 0 $ map getStudioAndCount xs

getStudioAndCount :: Games -> (String, Int)
getStudioAndCount  (x,y,z) = (y,z)

注意使用辅助函数来获取实际上重要的2个元素的元组。但这看起来仍然很难看,而且可以更简洁。

现在我们已经有了折叠的基本思路来得到总和,我们改变它,首先使用filter来获取所选工作室的所有记录,然后使用foldr

sumByStudio:: String -> [Games] -> Int
sumByStudio3 studioName [] = 0
sumByStudio3 studioName xs = foldr(\(_,_,z) acc -> z + acc) 0 $ filter (\(_,y,_) -> y == studioName) xs

请注意,在lambda中使用模式匹配消除了对我们在foldl示例中使用的辅助函数的需要。

最后,由于以上所有内容基本上都返回一个表示总和的值,因此将返回类型设为Int而不是[Int]可能是个好主意。但是,如果由于某种原因需要返回[Int],可以像这样修改函数:

sumByStudio3 studioName xs = flip (:) [] $ foldr(\(_,_,z) acc -> z + acc) 0 $ filter (\(_,y,_) -> y == studioName) xs    

要回答第二个问题,关于总结所有销售,你可以这样做:

sumAll :: [Games] -> Int
sumAll [] = 0
sumAll xs = foldr(\(_,_,z) acc -> z + acc) 0 xs