Haskell 带异常的列表连接

Haskell 带异常的列表连接,haskell,exception,undefined,lazy-evaluation,Haskell,Exception,Undefined,Lazy Evaluation,我有以下表达: let x = [1] ++ undefined ++ [3] 我有以下例外情况: [1*** Exception: Prelude.undefined CallStack (from HasCallStack): error, called at libraries\base\GHC\Err.hs:79:14 in base:GHC.Err undefined, called at <interactive>:39:16 in interactive:Gh

我有以下表达:

let x = [1] ++ undefined ++ [3]
我有以下例外情况:

[1*** Exception: Prelude.undefined
CallStack (from HasCallStack):
  error, called at libraries\base\GHC\Err.hs:79:14 in base:GHC.Err
  undefined, called at <interactive>:39:16 in interactive:Ghci20
在这里,我得到的结果
3
不是一个例外,因为在调用之前,不会对值求值

为什么前面的表达式会引发异常

如果你写:

let x = [1] ++ undefined ++ [3]
然后不计算
x
。它存储为必须求值的表达式。但是你可以查询
x
。换句话说,您希望
显示x
。现在为了显示列表,必须计算整个列表(及其元素)

现在concat操作符
(++)::[a]->[a]->[a]
被定义为:

(++) :: [a] -> [a] -> [a]
(++) (x:xs) ys = x : xs ++ ys
(++) [] ys = ys
例如
[1,2]+[3,4]
:将导致:

   [1,2] ++ [3,4]
-> 1 : ([2] ++ [3,4])
-> 1 : 2 : ([] ++ [3,4])
-> 1 : 2 : [3,4]
或在使用未定义的
时:

   [1,2] ++ undefined
-> 1 : ([2] ++ undefined)
-> 1 : 2 : ([] ++ undefined)
-> 1 : 2 : undefined
请注意,这是惰性地完成的:只有在需要更多元素时,才需要进一步压缩。此外,
(++)
不关心单个元素:如果元素包含计算开销大的操作,则该操作将被推迟到需要计算时

因此,只要第一个列表仍然可以发射元素,我们就不关心第二个列表。当到达第一个列表的末尾时,我们只返回第二个列表

我们看到这部分工作:解释器打印
[1***异常:Prelude.undefined
,因此在对
(++)
操作符求值时,
[1]
第一个元素被发出,求值并打印。但随后我们到达
undefined
,Haskell无法处理它以打印它(其元素)并终止

另一方面,如果您编写
让x=[undefined,undefined,undefined]
并调用
length x
,那么列表中的元素不会被计算
length
的定义如下:

length :: [a] -> Int
length [] = 0
length (_:xs) = 1 + length xs
请注意函数中的下划线(
length
不关心单个元素。它只需遍历列表,直到找到空列表,然后返回该计数,因此
3

如果另一方面你会写

length $ [1] ++ undefined ++ [2,3]
你会遇到同样的问题:

Prelude> length $ [1] ++ undefined ++ [2,3]
*** Exception: Prelude.undefined

因为为了浏览这个列表,我们必须将
[1]
未定义的

联系起来,我无法想象
++
是如何工作的。请您展开一下,例如
[1,2,3]++[3,4,5]
注意这两个表达式是不同的。为了简洁起见,假设
u=undefined
[u,u,u]
[u]+[u]+[u]
,而不是
[u]++u++[u]
。在第一个示例中,您有一个未定义的列表,而在第二个示例中,您只有未定义的元素,因此
长度
可以正常工作。
Prelude> length $ [1] ++ undefined ++ [2,3]
*** Exception: Prelude.undefined