我刚刚开始使用 Haskell,但是从我发现的所有在线教程中,我似乎无法找到是否有一种可接受的方式来执行条件控制语句。我见过 if-else、守卫和模式匹配,但它们似乎都完成了同样的事情。是否有一种普遍接受/更快/更有效的方式比其他方式?
4 回答
是否有一种普遍接受/更快/更有效的方式比其他方式?
守卫是(相当复杂的)语法糖,用于 if-then-else 以下模式匹配。If-then-else 是case
over的语法糖Bool
。所以这些东西大多同样有效。
但这里有一个观察:使用布尔表达式通常很容易低效地执行模式匹配的高效操作。开始 Haskell 程序员最喜欢的例子是编写
length xs == 0
其成本与 的长度成正比xs
,其中
case xs of { [] -> True; _:_ -> False }
花费恒定的时间。
观察正在发生的事情的更精确的方法是(没有像视图模式这样的花哨的扩展),模式匹配的最坏情况成本与出现在左侧的构造函数的数量成正比——你不能写一个既昂贵又小规模的模式匹配。相比之下,布尔表达式的大小无法告诉您评估它的成本。在这个意义上,并且仅在这个意义上,模式匹配比 if-then-else 或 guards 更便宜。
对于初学者来说,一个很好的启发是尽可能地使用模式匹配。随着您获得更多经验,您可以改进您的方法。
好吧,我不知道从“控制语句”的角度思考是否是在 Haskell 中处理它的最佳方式。也就是说,最终归结为模式匹配。例如,if ... then ... else
可以根据构造函数的模式匹配来定义布尔条件Bool
。
最“原始”的形式可能是语句——函数定义的模式匹配只是包含一个大表达式case
的单个函数定义的语法糖。case
就你应该使用的东西而言,选择在概念上最有意义的东西。当您需要拆分代数数据类型时,模式匹配最合适;if
当您需要某个谓词的简单是/否结果时,块适用。当您需要数据类型解构和布尔谓词的混合时,通常使用守卫。
要记住的最重要的一点是模式匹配是分解代数数据类型的唯一方法。布尔谓词可以很容易地用高阶函数替换,但是在数据构造函数中提取值需要模式匹配。
这三个选项中没有一个做完全相同的事情,也不能在所有情况下使用。
模式匹配检查哪个构造函数用于创建给定值并绑定变量。if 和警卫都不会那样做。如果您匹配实现 Eq 的类型的空构造函数(或数字文字),则只能使用它们而不是模式匹配
例子:
foo (Just x) = x+1 -- Can not do this without a pattern match (except by using
-- functions like fromJust that themselves use pattern matches)
foo Nothing = 0 -- You could do this using a pattern guards like
-- foo x | x==Nothing = 0, but that is less readable and less
-- concise than using a plain pattern match
模式守卫允许您检查除相等之外的其他内容。例如,您可以检查给定数字是否大于零。当然你可以用 if 做同样的事情,但是当一个守卫失败时,模式守卫允许你进入下一个模式,这比使用 if 可以减少重复。例子:
maybeSqrt (Just x) | x >= 0 = sqrt x
maybeSqrt _ = Nothing
使用 if 这看起来像这样(注意 Nothing 的重复):
maybeSqrt (Just x) = if x >= 0 then sqrt x
else Nothing
maybeSqrt _ = Nothing
最后 if 可以在没有模式匹配的情况下使用。如果您实际上并未对给定值使用模式匹配,case x of ...
那么您可以使用模式保护就没有什么意义,并且比仅使用 if 更不易于阅读和简洁。
我根据使代码看起来更漂亮和更易于阅读的原因进行选择。正如@Don 指出的那样,许多这些不同的形式被编译为case
. 由于可用的语法糖,它们看起来不同。这种糖不是给编译器的,它是给人类的。因此,请根据您认为其他人想要阅读的内容以及您认为可以阅读的内容来决定。