Карта применяется к нескольким аргументам в Haskell - PullRequest
8 голосов
/ 02 марта 2010

Есть ли способ использовать "карту" Хаскелла или нечто подобное с несколькими аргументами?

т.е. чтобы найти расстояние между данной точкой (определенной как кортеж) и списком других точек:

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) - только пример. Это должно быть переменной; он не может быть жестко закодирован в функцию.

Может быть, это будет иметь немного больше смысла:

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 ]

19 голосов
/ 02 марта 2010
allDistances src dests = map (\point -> distance src point) dests

allDistances src dests = map (distance src) dests

allDistances src = map (distance src)

allDistances = map . distance
11 голосов
/ 02 марта 2010

Вы хотите:

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

, что

map f buildings 
  where f = distance (-3, -3)  
2 голосов
/ 02 марта 2010

После просмотра комментария к ответу Джа, я думаю, вы хотите использовать zipWith

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

Документация гласит:

zipWith обобщает zip, архивируя с функцией, заданной в качестве первого аргумента, вместо функции tupling Например, 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]
0 голосов
/ 02 марта 2010

Формула расстояния проста:

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 у нас их два:

  1. distance p является функцией одной точки, значение которой равно расстоянию этой точки от p
  2. map (distance p) является функцией списка точек, значение которого равно соответствующим расстояниям этих точек от p

Попробуйте спроектировать функции Haskell для частичного применения, чтобы упростить объединение небольших функций в более крупные. Как отмечалось в ответе ephemient , вы можете продвинуться на шаг дальше, чтобы получить определение pointfree (без явных аргументов) - более элегантный, продвинутый стиль.

Расстояние до каждой точки в buildings от всех точек в lPoints равно

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

Например, при равенстве lPoints и buildings получается

 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

Выход:

 3.162  5.385  5.099  1.414
        3.606  2.828  2.828
               1.000  4.123
                      4.000
0 голосов
/ 02 марта 2010
distance (x, y) (z, w) = sqrt $ (x - z) ^ 2 + (y - w) ^ 2

func1 = map . distance

func2 starts ends = starts >>= flip func1 ends

func1 - это функция, которую вы описали, тогда как func2 похожа, но принимает несколько начальных точек вместо одной и находит расстояние между каждой комбинацией с конечными точками.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...