【问题标题】:Control statements in Haskell?Haskell 中的控制语句?
【发布时间】:2011-02-16 13:53:24
【问题描述】:

我刚刚开始使用 Haskell,但是从我发现的所有在线教程中,我似乎无法找到是否有一种可接受的方式来执行条件控制语句。我见过 if-else、guards 和模式匹配,但它们似乎都完成了同样的事情。有没有一种普遍接受/更快/更有效的方法?

【问题讨论】:

  • 所有这些技术都编译为“案例”。 'case' 是基本的控制机制——基于模式的调度。守卫、模式等只是糖。
  • @Don Stewart:甚至if...then...else?我真的很想知道这一点。在没有内置形式的情况下编写布尔条件显然需要case(至少忽略可怕的不安全事物),但我不知道编译器是否无论如何都会以这种方式去糖。
  • case x of True -> ... ; False -> ...?
  • @jrockway:我认为 camccann 知道您如何这样编写布尔条件 - 问题是编译器是否真的确实执行那个转变。 (作为另一个例子:乘法本质上是重复加法的糖,但我认为没有任何编译器实际上是这样对待它的。)
  • 但“真”和“假”并不是特别硬连线,它们只是另一种类型。

标签: haskell conditional guard if-statement


【解决方案1】:

是否有一种普遍接受/更快/更有效的方法?

守卫是(相当复杂的)语法糖,用于 if-then-else 以下模式匹配。 If-then-else 是 case 的语法糖,而不是 Bool。所以这些东西大多同样有效。

但这里有一个观察:使用布尔表达式inefficiently 通常很容易使用模式匹配efficient。开始 Haskell 程序员最喜欢的例子是写

length xs == 0

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

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

花费恒定的时间。

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

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

【讨论】:

  • (当然,你应该使用null xs来检查xs是否为空。)
  • @Kenny 如果我的每个写length xs == 0而不是null xs的学生都有一美元,我就足够让我喝咖啡一年了:-)
【解决方案2】:

我根据使代码看起来更漂亮和更易于阅读的原因进行选择。正如@Don 指出的那样,许多这些不同的形式都被编译为case。由于可用的语法糖,它们看起来不同。这种糖不是给编译器的,它是给人类的。因此,请根据您认为其他人喜欢阅读的内容以及您认为可以阅读的内容来决定。

【讨论】:

    【解决方案3】:

    这三个选项中没有一个做完全相同的事情,也不能在所有情况下使用。

    模式匹配检查哪个构造函数用于创建给定值并绑定变量。 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 this 看起来像这样(注意 Nothing 的重复):

    maybeSqrt (Just x) = if x >= 0 then sqrt x
                         else Nothing
    maybeSqrt _ = Nothing
    

    最后 if 可以在没有模式匹配的情况下使用。如果您实际上并未对给定值使用模式匹配,那么引入 case x of ... 只是为了使用模式保护,这没有什么意义,并且比仅使用 if 更不可读和简洁。

    【讨论】:

      【解决方案4】:

      嗯,我不知道从“控制语句”的角度思考是否是在 Haskell 中处理它的最佳方式。也就是说,最终归结为模式匹配。例如,if ... then ... else 等布尔条件可以根据 Bool 的构造函数的模式匹配来定义。

      最“原始”的形式可能是case 语句——函数定义的模式匹配只是包含一个大的case 表达式的单个函数定义的语法糖。

      就您应该使用的内容而言,请选择在概念上最有意义的内容。当您需要分解代数数据类型时,模式匹配最合适; if 块适用于当您需要某个谓词的简单是/否结果时。当您需要混合使用数据类型解构和布尔谓词时,通常使用守卫。

      要记住的最重要一点是模式匹配是分解代数数据类型的唯一方法。布尔谓词可以很容易地使用replaced with higher-order functions,但在数据构造函数中提取值需要模式匹配。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-01-23
        • 2012-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多