Как я могу сравнить и вернуть данные, используя список данных - PullRequest
3 голосов
/ 17 апреля 2020

Я новичок ie до Haskell, и я изо всех сил пытаюсь найти способ использовать переменные-члены класса для возврата переменной-члена, которую я ищу. У меня есть эти данные:

 data Place = Place {name :: String, 
                north :: Float, 
                east :: Float, 
                rainfall :: [Int]
                } deriving (Eq, Ord, Show)

 testData :: [Place]
 testData = [
        Place "London"     51.5  (-0.1)  [0, 0, 5, 8, 8, 0, 0],
        Place "Norwich"    52.6  (1.3)   [0, 6, 5, 0, 0, 0, 3],
        Place "Birmingham" 52.5  (-1.9)  [0, 2, 10, 7, 8, 2, 2],
        Place "Hull"       53.8  (-0.3)  [0, 6, 5, 0, 0, 0, 4],
        Place "Newcastle"  55.0  (-1.6)  [0, 0, 8, 3, 6, 7, 5],
        Place "Aberdeen"   57.1  (-2.1)  [0, 0, 6, 5, 8, 2, 0],
        Place "St Helier"  49.2  (-2.1)  [0, 0, 0, 0, 6, 10, 0]
        ]

То, что я пытаюсь сделать, это вернуть место, ближайшее к указанному месту. Пока что я могу рассчитать расстояния для каждого места до данного места, и я точно знаю, какой Предмет должен быть возвращен, но я не знаю, как на самом деле go сделать это. Это код, который у меня есть до сих пор:

closestDry :: Float -> Float -> [Place] -> [Float]
closestDry _ _ [] = []
closestDry lx ly (x:xs) = distance(lx)(ly)(north x)(east x)):closestDry lx ly xs

distance :: Float -> Float -> Float -> Float -> Float
distance x1 y1 x2 y2 = sqrt ((y1 - y2)^2 + (x1 - x2)^2)

Набрав в консоли 'ближайший Dry 51.5 (-0.1) testData' вывод:

[0.0,1.7804484,2.059126,2.3086786,3.8078866,5.946426,3.0479496] 

Я вижу, что Ближайшая область должна быть "Лондон", чтобы с указанным списком мест, поскольку расстояние равно "0,0", но как мне вернуть это единственное место?

Я не хочу возвращать список расстояний, но я не могу понять, как заставить функцию получить наименьшее расстояние и вернуть соответствующее место, поскольку его необходимо сравнить с другими местами.

1 Ответ

5 голосов
/ 17 апреля 2020

closestDry - бесполезный беспорядок, так что избавьтесь от него. Затем напишем функцию distanceTo, которая дает вам расстояние от координат до места:

distanceTo :: Float -> Float -> Place -> Float
distanceTo lat lon place = distance lat lon (north place) (east place)

Теперь давайте напишем функцию, которая связывает места с расстояниями до них:

distancesTo :: Float -> Float -> [Place] -> [(Place, Float)]
distancesTo lat lon = map (\place -> (place, distanceTo lat lon place))

Попробуем:

λ> distancesTo 51.5 (-0.1) testData
[(Place {name = "London", north = 51.5, east = -0.1, rainfall = [0,0,5,8,8,0,0]},0.0),(Place {name = "Norwich", north = 52.6, east = 1.3, rainfall = [0,6,5,0,0,0,3]},1.7804484),(Place {name = "Birmingham", north = 52.5, east = -1.9, rainfall = [0,2,10,7,8,2,2]},2.059126),(Place {name = "Hull", north = 53.8, east = -0.3, rainfall = [0,6,5,0,0,0,4]},2.3086786),(Place {name = "Newcastle", north = 55.0, east = -1.6, rainfall = [0,0,8,3,6,7,5]},3.8078866),(Place {name = "Aberdeen", north = 57.1, east = -2.1, rainfall = [0,0,6,5,8,2,0]},5.946426),(Place {name = "St Helier", north = 49.2, east = -2.1, rainfall = [0,0,0,0,6,10,0]},3.0479496)]

Выглядит хорошо!

Теперь мы можем использовать minimumBy, comparing и snd, чтобы получить кортеж, и затем извлеките только место с помощью fst:

import Data.Foldable (minimumBy)
import Data.Ord (comparing)

closestTo :: Float -> Float -> [Place] -> Place
closestTo lat lon places = fst $ minimumBy (comparing snd) (distancesTo lat lon places)

Давайте попробуем:

λ> closestTo 51.5 (-0.1) testData
Place {name = "London", north = 51.5, east = -0.1, rainfall = [0,0,5,8,8,0,0]}

Успех!


Как альтернатива distancesTo, вы также можете рассчитать расстояния с помощью comparing, например:

closestTo :: Float -> Float -> [Place] -> Place
closestTo lat lon places = minimumBy (comparing (distanceTo lat lon)) places

Преимущество этого состоит в том, что вам не нужен ни один из кортежей, а в том, что вы пересчитываете расстояние для одного и того же места несколько раз. .


Предупреждение в любом случае: minimumBy - опасная частичная функция, которая взломает sh вашу программу, если она когда-либо получит пустой список, что произойдет, если closestTo получит пустой list:

λ> closestTo 51.5 (-0.1) []
*** Exception: Prelude.foldl1: empty list

Если вы заботитесь об этом, вам нужно избежать этого, возвращая Maybe Place вместо этого и корректируя код так, чтобы он возвращал Nothing, когда список ввода пуст, вместо вызова minimumBy. (IMO, это бородавка в Haskell, и minimumBy должен просто вернуть Maybe вместо того, чтобы взломать sh.)

...