Map applied to multiple arguments in Haskell
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开发者_如何学Go" to be created by taking all distances from a point in a list lPoints to a point in list buildings.
allDistances src dests = map (\point -> distance src point) dests
allDistances src dests = map (distance src) dests
allDistances src = map (distance src)
allDistances = map . distance
you want:
map (distance (-3, -3)) buildings
which is
map f buildings
where f = distance (-3, -3)
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]
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.
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 fromp
map (distance p)
is a function of a list of points whose value is those points' respective distances fromp
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
精彩评论