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
, and 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]}
Success!
作为替代方案distancesTo
,您还可以使用以下方法计算距离comparing
, 像这样:
closestTo :: Float -> Float -> [Place] -> Place
closestTo lat lon places = minimumBy (comparing (distanceTo lat lon)) places
这样做的优点是不需要任何元组,但缺点是多次重新计算同一位置的距离。
对任何一种方式的警告:minimumBy
是一个危险的部分函数,如果它得到一个空列表,它将使你的程序崩溃,如果closestTo
得到一个空列表:
λ> closestTo 51.5 (-0.1) []
*** Exception: Prelude.foldl1: empty list
如果你关心这一点,你需要通过返回来避免它Maybe Place
相反,并调整代码以返回Nothing
当输入列表为空时,而不是调用minimumBy
。 (IMO,这是 Haskell 中的一个缺陷,并且minimumBy
应该只返回一个Maybe
本身而不必崩溃。)