这是我生命中的第三次尝试通过Learn you a Haskell...来学习Haskell。 当作者解释警卫时,他展示了这个例子:
bmiTell :: (RealFloat a) => a -> String
bmiTell bmi
| bmi <= 18.5 = "You're underweight, you emo, you!"
| bmi <= 25.0 = "You're supposedly normal. Pffft, I bet you're ugly!"
| bmi <= 30.0 = "You're fat! Lose some weight, fatty!"
| otherwise = "You're a whale, congratulations!"
并说
这让人联想到命令式语言中的一个很大的if else树,只有这个更好,更易读。虽然大的if else树通常不受欢迎,但有时候问题是以这样一种离散的方式定义的,你无法绕过它们。警卫是一个非常好的选择。
我可以看到警卫更具可读性,但我不明白为什么这种语法“更好” 它更灵活吗?它更强大?守卫的最大优势是什么?
我的大问题可能是句子
虽然大的if else树通常不受欢迎,但有时候问题是以这样一种离散的方式定义的,你无法绕过它们
有人能举例说明吗?
答案 0 :(得分:12)
expandRange x (Just lo, Just hi) | hi < lo = (Just x, Just x)
expandRange x (Just lo, hi) | x < lo = (Just x, hi)
expandRange x (lo, Just hi) | x > hi = (lo, Just x)
expandRange _ range = range
如果我们认为Nothing
是无界的,则需要一个元素进行比较,并将一个负范围“扩展”到该元素,移动一个下限/上限以包含该元素,或者离开该范围如果元素已被包含,则不变。
现在,考虑如何在不使用警卫的情况下编写上述内容!你最终会复制一个在概念上相同的分支多少次,因为模式不同?是的,我意识到这个小例子可以被重写以完全避免这个问题,但这并不总是可能的(或者是可取的)。
在我看来,这种风格的定义是你可以使用警卫表达的最重要的事情,尽管仍然可能,如果它被写成(无人看守)模式的混合物,那将是非常冗长和更难阅读的案例和if
表达。
答案 1 :(得分:7)
Guards在语法上更轻松:
比较
describeLetter c
| c >= 'a' && c <= 'z' = "Lower case"
| c >= 'A' && c <= 'Z' = "Upper case"
| otherwise = "Not an ASCII letter"
使用:
describeLetter c =
if c >= 'a' && c <= 'z'
then "Lower case"
else if c >= 'A' && c <= 'Z'
then "Upper case"
else "Not an ASCII letter"
规则部分在语法上更清晰,更易于维护。
此外,它们与视图模式组合良好,以产生令人愉悦的语法。
f x | Just t <- bar x = Right (f t)
| otherwise = Left "some error case"
例如。
答案 2 :(得分:2)
在这个地方有很多if的任何问题都可以被视为一个决策图。您引用的LYAH示例的决策图的一部分将会出现:
.
/ \
/ \
/bmi? \
\ /
\ /
/ \ / \
/ / \ \
/ | | \
/ | | \
/ | | \
/ | | \
< 18.5 /18.5-25| | 25-30 \ > 30
/ | | \
... ... ... ...
守卫的一大优势是他们让语法结构反映了决策图的结构。如果你所拥有的只是if-then-else,那么你将不得不用一系列嵌套if来实现上面的决策图,即编码一个具有两个分支选择级联的多分支选择。嵌套如果模糊了算法的高级概念。
现在,我认为LYAH的作者在你引用的句子中得到的结论是,有时你用防守做得比用嵌套的if-then-else更好。但这只有在选择相互依赖时才会出现,即当您的决策图包含许多菱形框(选项)时,每个框只有两个分支,并且不能以任何其他方式重写。请注意,在bmiTell
示例中,每个分支都独立于另一个分支,因为BMI只能属于4个类别,这两个类别都不会与其他类别重叠。
答案 3 :(得分:0)
警卫在视觉上更明显,并且不那么冗长/对他们的“噪音”较少。
比较
bmiTell bmi
| bmi <= 18.5 = "................................."
| bmi <= 25.0 = "..........................................."
| bmi <= 30.0 = "...................................."
| otherwise = "................................"
bmiTell bmi =
if bmi <= 18.5 then "................................."
else if bmi <= 25.0 then "..........................................."
else if bmi <= 30.0 then "...................................."
else "................................"
(加上C.A. McCann said关于坠落案件的内容)。 :)