我对Haskell和函数式编程都是非常陌生的。我的问题很简单。模式匹配和卫士有什么不同?
使用模式匹配的函数
check :: [a] -> String
check [] = "Empty"
check (x:xs) = "Contains Elements"
使用guards的函数
check_ :: [a] -> String
check_ lst
| length lst < 1 = "Empty"
| otherwise = "Contains elements"
在我看来,模式匹配和Guard在本质上是一样的。两者都计算一个条件,如果为true,将执行与其挂钩的表达式。我的理解是正确的吗?
在本例中,我可以使用模式匹配或保护来得到相同的结果。但有些东西告诉我,我在这里错过了一些重要的东西。我们总是可以用一个替换另一个吗?
谁能举个例子,模式匹配比守卫更受欢迎,反之亦然?
发布于 2010-11-12 00:44:22
实际上,它们从根本上是完全不同的!至少在Haskell,至少是这样。
Guard既简单又灵活:它们本质上只是一种特殊的语法,可以转换为一系列if/then表达式。您可以在if
中放入任意的布尔表达式,但它们不会执行您无法使用正则guards执行的任何操作。
模式匹配可以做几件额外的事情:它们是解构数据的唯一方法,并且它们在其作用域内绑定标识符。在同样的意义上,case
等同于if
表达式,模式匹配等同于guards表达式。声明(在顶层或类似于let
表达式的形式)也是模式匹配的一种形式,“普通”定义与简单的模式(单个标识符)相匹配。
模式匹配也往往是Haskell中实际发生事情的主要方式--尝试解构模式中的数据是强制执行计算的少数事情之一。
顺便说一句,您实际上可以在顶级声明中进行模式匹配:
square = (^2)
(one:four:nine:_) = map square [1..]
这有时对一组相关的定义很有用。
GHC还结合了provides the ViewPatterns extension和GHC;您可以在绑定上下文中使用任意函数,然后对结果进行模式匹配。当然,这仍然只是常规内容的语法糖。
至于在哪里使用的日常问题,这里有一些粗略的指南:
对于任何可以直接匹配到一两个构造函数深度的东西,
@
语法允许你将整个结构绑定到一个变量上,同时也可以对其进行模式匹配,但是在一个模式中做太多这样的事情可能会让quickly.Int
值,看看哪一个更大。@
和_
.case
表达式放在函数体中,以便将一些模式匹配的内容下推到函数上,并放在主定义之外。发布于 2010-11-12 00:43:18
在我看来,模式匹配和Guard在本质上是一样的。两者都计算一个条件,如果为true,将执行与其挂钩的表达式。我的理解是正确的吗?
不完全是。首先,模式匹配不能评估任意条件。它只能检查某个值是否是使用给定的构造函数创建的。
第二,模式匹配可以绑定变量。因此,虽然模式[]
可能等同于保护null lst
(不使用长度,因为这不是等同的-稍后将详细介绍),但模式x:xs
肯定不等同于保护not (null lst)
,因为该模式绑定了变量x
和xs
,而保护不绑定这些变量。
关于使用length
的注意事项:使用length
检查列表是否为空是非常糟糕的做法,因为要计算需要遍历整个列表的长度,这将花费O(n)
时间,而仅检查列表是否为空则需要使用null
或模式匹配的O(1)
时间。此外,使用``length‘只是普通的,在无限列表上不起作用。
发布于 2010-11-12 00:44:28
除了其他好的答案之外,我将尝试具体地介绍卫士:卫士只是语法上的糖。如果你仔细想想,你的程序中通常会有以下结构:
f y = ...
f x =
if p(x) then A else B
也就是说,如果模式匹配,则紧跟其后的是if-then-else判别。守卫将这种辨别直接放入模式匹配中:
f y = ...
f x | p(x) = A
| otherwise = B
(在标准库中,otherwise
被定义为True
)。它比if-then-else链更方便,有时它还使代码变得更简单,因此比if-then-else结构更容易编写。
换句话说,它是在另一个结构之上的糖,在许多情况下大大简化了你的代码。你会发现它消除了很多if-then-else链,使你的代码更具可读性。
https://stackoverflow.com/questions/4156727
复制相似问题