Haskell函数保护如何在函数参数之外的其他值上运行?

Mar*_*Lux 10 haskell

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)
Run Code Online (Sandbox Code Playgroud)

以更易理解的形式.我发现这条线有点令人困惑,因为守卫应该对函数的参数进行操作(在本例中为"readPoint"),但是这里的守卫显然是对matchRegex的结果进行操作.

所以有人可以解释这背后的魔力吗?

这可以改写成更容易理解的形式吗?

Tik*_*vis 9

您可以将警卫视为if语句的语法糖.guard中的表达式可以是任何有效的布尔表达式,就像在if语句中一样.这意味着您可以使用范围中的任何值和函数.

例如,您可以重写以下内容:

foo x | abc = ...
      | def = ...
      | otherwise = ...
Run Code Online (Sandbox Code Playgroud)

foo x = if abc then ... else if def then ... else ...
Run Code Online (Sandbox Code Playgroud)

我们也可以更详细地写这个case而不是if:

foo x = case abc of
  True -> ...
  False -> case def of
    True -> ...
    False -> ...
Run Code Online (Sandbox Code Playgroud)

毕竟,if本身只是一个案例的语法糖!用一些方式编写所有内容case可以更容易地看到不同的功能如何只是同一件事的语法糖.

即使条件引用现有变量(abcdef)而不是函数参数x,第二个表达式仍然有意义; 警卫只是以同样的方式工作.

你的例子有点复杂,因为它使用了一个名为"模式守卫"的扩展.这意味着守卫不仅仅是一个布尔值 - 它还可以尝试匹配一个模式.如果模式匹配,则后卫成功(例如,与后卫相同True); 否则,后卫无法匹配(就像获得False).

我们可以想象将其重写如下:

readPoint s | Just [x, y] <- matchRegex (mkRegex "...") s = ...
            | otherwise = ...
Run Code Online (Sandbox Code Playgroud)

readPoint s = case matchRegex (mkRegex "...") s of
                Just [x, y] -> ...
                _ -> ...
Run Code Online (Sandbox Code Playgroud)

您可以看到此case版本和普通警卫版本之间的平行关系.模式守卫只是将desugaring延伸到任意模式而不仅仅是布尔.

同样,我相信你会同意在语句中允许任何表达式是有意义的case- 没有理由将它限制为使用函数的参数.卫兵也是如此,因为他们真的只是语法糖.

  • 优秀!提醒一下,Haskell中的所有内容都只是某些case和lambda表达式的语法糖,这总是好的. (3认同)

And*_*ewC 5

这被称为模式保护 - 在这里阅读它.

它相当于

readPoint :: String -> Point
readPoint s = case matchRegex (mkRegex "([0-9.]+),([0-9.]+)") s of
                   Just [x,y] -> (read x,read y) 
Run Code Online (Sandbox Code Playgroud)

这个想法是你可以摆脱讨厌的嵌套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)
Run Code Online (Sandbox Code Playgroud)

替换更冗长的

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)
Run Code Online (Sandbox Code Playgroud)