强制对 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
需要一个对列表。
那么,我的问题是
- 有没有办法强制对
calcBMI
函数进行急切求值,以便我可以在结果列表中 运行 head
?
- 有没有更好的方法然后强制急切求值让我
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
有一个。我们不将函数调用括起来,而是将函数的各个参数括起来。
翻阅 "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
需要一个对列表。
那么,我的问题是
- 有没有办法强制对
calcBMI
函数进行急切求值,以便我可以在结果列表中 运行head
? - 有没有更好的方法然后强制急切求值让我
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
有一个。我们不将函数调用括起来,而是将函数的各个参数括起来。