11
votes

Is there a way to use Haskell's "map" or something similar with multiple arguments?

i.e. to find the distance between a given point (defined as a tuple) and a list of other points:

map distance (-3,-3) buildings

Clearly, that doesn't work, because it tries to map "distance" to (-3,-3), where distance expects two tuples:

let distance pointA pointB = sqrt ( (frst pointB - frst pointA) * (frst pointB - frst pointA) + (scnd pointB - scnd pointA) * (scnd pointB - scnd pointA) )

distance takes two points as arguments: one is (-3,-3) in this example, and one is selected from the list "buildings".

(-3,-3) is just an example. This will have to be a variable; it can't be hardcoded into the function.

Maybe this will make a little more sense:

buildings = [(3,-2),(2,1),(5,3),(4,3),(4,-1)]

firstDiff pointA pointB = subtract ( fst pointA ) ( fst pointB )

secondDiff pointA pointB = subtract ( snd pointA ) ( snd pointB )

distance pointA pointB = sqrt ( (firstDiff pointA pointB) * (firstDiff pointA pointB) +     (secondDiff pointA pointB) * (secondDiff pointA pointB))

--- What I need to happen here is a list "score" to be created by taking all distances from a point in a list lPoints to a point in list buildings.
5

5 Answers

21
votes
allDistances src dests = map (\point -> distance src point) dests

allDistances src dests = map (distance src) dests

allDistances src = map (distance src)

allDistances = map . distance
14
votes

you want:

map (distance (-3, -3)) buildings

which is

map f buildings 
  where f = distance (-3, -3)  
2
votes

After seeing the comment on ja's response I'm guessing you wish to use zipWith

Prelude>:type zipWith
zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]

The documentation states:

zipWith generalises zip by zipping with the function given as the first argument, instead of a tupling function. For example, zipWith (+) is applied to two lists to produce the list of corresponding sums.

So in your code above this could look like:

Prelude> let dist a b = sqrt ( (fst b - fst a) * (fst b - fst a) + (snd b - snd a) * (snd b - snd a) )
Prelude> let buildings = [(1.0,1.0 ), (3.0,3.0 ), (4.0,4.0)]
Prelude> let points = [ (1.2, 2.23), (2.23, 34.23), (324.3, 34.3) ]
Prelude> zipWith dist points buildings
[1.2461540835707277,31.239491032985793,321.7299799521332]
0
votes
distance (x, y) (z, w) = sqrt $ (x - z) ^ 2 + (y - w) ^ 2

func1 = map . distance

func2 starts ends = starts >>= flip func1 ends

func1 is the function you described, whereas func2 is similar but takes in multiple start points instead of just one and finds the distance between every combination with the end points.

0
votes

The distance formula is straightforward:

distance :: Floating a => (a,a) -> (a,a) -> a
distance (x1,y1) (x2,y2) = sqrt $ (x2 - x1)^2 + (y2 - y1)^2

Note the use of pattern matching to decompose the arguments rather than littering the code with fst and snd.

The respective distances from a given point to all points in a list is then

distanceFrom :: Floating a => (a,a) -> [(a,a)] -> [a]
distanceFrom p = map (distance p)

Although arguments appear to be missing, this is known in Haskell parlance as partial application. In distanceFrom, we have two of them:

  1. distance p is a function of one point whose value is that point's distance from p
  2. map (distance p) is a function of a list of points whose value is those points' respective distances from p

Try to design your Haskell functions for partial application to make it easy to combine small functions into bigger ones. As noted in ephemient's answer, you could carry this a step further to get pointfree definition (no explicit arguments)—a more elegant, advanced style.

The distance to each point in buildings from all points in lPoints is then

main :: IO ()
main = do
  mapM_ (putStrLn . unwords . map (printf "%6.3f")) score
  where
    score = [ distanceFrom x buildings | x <- lPoints ]

For example, making lPoints and buildings equal, the output is

 0.000  3.162  5.385  5.099  1.414
 3.162  0.000  3.606  2.828  2.828
 5.385  3.606  0.000  1.000  4.123
 5.099  2.828  1.000  0.000  4.000
 1.414  2.828  4.123  4.000  0.000

But that's a little boring in this particular case given all the redundancy. To instead print the strict upper triangle, use

strictUpperTriangle :: [[a]] -> [[a]]
strictUpperTriangle [] = []
strictUpperTriangle xs = go (init xs)
  where go (x:xs) = tail x : map tail (go xs)
        go [] = []

printSUT :: PrintfArg a => [[a]] -> IO ()
printSUT sut = putStr (unlines $ map pad sut)
  where n = length sut
        pad xs = let k = n - length xs in
                 unwords $ take k blanks ++ map (printf "%*.3f" w) xs
        blanks = repeat (take w $ repeat ' ')
        w = 6 :: Int

main :: IO ()
main = printSUT tri
  where
    score = [ distanceFrom x buildings | x <- lPoints ]
    tri = strictUpperTriangle score

Output:

 3.162  5.385  5.099  1.414
        3.606  2.828  2.828
               1.000  4.123
                      4.000