views:

340

answers:

5

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.
+6  A: 

you want:

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

which is

map f buildings 
  where f = distance (-3, -3)  
ja
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.
Jason B
obviously "map (distance firstpoint) buildings" would work, wouldn't it? Or to put it bluntly: "frombuilding point buildings = map (distance point) buildings"
ondra
Of course you can replace (-3, -3) with a variable.
helium
My mistake, it turns out a bug in my distance function was throwing an error; fixing it allowed this to work quite well. Thanks!
Jason B
you might also need to throw a fromIntegral in: distance pointA pointB = (sqrt.fromIntegral) ( (frst pointB - frst pointA) * (frst ...
ja
That's exactly what wound up fixing it
Jason B
+11  A: 
allDistances src dests = map (\point -> distance src point) dests

allDistances src dests = map (distance src) dests

allDistances src = map (distance src)

allDistances = map . distance
ephemient
Nice deduction of the point-free form.
Bas Bossink
A: 

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]
Bas Bossink
A: 
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.

trinithis
A: 

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
Greg Bacon
I don't get it. What's the point of printing things like that? It is only redundant if lPpoints and buildings are equal.
trinithis
@trinithis The "in this case" that was in my head didn't make it to my answer. Thanks and edited!
Greg Bacon