Reputation: 665
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.
Upvotes: 11
Views: 22059
Reputation: 139411
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:
distance p
is a function of one point whose value is that point's distance from p
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
Upvotes: 0
Reputation: 1
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.
Upvotes: 0
Reputation: 9678
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]
Upvotes: 2
Reputation: 204678
allDistances src dests = map (\point -> distance src point) dests
allDistances src dests = map (distance src) dests
allDistances src = map (distance src)
allDistances = map . distance
Upvotes: 22
Reputation: 4249
you want:
map (distance (-3, -3)) buildings
which is
map f buildings
where f = distance (-3, -3)
Upvotes: 14