Xander Dunn
Xander Dunn

Reputation: 2402

Haskell Returns only Numerator of Division (Sometimes)

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

Upvotes: 0

Views: 165

Answers (1)

dave4420
dave4420

Reputation: 47062

My guess is that when you call calctvd by hand, the p parameter you pass is not also the first element of the ps parameter. But when calctvd is called from updatetvd, p = head ps.

I cannot be sure, because you've shown us neither failing test cases nor the definition of perkAndPreqs (if points is being miscalculated as 1, the clue as to why is likely to be in perkAndPreqs).

Upvotes: 1

Related Questions