强制对 Haskell 列表进行急切求值

Forcing eager evaluation of a Haskell list

翻阅 "Learn yourself..." 这本书时,我遇到了一个 BMI 计算器应用程序。到目前为止,该应用程序采用 Double 对列表和 returns 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)后,我发现了问题所在:我试图在 的剩余部分调用 head lazy 列表,这意味着解释器尝试 first 运行 calcBMI 然后才 head-ing 结果...和这当然会失败,因为 calcBMI 需要一个对列表。

那么,我的问题是

  1. 有没有办法强制对 calcBMI 函数进行急切求值,以便我可以在结果列表中 运行 head
  2. 有没有更好的方法然后强制急切求值让我head结果列表的每个元素?

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

GHCi 会话示例:

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

这里我们询问了calcBMI xs的类型,得到了[Double]的预期。

现在我们可以使用 head:

获取 calcBMI xs 的第一个元素
> 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"

这里我们在 Double 上使用了 declareBMI。我们也可以先在列表元素上使用 declareBMI on,然后再应用 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 有一个。我们不将函数调用括起来,而是将函数的各个参数括起来。