当前位置: 首页 > 知识库问答 >
问题:

Haskell中case表达式的求值

贲高寒
2023-03-14

我目前正在学习Haskell,突然遇到了这个表达式。

statement
   case 1 ´div´ 0 of _ -> 42

我的直觉是,由于用0除法,这将导致运行时错误,但从测试来看,情况并非如此。

我的结论是,这一定是由于Haskell内部的懒惰评估。因为_匹配任何东西,所以它不会检查它与什么比较?

所以,如果有人能告诉我这个评估是否正确,如果不正确,为什么。另外,请详细说明在不实际查看表达式的情况下,案例行可以匹配的前提。

共有2个答案

冯野
2023-03-14

我的结论是,这一定是由于Haskell内部的懒惰评估。因为_匹配任何东西,所以它不会检查它与什么比较?

是的,没错!

在模式匹配过程中,仅在需要匹配模式中的构造函数(和数字/字符文本)时执行求值。如果模式是变量或通配符,则无需计算。

case undefined of _ -> ()  -- OK
case undefined of True -> () -- crash
case undefined of (_,_) -> () -- crash
case (undefined, 4) of (_,x) -> x+1 -- OK

此外,模式匹配从左到右、从上到下进行:

case (4, undefined) of
   (3, True) -> 1
   (4, _)    -> 2

计算结果为<代码>2--我们首先将<代码>4与<代码>3进行匹配,但在尝试将<代码>未定义的与<代码>真的进行匹配之前失败。这样我们就避免了崩溃,第二种模式成功了。

(详尽地说:这实际上有一个例外。如果您对一个新类型的构造函数进行模式匹配,则不会进行任何计算。您可以暂时忽略这种情况,因为您正在学习——这并不常见。)

刘修能
2023-03-14

你的分析是准确的,但这实际上是非常繁琐的。它不复杂,但它需要仔细考虑非常精确的问题。

大小写表达式具有一般形式

case exp0 of {pat1 -> exp1 ; ...}

计算case表达式时,将对exp0进行足够的计算,以确定它是否与pat1匹配。如果是,case表达式将使用为其创建的任何附加绑定对其求值。如果不匹配,则会切换到下一个模式或后卫并重复。(如果没有匹配项,则计算为异常。)

值得注意的是,您的第一个替代方案匹配所有内容,而无需任何评估。因此不执行div 1 0的评估。该模式不绑定任何内容,然后case表达式的评估结果为42。

这与你做的不同:

case 1 `div` 0 of
    0 -> 42
    _ -> 42

在这种情况下,必须计算div表达式,以查看它是否等于0。这将导致一个异常,该异常将成为整个case表达式的结果。即使两个分支具有相同的值,这也是事实。模式的内容对于确定与严格相关的行为非常重要。

到目前为止,GHC Haskell编译器是最常见的编译器,它有一个称为“core”的中间表示形式,看起来与Haskell相似,但类型类和大多数语法糖都已完全去除。Core的语义也与Haskell略有不同,尤其是在case构造方面。在core中,case总是导致对顶级构造函数的评估。使用嵌套重写模式,以匹配原始Haskellhtml" target="_blank">代码的模式语义。

本节只是一个旁白,但是如果你研究优化Haskell代码,你会遇到core,知道有区别是很有用的。

 类似资料:
  • 我正在练习计算let表达式的问题,但我不理解这个表达式的输出。 下面是一个表达: 输出应该是[1,2,4,8]。有人能一步一步地解释为什么这是输出吗

  • 问题内容: 我正在尝试创建一个switch语句,但似乎无法使用被求值的表达式(而不是设置的字符串/整数)。我可以轻松地使用if语句来执行此操作,但希望大小写应该更快。 我正在尝试以下 我缺少明显的东西吗?Google在这种情况下并不友好。 任何帮助/指针表示赞赏 问题答案: 你总是可以做 它之所以有效是因为它是一个常量,因此将执行第一个case语句下带有计算结果为true的表达式的代码。 我猜这有

  • 本文向大家介绍Ruby中case表达式详解,包括了Ruby中case表达式详解的使用技巧和注意事项,需要的朋友参考一下 Ruby的case表达式有两种形式: 第一种形式接近于一组连续的if语句:它让你列出一组条件,并执行第一个为真的条件表达式所对应的语句。 第二种形式,在case语句的顶部指定一个目标,而每个when从句列出一个或者多个比较条件 和if一样,case返回执行的最后一个表达式的值;而

  • 问题内容: 我有以下代码片段: 我知道在C ++中,您被教导不要依赖子表达式的求值顺序,因为它不能保证完全是任何顺序。因此,此代码将是错误的,并且不能保证条件中表达式所产生的布尔值是真实的(例如,可以在第一次等式测试中对y进行递增运算)。由于我是在Java认证书中阅读此代码的,因此我认为Java并非如此。.我的意思是,我是否保证Java的评估顺序始终是从左到右?因此,以上表达式应始终为true。

  • 我有以下代码片段: 我知道在C++中,你被教导不要依赖子表达式的求值顺序,因为它不能保证是任何顺序。因此这段代码是错误的,并且条件中的表达式所产生的布尔值不能保证为真(例如,在第一个相等性测试中计算y之前,可以增加y)。因为我是在Java认证书上读到这段代码的,所以我假设Java不是这样的。我的意思是,我能保证Java的计算顺序总是从左到右吗?所以上面的表达式应该总是产生true。

  • 问题内容: 我的转换案例声明昨天运行良好。但是,当我今天早些时候运行代码时,eclipse给我一个错误,强调了红色的case语句,并说:case表达式必须是常量表达式,它是常量,我不知道发生了什么。这是我的代码如下: 所有R.id.int都用红色下划线。 问题答案: 在常规的Android项目中,资源R类中的常量声明如下: 但是,从ADT 14开始,在图书馆项目中,它们将这样声明: 换句话说,常数