这个表达式是如何评估的?

时间:2018-04-02 05:55:12

标签: haskell

我正在阅读Real World Haskell,我无法理解评估break方法的结果何时。 如果它以pre:case suf of子句开头,则必须在let子句中获取break方法生成的元组,然后处理case表达式? 或者它是如何做到的?

splitLines [] = []
splitLines cs = 
  let (pre, suf) = break isLineTerminator cs
  in pre : case suf of
            ('\r':'\n':rest) -> splitLines rest 
            ('\r':rest)      -> splitLines rest
            ('\n':rest)      -> splitLines rest
            _            -> []

isLineTerminator c = c == '\r' || c == '\n'

是吗:

  • 1.Goes into pre:case suf of

  • 2.Goes to let子句确定presuf是什么

  • 3.了解presuf,然后处理案例表达式?

或者构造案例表达式的顺序是什么?

2 个答案:

答案 0 :(得分:7)

除非您指定如何使用splitLines的结果,否则无法说出它的评估方式。懒惰评估尽可能少。例如,如果您使用null测试结果,则永远不会调用break。如果您使用头部并使用它,则会调用break,但永远不会评估case。等等。

答案 1 :(得分:3)

原则上,所有Haskell函数都是使用单个表达式定义的(至少,如果忽略do语法)。与基于语句的语言(如C系列)相反,函数只是表达式。

但是,有时候,如果您可以在两个或更多子步骤中分解函数定义,它可以增强可读性。 Haskell的let...in语法使您能够做到这一点。

let允许您定义一个或多个名称,然后可以在in表达式中引用这些名称。您可以在let部分中定义任意数量的名称,但in部分仍必须是单个表达式。

另一种选择是使用where。您可以使用splitLines语法重写where函数:

splitLines [] = []
splitLines cs =
  pre : case suf of
          ('\r':'\n':rest) -> splitLines rest 
          ('\r':rest)      -> splitLines rest
          ('\n':rest)      -> splitLines rest
          _            -> []
  where (pre, suf) = break isLineTerminator cs

isLineTerminator c = c == '\r' || c == '\n'

在这两种情况下,splitLines 的表达式将 pre包含在以case suf of开头的表达式中。

为了能够做到这一点,它首先必须评估presuf,它们都由表达式break isLineTerminator cs定义。因此,为了评估presuf,整体函数会评估break isLineTerminator cs

它现在知道presuf是什么,因此它可以评估case表达式的其余部分,并使用pre来计算结果。

请注意,splitLines是递归的,因此在splitLines点击基本案例[]之前,这种情况会继续发生。

回想一下Haskell被懒惰地评估,所以所有这些只是在需要结果的时候才发生。