Существует ли способ использовать "карту" Haskell или что-то похожее с несколькими аргументами?
т.е. найти расстояние между данной точкой (определенным как кортеж) и списком других точек:
map distance (-3,-3) buildings
Очевидно, это не работает, потому что это пытается отобразить "расстояние" до (-3,-3), где расстояние ожидает два кортежа:
let distance pointA pointB = sqrt ( (frst pointB - frst pointA) * (frst pointB - frst pointA) + (scnd pointB - scnd pointA) * (scnd pointB - scnd pointA) )
расстояние берет две точки в качестве аргументов: каждый (-3,-3) в этом примере, и каждый выбран из списка "здания".
(-3,-3), просто пример. Это должно будет быть переменной; это не может быть hardcoded в функцию.
Возможно, это будет иметь немного больше смысла:
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.
вы хотите:
map (distance (-3, -3)) buildings
то есть
map f buildings
where f = distance (-3, -3)
allDistances src dests = map (\point -> distance src point) dests
allDistances src dests = map (distance src) dests
allDistances src = map (distance src)
allDistances = map . distance
После просмотра комментария к ответу ja Я предполагаю, что вы хотите использовать zipWith
Prelude>:type zipWith
zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
zipWith обобщает zip путем архивирования с функцией, заданной в качестве первого аргумента, вместо функции кортежа. Например, zipWith (+) применяется к двум спискам для получения списка соответствующих сумм.
В приведенном выше коде это может выглядеть так:
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 :: Floating a => (a,a) -> (a,a) -> a
distance (x1,y1) (x2,y2) = sqrt $ (x2 - x1)^2 + (y2 - y1)^2
Обратите внимание на использование сопоставления по образцу для разложения аргументов, а не на замусоривание кода fst
и snd
.
Соответствующие расстояния от заданной точки до всех точек в списке находятся тогда
distanceFrom :: Floating a => (a,a) -> [(a,a)] -> [a]
distanceFrom p = map (distance p)
Хотя аргументы кажутся отсутствующими, это известно на языке Haskell как частичное применение. В distanceFrom
их два:
distance p
- функция одной точки, значение которой - расстояние этой точки от p
map (distance p)
- функция списка точек, значение которой - соответствующие расстояния этих точек от p
Старайтесь проектировать свои функции Haskell для частичного применения, чтобы было легко объединять маленькие функции в большие. Как отмечено в ответе ephemient'а, вы можете продвинуть это на шаг дальше, чтобы получить pointfree определение (без явных аргументов) - более элегантный, продвинутый стиль.
Тогда расстояние до каждой точки в зданиях
от всех точек в lPoints
будет
main :: IO ()
main = do
mapM_ (putStrLn . unwords . map (printf "%6.3f")) score
where
score = [ distanceFrom x buildings | x <- lPoints ]
Например, если сделать lPoints
и здания
равными, то получится
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
Но это немного скучно в данном конкретном случае, учитывая всю избыточность. Чтобы вместо этого вывести строгий верхний треугольник, используйте
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
distance (x, y) (z, w) = sqrt $ (x - z) ^ 2 + (y - w) ^ 2
func1 = map . distance
func2 starts ends = starts >>= flip func1 ends
func1 - это функция, которую вы описали, а func2 похожа, но принимает несколько начальных точек вместо одной и находит расстояние между каждой комбинацией с конечными точками.