import Data.Map
import List
type Prefs = Map [Char] (Map [Char] Double)
type SimilarityF = Prefs -> [Char] -> [Char] -> Double
type Person = [Char]
critics = fromList [
("Lisa Rose", fromList [("Lady in the Water", 2.5),
("Snakes on a Plane", 3.5),
("Just My Luck", 3.0),
("Superman Returns", 3.5),
("You, Me and Dupree", 2.5),
("The Night Listener", 3.0)]),
("Gene Seymour", fromList [("Lady in the Water", 3.0),
("Snakes on a Plane", 3.5),
("Just My Luck", 1.5),
("Superman Returns", 5.0),
("The Night Listener", 3.0),
("You, Me and Dupree", 3.5)]),
("Michael Phillips", fromList [("Lady in the Water", 2.5),
("Snakes on a Plane", 3.0),
("Superman Returns", 3.5),
("The Night Listener", 4.0)]),
("Claudia Puig", fromList [("Snakes on a Plane", 3.5),
("Just My Luck", 3.0),
("The Night Listener", 4.5),
("Superman Returns", 4.0),
("You, Me and Dupree", 2.5)]),
("Mick LaSalle", fromList [("Lady in The Water", 3.0),
("Snakes on a Plane", 4.0),
("Just My Luck", 2.0),
("Superman Returns", 3.0),
("The Night Listener", 3.0),
("You, Me and Dupree", 2.0)]),
("Jack Matthews", fromList [("Lady in the Water", 3.0),
("Snakes on a Plane", 4.0),
("The Night Listener", 3.0),
("Superman Returns", 5.0),
("You, Me and Dupree", 3.5)]),
("Toby", fromList [("Snakes on a Plane", 4.5),
("You, Me and Dupree", 1.0),
("Superman Returns", 4.0)]),
("xa4a", fromList [])
]
sim_distance :: SimilarityF
sim_distance prefs person1 person2 =
let squares = intersectionWith (\x y->(x-y)**2) (prefs!person1) (prefs!person2)
in
if length (elems squares) == 0 then 0
else 1/(1+(sum (elems squares)))
sim_pearson :: SimilarityF
sim_pearson prefs p1 p2 =
let
si = intersectionWith (\x y->1) (prefs!p1) (prefs!p2)
n = fromIntegral (length (elems si))
sum1 = sum [prefs!p1!it | it <- keys si]
sum2 = sum [prefs!p2!it | it <- keys si]
sum1Sq = sum [prefs!p1!it**2 | it <- keys si]
sum2Sq = sum [prefs!p2!it**2 | it <- keys si]
pSum = sum [prefs!p1!it * prefs!p2!it | it <- keys si]
num = pSum - (sum1*sum2/n)
den = sqrt ((sum1Sq - sum1**2/n)*(sum2Sq-sum2**2/n))
in
if or [n==0, den==0] then 0
else num/den
topMatches :: Prefs -> Person -> Int -> SimilarityF -> [(Double,[Char])]
topMatches prefs person n similarity =
let scores = [(similarity prefs person other, other) | other <- keys prefs, other/=person]
in take n (reverse (sort scores))
--getRecommendations :: Prefs -> Person -> SimilarityF -> a
getRecommendations prefs person similarity =
let
others_w = filterWithKey (\k v->k/=person && v>0) (Data.Map.mapWithKey (\k a->similarity prefs person k) prefs)
totals_for_item item = sum [others_w!other * prefs!other!item | other <- keys (Data.Map.filter (member item) prefs)]
sim_sums item = sum [others_w!other | other <- keys (Data.Map.filter (member item) prefs)]
get_new_totals old_totals new_weight new_items =
unionWith (\x y->x+y*new_weight) old_totals new_items
t_folder key weight acc = get_new_totals acc weight (prefs!key)
totals = foldWithKey t_folder empty others_w
get_new_sims old_sims new_items sim =
let new_sims = fromList [(item, sim) | item <- keys new_items, notMember item (prefs!person)]
in unionWith (+) old_sims new_sims
s_folder key weight acc = get_new_sims acc (prefs!key) weight
simSums = foldWithKey s_folder empty others_w
rankings = [(total/(simSums!item)) | (item,total) <- assocs totals]
in reverse (sort rankings)