在http://lisperati.com/haskell/ht4.html上,作者显示了从简单的SVG文件中读取多边形的函数。我理解大部分代码,但我想知道是否可以重写函数
let readPoint :: String -> Point
readPoint s | Just [x,y] <- matchRegex (mkRegex "([0-9.]+),([0-9.]+)") s = (read x,read y)
以更易理解的形式。我发现这条线有点令人困惑,因为守卫应该对函数的参数进行操作(在本例中为“readPoint”),但是这里的守卫显然是对matchRegex的结果进行操作。
那么有人可以解释这背后的魔力吗?
这可以改写成更容易理解的形式吗?
答案 0 :(得分:9)
您可以将警卫视为if语句的语法糖。 guard中的表达式可以是任何有效的布尔表达式,就像在if语句中一样。这意味着您可以使用范围内的任何值和函数。
例如,您可以重写以下内容:
foo x | abc = ...
| def = ...
| otherwise = ...
as
foo x = if abc then ... else if def then ... else ...
我们也可以用case
而不是if
更详细地写这个:
foo x = case abc of
True -> ...
False -> case def of
True -> ...
False -> ...
毕竟,if
本身就是一个案例的语法糖!用case
来编写所有内容,可以更容易地看到不同的特征对于同一事物而言只是语法糖。
第二个表达式显然有意义,即使条件引用现有变量(abc
和def
)而不是函数参数x
;警卫只是以同样的方式工作。
您的示例有点复杂,因为它使用名为"pattern guards"的扩展名。这意味着守卫不仅仅是一个布尔值 - 它还可以尝试匹配一个模式。如果模式匹配,则后卫成功(例如,与具有True
的后卫相同);否则,守卫无法匹配(就像获得False
)。
我们可以想象将其重写如下:
readPoint s | Just [x, y] <- matchRegex (mkRegex "...") s = ...
| otherwise = ...
作为
readPoint s = case matchRegex (mkRegex "...") s of
Just [x, y] -> ...
_ -> ...
您可以看到此版本与case
版本的普通警卫之间的平行关系。模式守卫只是将desugaring延伸到任意模式而不仅仅是布尔值。
同样,我相信你会同意在case
语句中允许任何表达式是有意义的 - 没有理由将它限制为使用函数的参数。对于守卫来说也是如此,因为他们真的只是语法糖。
答案 1 :(得分:5)
这称为模式保护 - 阅读here。
相当于
readPoint :: String -> Point
readPoint s = case matchRegex (mkRegex "([0-9.]+),([0-9.]+)") s of
Just [x,y] -> (read x,read y)
这个想法是你可以摆脱讨厌的嵌套case语句。使用模式保护,您可以执行类似
的操作readPoint :: String -> Point
readPoint s | Just [x,y] <- matchRegex (mkRegex "([0-9.]+),([0-9.]+)") s = (read x,read y)
| Just [x] <- matchRegex (mkRegex "([0-9.]+)") s = (read x,0)
| otherwise = (0,0)
替换更详细的
readPoint :: String -> Point
readPoint s = case matchRegex (mkRegex "([0-9.]+),([0-9.]+)") s of
Just [x,y] -> (read x,read y)
Nothing -> case matchRegex (mkRegex "([0-9.]+)") s of
Just [x] -> (read x,0)
Nothing -> (0,0)