我正在阅读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子句确定pre
和suf
是什么
3.了解pre
和suf
,然后处理案例表达式?
或者构造案例表达式的顺序是什么?
答案 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
开头的表达式中。
为了能够做到这一点,它首先必须评估pre
和suf
,它们都由表达式break isLineTerminator cs
定义。因此,为了评估pre
和suf
,整体函数会评估break isLineTerminator cs
。
它现在知道pre
和suf
是什么,因此它可以评估case
表达式的其余部分,并使用pre
来计算结果。
请注意,splitLines
是递归的,因此在splitLines
点击基本案例[]
之前,这种情况会继续发生。
回想一下Haskell被懒惰地评估,所以所有这些只是在需要结果的时候才发生。