强制评估Haskell列表

时间:2015-07-26 09:11:39

标签: haskell recursion lazy-evaluation

翻阅“自学......”一书,我遇到了一个BMI计算器应用程序。到目前为止,该应用程序采用Double对列表并返回Double列表:

calcBMI :: [(Double, Double)] -> [Dobule]
calcBMI xs = [bmi w h | (w, h) <- xs]
    where bmi weight height = weight / height ^ 2

尝试将另一个函数应用于返回列表的每个元素我尝试了map declareBMI head calcBMI [(w, h) | w <- [60..70], h <- [1.65]]但是出现了错误(定义了declareBMI):

declareBMI :: Double -> String
declareBMI bmi
    | bmi <= 18.5 = "Skinny"
    | bmi <= 25 = "Norm"
    | bmi <=30 = "Overweight"
    | otherwise = "OBESE!"

在查看了map的定义(即map f x:xs = f x : map f xs)后,我发现了问题:我正试图在 lazy head > list,意思是解释器尝试第一次运行calcBMI,然后只有head - 结果......这当然会失败,因为calcBMI需要对列表。

所以,我的问题是

  1. 有没有办法强制热切评估calcBMI函数,以便在结果列表中运行head
  2. 是否有更好的方法,然后强制进行急切评估,以允许我head结果列表中的每个元素?

1 个答案:

答案 0 :(得分:1)

确保类型排列。如果您不确定类型或编译器错误,请尝试将代码分解为较小的表达式,并使用GHCi或TypedHoles来检查类型。

示例GHCi会话:

> let xs = [(w, h) | w <- [60..70], h <- [1.65]]
> :t calcBMI xs
calcBMI xs :: [Double]

我们在此处询问了calcBMI xs的类型,并按预期获得了[Double]

现在我们可以使用calcBMI xs

获取head的第一个元素
> head (calcBMI xs)
22.03856749311295

我们可以使用head,因为它的输入是一个列表,calcBMI xs是一个列表。

> :t head
head :: [a] -> a

declareBMI的类型为Double -> String,因此我们可以在普通Double值上使用它,但我们也可以在map列表中的值上使用它。

> let bmis = calcBMI xs
> declareBMI (head bmis)
"Norm"

我们在declareBMI上使用了Double。我们也可以先在列表元素上使用declareBMI,然后再应用head

> :t map declareBMI bmis
map declareBMI bmis :: [String]
> map declareBMI bmis
["Norm","Norm","Norm","Norm","Norm","Norm","Norm","Norm","Norm","Overweight","Overweight"]
> head (map declareBMI bmis)
"Norm"

另外,不要忘记在Haskell中我们只使用空格来应用函数。所以head (map declareBMI bmis)是正确的,因为map在这里有两个参数,head需要一个参数。我们不会为函数调用括号,而是函数的各个参数。