I have two functions:
calctvd :: [Perk] -> [Perk] -> Perk -> Double
calctvd ps fs p = (fromIntegral(tvn) / fromIntegral(points)) :: Double
where
tvn = calctvn ps fs p
points = length $ [i | i <- perkAndPreqs ps p, i `notElem` fs]
The above function always succeeds in returning the double that I would expect. The important line is the division (fromIntegral(tvn) / fromIntegral(points)). The function calctvn (not shown here) and the variable points are always integers, so fromIntegral() is necessary.
updatetvd :: [Perk] -> [Perk] -> [Perk]
updatetvd [] _ = []
updatetvd ps fs
--If p is in the list of elements already taken, then do not update it
| p `elem` fs = [p] ++ updatetvd (tail ps) fs
--Otherwise, update the tvd value
| otherwise = [PerkImpl (tvd, school, skill, name, def, preqstr, pref)] ++ updatetvd (tail ps) fs
where
p = head ps
PerkImpl (_, school, skill, name, def, preqstr, pref) = p
tvd = calctvd ps fs p
Essentially, this second function should just insert the value of the first function into a list. However, it only inserts the numerator of the term (fromIntegral(tvn) / fromIntegral(points)). I proved this by changing that line in calctvd to 3 / fromIntegral(points). With this, calctvd still returned the correctly divided double, whereas updatetvd always inserted a value of 3.0. It is as if Haskell does not evaluate the denominator if calctvd is called from inside updatetvd.
Update 1:
However, it appears that this oddity relies on some complexity in the above two functions. I tried to break it down into a simple example:
testcalctvd :: Double
testcalctvd = fromIntegral(3) / fromIntegral(4) :: Double
testupdatetvd :: Double
testupdatetvd = testcalctvd
However, both testcalctvd and testupdatetvd return the correct 0.75.
Update 2:
Here is an example straight from Terminal, using the test term 3 / fromIntegral(points):
> calctvd initial [] i17
0.6 {This is 3/5, because i17 has 5 points}
> updatetvd initial []
[...3.0...] {This is just the numerator 3}
Update 3:
Here is the perkAndPreqs function, which is probably the culprit, but I am not sure how much sense it will make:
--Take a perk and return a list of that perk and all of its pre-requisites
perkAndPreqs :: [Perk] -> Perk -> [Perk]
perkAndPreqs _ NULL = []
perkAndPreqs ps p = [p] ++ perkAndPreqs ps preq
where
PerkImpl (_, _, _, _, _, preqstring, _) = p
preq = perkIdentifier preqstring ps
My guess is that when you call
calctvdby hand, thepparameter you pass is not also the first element of thepsparameter. But whencalctvdis called fromupdatetvd,p = head ps.I cannot be sure, because you’ve shown us neither failing test cases nor the definition of
perkAndPreqs(ifpointsis being miscalculated as1, the clue as to why is likely to be inperkAndPreqs).