在哪种感觉守卫比命令更好 - 如果? (haskell新手)

时间:2012-10-09 13:52:03

标签: haskell

这是我生命中的第三次尝试通过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树通常不受欢迎,但有时候问题是以这样一种离散的方式定义的,你无法绕过它们

有人能举例说明吗?

4 个答案:

答案 0 :(得分:12)

Don给出了使用警卫的主要动机,但除此之外,他们还很好地结合了模式匹配。如果模式上的所有防护装置都失败,它会下降到下一个模式,因此您可以同时检查模式和条件,而不会出现大量重复的坠落情况。这是一个(非常人为的)例子:

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关于坠落案件的内容)。 :)