Haskell 中的控制语句?

发布于 2024-08-31 18:41:49 字数 124 浏览 10 评论 0原文

我刚刚开始使用 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

巴黎夜雨 2024-09-07 18:41:49

有没有一种比其他方法被普遍接受/更快/更有效的方法?

守卫是(相当复杂的)用于 if-then-else 后面的模式匹配的语法糖。 If-then-else 是 case 优于 Bool 的语法糖。所以这些事情大多都是同样有效的。

但这里有一个观察:使用布尔表达式通常很容易低效而使用模式匹配高效。 Haskell 初学者最喜欢的一个例子是编写

length xs == 0

其成本与 xs 的长度成正比的代码,其中

case xs of { [] -> True; _:_ -> False }

成本恒定。

观察正在发生的事情的更精确的方法是(没有像视图模式这样的花哨扩展),模式匹配的最坏情况成本与出现在左侧的构造函数的数量成正比 - 你只是不能写既昂贵又小的模式匹配。相比之下,布尔表达式的大小无法告诉您评估它的成本。从这个意义上说,并且仅从这个意义上说,模式匹配比 if-then-else 或防护更便宜。

对于初学者来说,一个好的启发是尽可能使用模式匹配。随着经验的积累,您可以改进您的方法。

Is there one generally accepted/faster/more efficient way than the rest?

Guards are (rather complex) syntactic sugar for if-then-else following pattern matching. If-then-else is syntactic sugar for case over Bool. 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

length xs == 0

which costs proportional to the length of xs, where

case xs of { [] -> True; _:_ -> False }

costs 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.

断念 2024-09-07 18:41:49

好吧,我不知道用“控制语句”来思考是否是 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 for Bool, 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 big case 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.

逐鹿 2024-09-07 18:41:49

这三个选项都没有做完全相同的事情或可以在所有情况下使用。

模式匹配检查哪个构造函数用于创建给定值并绑定变量。 if 和警卫都不会这样做。如果您与实现 Eq.1 的类型的空构造函数(或数字文字)进行匹配,则只能使用它们而不是模式匹配。

示例:

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 的可读性和简洁性较差。

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:

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

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:

maybeSqrt (Just x) | x >= 0 = sqrt x
maybeSqrt _ = Nothing

Using if this would look like this (note the repetition of Nothing):

maybeSqrt (Just x) = if x >= 0 then sqrt x
                     else Nothing
maybeSqrt _ = 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.

旧故 2024-09-07 18:41:49

我的选择基于什么使代码看起来更漂亮、更容易阅读。正如 @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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文