Haskell 中的控制语句?
我刚刚开始使用 Haskell,但从我发现的所有在线教程中,我似乎找不到是否有一种可接受的方法来执行条件控制语句。我见过 if-else、守卫和模式匹配,但它们似乎都完成同样的事情。有没有一种比其他方法被普遍接受/更快/更有效的方法?
I am just beginning Haskell, but from all the online tutorials I've found I can't seem to find if there is one accepted way to do a conditional control statement. I have seen if-else, guards, and pattern matching, but they all seem to accomplish the same thing. Is there one generally accepted/faster/more efficient way than the rest?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
守卫是(相当复杂的)用于 if-then-else 后面的模式匹配的语法糖。 If-then-else 是
case
优于Bool
的语法糖。所以这些事情大多都是同样有效的。但这里有一个观察:使用布尔表达式通常很容易低效而使用模式匹配高效。 Haskell 初学者最喜欢的一个例子是编写
其成本与
xs
的长度成正比的代码,其中成本恒定。
观察正在发生的事情的更精确的方法是(没有像视图模式这样的花哨扩展),模式匹配的最坏情况成本与出现在左侧的构造函数的数量成正比 - 你只是不能写既昂贵又小的模式匹配。相比之下,布尔表达式的大小无法告诉您评估它的成本。从这个意义上说,并且仅从这个意义上说,模式匹配比 if-then-else 或防护更便宜。
对于初学者来说,一个好的启发是尽可能使用模式匹配。随着经验的积累,您可以改进您的方法。
Guards are (rather complex) syntactic sugar for if-then-else following pattern matching. If-then-else is syntactic sugar for
case
overBool
. So these things are mostly equally efficient.But here's an observation: it's often easy to do inefficiently with a Boolean expression what is efficient with a pattern match. A favorite example of beginning Haskell programmers is to write
which costs proportional to the length of
xs
, wherecosts constant time.
A more precise way to observe what's going on is that (absent fancy extensions like view patterns), the worst-case cost of a pattern match is proportional to the number of constructors appearing on the left-hand side—you just can't write a pattern match that is both expensive and small. By contrast, the size of a Boolean expression tells you nothing about what it costs to evaluate it. In this sense, and in this sense only, pattern matching is cheaper than if-then-else or guards.
A good heuristic for beginners is to use pattern matching wherever you possibly can. As you get more experience, you can refine your approach.
好吧,我不知道用“控制语句”来思考是否是 Haskell 中最好的方法。也就是说,最终大部分都归结为模式匹配;例如,诸如
if ... then ... else
之类的布尔条件可以根据Bool
构造函数上的模式匹配来定义。最“原始”的形式可能是
case
语句——函数定义的模式匹配只是包含一个大case
表达式的单个函数定义的语法糖。就您应该使用的内容而言,请选择概念上最有意义的内容。模式匹配最适合当您需要分解代数数据类型时;
if
块适用于某些谓词需要简单的是/否结果的情况。防护通常用于需要混合使用数据类型解构和布尔谓词的情况。要记住的最重要的一点是模式匹配是分解代数数据类型的唯一方法。布尔谓词可以轻松替换为高阶函数,但在数据构造函数内提取值需要模式匹配。
Well, I don't know if thinking in terms of "control statements" is the best way to go about it in Haskell. That said, it mostly all comes down to pattern matching in the end; boolean conditionals such as
if ... then ... else
can be defined in terms of pattern matching on constructors forBool
, for instance.The most "primitive" form is probably the
case
statement--pattern matching for function definitions is just syntactic sugar over a single function definition containing one bigcase
expression.In terms of what you should use, go with whatever makes the most sense conceptually. Pattern matching is most appropriate for when you need to take apart an algebraic data type;
if
blocks are appropriate for when you need a simple yes/no result for some predicate. Guards are generally used for when you need a mixture of data type deconstruction and boolean predicates.The most significant point to keep in mind is that pattern matching is the only way to take apart an algebraic data type. Boolean predicates can be easily replaced with higher-order functions, but extracting values inside a data constructor requires pattern matching.
这三个选项都没有做完全相同的事情或可以在所有情况下使用。
模式匹配检查哪个构造函数用于创建给定值并绑定变量。 if 和警卫都不会这样做。如果您与实现 Eq.1 的类型的空构造函数(或数字文字)进行匹配,则只能使用它们而不是模式匹配。
示例:
模式防护允许您检查除相等性之外的其他内容。例如,您可以检查给定数字是否大于零。当然,您可以使用 if 执行相同的操作,但是模式防护允许您在防护失败时转到下一个模式,这可以导致比使用 if 更少的重复。示例:
使用 if 看起来像这样(注意 Nothing 的重复):
最后,if 可以在没有模式匹配的情况下使用。如果您实际上并未对给定值使用模式匹配,那么仅仅为了使用模式保护而引入
case x of ...
就没有什么意义,并且比仅使用 if 的可读性和简洁性较差。None of the three options do exactly the same thing or can be used in all situations.
Pattern matches check which constructor was used to create a given value and they bind variables. Neither if nor guards do that. You could only use them instead of pattern matches if you match against a nullary constructor (or a number literal) of a type implementing Eq.
Example:
Pattern guards allow you to check for other things than equality. E.g. you can check whether a given number is greater than zero. Of course you can do the same thing with if, but pattern guards allow you to go to the next pattern when a guard fails, which can lead to less repetition than using if. Example:
Using if this would look like this (note the repetition of Nothing):
Lastly if can be used without pattern matches. If you don't actually use pattern matching on a given value introducing a
case x of ...
just so you can use pattern guards makes little sense and is less readable and concise than just using if.我的选择基于什么使代码看起来更漂亮、更容易阅读。正如 @Don 指出的,许多不同的形式都被编译为
case
。由于可用的语法糖,它们看起来有所不同。这个糖不是给编译器的,而是给人类的。因此,请根据您认为其他人喜欢阅读的内容以及您认为可读的内容来做出决定。I choose based on what makes the code look prettier and easier to read. As @Don points out, many of these different forms are compiled to
case
. They look different because of the syntactic sugar that's available. This sugar isn't for the compiler, it's for humans. So decide based on what you think other humans would like to read, and what looks readable to you.