我已经编写了一个简单的函数来显示:地名、北度、东度和降雨量列表。
如何获得某个地方的平均降雨量?例如,在我的代码中,如何获得伦敦的平均降雨量?
抱歉,如果我的代码不是最好的,我只是在学习Haskell。
import Data.Char
import Data.List
type Place = (String, Float, Float, [Int])
testData :: [Place]
testData = [("London", 51.5, -0.1, [0, 0, 5, 8, 8, 0, 0]),
("Cardiff", 51.5, -3.2, [12, 8, 15, 0, 0, 0, 2]),
("Norwich", 52.6, 1.3, [0, 6, 5, 0, 0, 0, 3])]
rainLevels :: [Place] -> Float
rainLevels level (_, _, _, numbers) = sum numbers / 7发布于 2020-04-23 06:48:53
要找到“伦敦”的平均降雨量,首先需要在列表中找到“伦敦”的记录,然后求其降雨量的平均值。
getAvgRainLevel使用一个特定城市的名称:name,以及一个记录列表:xs;它通过name查找该城市的记录,然后计算该城市的平均降雨量。
getAvgRainLevel name xs = fmap avg $ lookup name (sanitize xs)
where
sanitize = fmap (\(city, _, _, rainLevels) -> (city, rainLevels))
avg = (/) <$> sum <*> fromIntegral . length发布于 2020-04-22 20:49:07
如果你想在Haskell中除以Ints,你首先必须将它们转换成Floats,这样就避免了在其他静态类型语言中遇到的除法问题,比如C,其中5/2是2。
那么,如何将Int转换为Float呢?有许多函数可以做到这一点,但最典型的选择是fromIntegral
fromIntegral :: (Num b, Integral a) => a -> b这会将任何整数类型转换为任何数值类型。因此,如果需要将[Int]转换为[Float]
fmap fromIntegral :: (Functor f, Num b, Integral a) => f a -> f b还有另外几个问题。您的代码不会按原样编译。您已经将函数rainLevels声明为只接受一个参数,但是在定义它时,您需要对两个参数进行模式匹配。按照目前的定义,rainLevels的实际类型是:
rainLevels :: (Frac e) => a -> (b, c, d, [e]) -> ePlace与(b, c, d, [e])匹配,但不使用约束(Frac e),这是fromIntegral将解决的问题,但您仍然会遇到[Place]不会在(_, _, _, numbers)上模式匹配的问题。由于我的读心术已经不幸失效,我留给您来决定rainLevels是要接受一个Place还是一个列表,但无论哪种方式,您都需要使签名与定义相匹配。
发布于 2020-04-22 23:10:31
除了对Andrew Ray的解释之外,我认为用代码提供一个可用的示例可能会有所帮助:
module Lib
( avgRainLevel
, rainLevels
, testData
) where
import Data.Char
import Data.List
type Place = (String, Float, Float, [Int])
testData :: [Place]
testData = [("London", 51.5, -0.1, [0, 0, 5, 8, 8, 0, 0]),
("Cardiff", 51.5, -3.2, [12, 8, 15, 0, 0, 0, 2]),
("Norwich", 52.6, 1.3, [0, 6, 5, 0, 0, 0, 3])]
findPlace :: [Place] -> String -> Place
findPlace placeList place = head . filter isPlace $ placeList
where isPlace (name, _, _, _) = name == place
getLevel :: Place -> [Int]
getLevel (_, _, _, levels) = levels
rainLevels :: [Place] -> String -> [Int]
rainLevels placeList place = getLevel $ findPlace placeList place
average :: [Int] -> Float
average ints = fromIntegral levelSum / fromIntegral count
where levelSum = sum ints
count = length ints
avgRainLevel :: [Place] -> String -> Float
avgRainLevel placeList place = average $ rainLevels placeList place我有点不确定你想要得到的实际值是什么。根据名称rainLevels,我得出的结论是您需要雨量列表,因此我将类型调整为[Int]。因为您不仅提供了位置列表,还提供了要查询的实际位置,所以我必须添加另一个参数,该参数将一个-> String添加到函数的类型声明中。
基于该函数,我执行了另一个函数avgRainLevel,它计算类型为[Int]的列表中的值的平均值。
代码将会像文档中描述的那样使用这些测试:
import Lib (rainLevels, avgRainLevel, testData)
import Test.Hspec (Spec, it, shouldBe)
import Test.Hspec.Runner (configFastFail, defaultConfig, hspecWith)
main :: IO ()
main = hspecWith defaultConfig {configFastFail = True} specs
specs :: Spec
specs = do
it "can read the list of levels" $ do
let levels = rainLevels testData "London"
levels `shouldBe` [0, 0, 5, 8, 8, 0, 0]
it "can calculate the average rain level" $ do
let avg = avgRainLevel testData "Norwich"
avg `shouldBe` 2请注意,实际上您不应该从这两个函数中的任何一个返回[Int]或Float。当您查询一个不在位置列表中的位置时,该代码将会死机。你最好返回Maybe [Int]和Maybe Float。在这种情况下,当您没有关于给定地点的信息时,可以返回Nothing。
https://stackoverflow.com/questions/61365035
复制相似问题