Search code examples
haskelltuplesfold

Total Distance using Foldl


I want get the total distance of a given list, that contains tuples of Floats. I have to guarantee that a list with less than 2 elements is going to output 0.0

What i did so far was this:

distancia :: [(Float,Float)] -> Float
distancia [] = 0.0
distancia [(_,_)] = 0.0
distancia (x:y:xs) = foldl(\(xa,ya)(xb,yb) -> sqrt(((xa-xb)**2)+((ya-yb)**2))) 0 xs

so the outputs i´m expecting is

ghci> distancia [(0,0), (0,0), (1,0), (1,10)]
11.0
ghci> distancia [(1,1), (3,4)]
3.6055512

but im getting the following error:

t3_fc42035.hs:9:22: error:
    * Couldn't match expected type `Float'
                  with actual type `(Float, Float)'
    * In the expression:
        foldl
          (\ (xa, ya) (xb, yb) -> sqrt (((xa - xb) ** 2) + ((ya - yb) ** 2)))
          0
          xs
      In an equation for `distancia':
          distancia (x : y : xs)
            = foldl
                (\ (xa, ya) (xb, yb) -> sqrt (((xa - xb) ** 2) + ((ya - yb) ** 2)))
                0
                xs
  |
9 | distancia (x:y:xs) = foldl(\(xa,ya)(xb,yb) -> sqrt(((xa-xb)**2)+((ya- yb)**2))) 0 xs
  |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

t3_fc42035.hs:9:47: error:
    * Couldn't match expected type `(Float, Float)'
                  with actual type `Float'
    * In the expression: sqrt (((xa - xb) ** 2) + ((ya - yb) ** 2))
      In the first argument of `foldl', namely
        `(\ (xa, ya) (xb, yb)
            -> sqrt (((xa - xb) ** 2) + ((ya - yb) ** 2)))'
      In the expression:
        foldl
          (\ (xa, ya) (xb, yb) -> sqrt (((xa - xb) ** 2) + ((ya - yb) ** 2)))
      0
      xs
  |
9 | distancia (x:y:xs) = foldl(\(xa,ya)(xb,yb) -> sqrt(((xa-xb)**2)+((ya-yb)**2))) 0 xs
  |                                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

I don't understand why i have to match the type (Float,Float)


Solution

  • Folding, even if it feels like a reasonable approach, is not the right tool for this particuluar case since to calculate an accumulator value you need two items from the list at once along with the accumulator itself. This is not possible so no folding.

    Could be done like;

    Prelude> :{
    Prelude| let dist = sum . (zipWith hypot <*> tail)
    Prelude|            where hypot = \(a,b) (c,d) -> sqrt((a-c)^2 + (b-d)^2)
    Prelude| :}
    Prelude> dist [(0,0), (0,0), (1,0), (1,10)]
    11.0
    Prelude> dist [(1,1), (3,4)]
    3.605551275463989