2014-07-20 18 views
0
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]」。如何從這個函數輸出一個[Int]?

我該如何修改這個,所以當調用「studioSales」rockstar「test」時,返回的值是「[600]」,其中兩個Int是相加的。

另外我怎麼能加起來所有的銷售?所以一個函數會返回所有加起來的整數?

回答

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 
+1

謝謝,很好地解釋。很好的幫助。 –

+1

那麼foldl基本上減少了名單? –

+1

是的,'foldl'和'fol​​dr'都用一些指定的操作來減少列表,這是在這種情況下的總和。看看這個主題的更多信息:http://www.haskell.org/haskellwiki/Fold –

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](的Int列表),您可以使用(:[])return使一個單一的值轉換成一個列表(第二個原因列出單子)。像這樣:

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

不錯。在第一個版本中,'studioSales'應該使用's',而不是'studio'作爲參數。 – chi

+0

@chi固定,不同。 –

相關問題