Haskell,无限列表上的foldr

Haskell,无限列表上的foldr,haskell,type-inference,fold,Haskell,Type Inference,Fold,我在读李安的时候有个问题 以下是我对无限列表中foldr的看法: foldr (:) [] [1..] = 1:2:...:**∞:[]** 我认为GHCi在评估∞:[] 但GHCi确实知道 所以我认为它可以识别foldr(:)[[无限列表]=[无限列表本身] Prelude> [1..10] == (take 10 $ foldr (:) [] [1..]) True Prelude> [1..] == (foldr (:) [] [1..]) Interrupted. 但事实

我在读李安的时候有个问题

以下是我对无限列表中foldr的看法:

foldr (:) [] [1..] = 1:2:...:**∞:[]**
我认为GHCi在评估∞:[]

但GHCi确实知道

所以我认为它可以识别foldr(:)[[无限列表]=[无限列表本身]

Prelude> [1..10] == (take 10 $ foldr (:) [] [1..])
True
Prelude> [1..] == (foldr (:) [] [1..])
Interrupted.
但事实并非如此

我想知道,在评估之前,当GHCi认识到它是[1..]时,实际会发生什么∞:[…]

只是在评估之前输入推断

在评估
∞:[]

GHCi不承认它是
[1…]
,这只是延迟评估的结果

foldr
实现为:

foldr _ z [] = z
foldr f z (x:xs) = f x (foldr f z xs)
如果您编写类似于
foldr(:)[[1..]
的代码,那么Haskell不会(直接)计算它,它只存储您想要计算的内容

现在假设您想
打印(取3(foldr(:)[[1..])
该列表,然后Haskell被迫对其进行评估,它将通过计算:

take 3 (foldr (:) [] [1..])
-> take 3 ((:) 1 (foldr (:) [] [2..]))
-> (:) 1 (take 2 (foldr (:) [] [2..]))
-> (:) 1 (take 2 ((:) 2 (foldr (:) [] [3..]))
-> (:) 1 ((:) 2 (take 1 (foldr (:) [] [3..])))
-> (:) 1 ((:) 2 (take 1 ((:) 3 (foldr (:) [] [4..]))))
-> (:) 1 ((:) 2 ((:) 3 (take 0 (foldr (:) [] [4..]))))
-> (:) 1 ((:) 2 ((:) 3 [])
因此它派生出
[1,2,3]
,由于哈斯凯尔的懒惰,它对什么是
foldr(:)[[4..]
不感兴趣。即使该列表最终会停止,它也不会被评估

如果计算类似于
[1..]=foldr(:)[[1..]
,则Haskell将检查列表相等性,列表相等性定义为:

[] == [] = True
(x:xs) == (y:ys) = x == y && xs == ys
[] == (_:_) = False
(_:_) == [] = False
因此Haskell被迫展开右侧的
foldr
列表,但它将继续这样做,直到找到不相等的项,或者列表中的一个到达末尾。但是,由于每次元素相等,并且两个列表永远不会结束,因此它将永远不会结束,si it将对其进行如下计算:

   (==) [1..] (foldr (:) [] [1..])
-> (==) ((:) 1 [2..])  ((:) 1 (foldr (:) [] [2..]))
它看到两者相等,因此递归调用:

-> (==) ((:) 1 [2..])  ((:) 1 (foldr (:) [] [2..]))
-> (==) [2..] foldr (:) [] [2..])
-> (==) ((:) 2 [3..])  ((:) 2 (foldr (:) [] [3..]))
-> (==) [3..] foldr (:) [] [3..])
-> ...
但正如你所看到的,它永远不会停止评估。Haskell不知道
foldr(:)[[1..]
等于
[1..]
,它的目的是计算它,因为相等迫使它计算整个列表,它将陷入无限循环

是的,可以在编译器中添加某种模式,这样
foldr(:)[]x
x
替换,因此将来Haskell编译器可能会为这些模式返回
True
,但这并不能从根本上解决问题,因为如果Haskell可以为任何类型的函数派生这样的东西(这里是
(:)
),那么它将解决一个不可判定的问题,因此是不可能的)

在评估
∞:[]

GHCi不承认它是
[1…]
,这只是延迟评估的结果

foldr
实现为:

foldr _ z [] = z
foldr f z (x:xs) = f x (foldr f z xs)
如果您编写类似于
foldr(:)[[1..]
的代码,那么Haskell不会(直接)计算它,它只存储您想要计算的内容

现在假设您想
打印(取3(foldr(:)[[1..])
该列表,然后Haskell被迫对其进行评估,它将通过计算:

take 3 (foldr (:) [] [1..])
-> take 3 ((:) 1 (foldr (:) [] [2..]))
-> (:) 1 (take 2 (foldr (:) [] [2..]))
-> (:) 1 (take 2 ((:) 2 (foldr (:) [] [3..]))
-> (:) 1 ((:) 2 (take 1 (foldr (:) [] [3..])))
-> (:) 1 ((:) 2 (take 1 ((:) 3 (foldr (:) [] [4..]))))
-> (:) 1 ((:) 2 ((:) 3 (take 0 (foldr (:) [] [4..]))))
-> (:) 1 ((:) 2 ((:) 3 [])
因此它派生出
[1,2,3]
,由于哈斯凯尔的懒惰,它对什么是
foldr(:)[[4..]
不感兴趣。即使该列表最终会停止,它也不会被评估

如果计算类似于
[1..]=foldr(:)[[1..]
,则Haskell将检查列表相等性,列表相等性定义为:

[] == [] = True
(x:xs) == (y:ys) = x == y && xs == ys
[] == (_:_) = False
(_:_) == [] = False
因此Haskell被迫展开右侧的
foldr
列表,但它将继续这样做,直到找到不相等的项,或者列表中的一个到达末尾。但是,由于每次元素相等,并且两个列表永远不会结束,因此它将永远不会结束,si it将对其进行如下计算:

   (==) [1..] (foldr (:) [] [1..])
-> (==) ((:) 1 [2..])  ((:) 1 (foldr (:) [] [2..]))
它看到两者相等,因此递归调用:

-> (==) ((:) 1 [2..])  ((:) 1 (foldr (:) [] [2..]))
-> (==) [2..] foldr (:) [] [2..])
-> (==) ((:) 2 [3..])  ((:) 2 (foldr (:) [] [3..]))
-> (==) [3..] foldr (:) [] [3..])
-> ...
但正如你所看到的,它永远不会停止评估。Haskell不知道
foldr(:)[[1..]
等于
[1..]
,它的目的是计算它,因为相等迫使它计算整个列表,它将陷入无限循环

是的,可以在编译器中添加某种模式,这样
foldr(:)[]x
x
替换,因此将来Haskell编译器可能会为这些模式返回
True
,但这并不能从根本上解决问题,因为如果Haskell可以为任何类型的函数导出这样的东西(这里是
(:)
,那么它将解决一个不可判定的问题,因此这是不可能的)。

Ghc(至少在理论上)不知道有限列表和无限列表之间的区别。它可以通过计算列表的长度来判断列表是有限的。如果你试图找到无限列表的长度,你会有一个糟糕的时间,因为你的程序永远不会终止

这个问题实际上是关于惰性评估的。在像C或python这样的严格语言中,您需要在每一步都知道某些东西的全部价值。如果您想添加列表中的元素,在开始之前,您已经需要知道列表中有哪些内容以及有多少内容

Haskell中的所有数据具有以下形式:

  • 一个基本的完全已知的东西,比如int(不一定是整数)
  • 数据类型构造函数及其参数,例如
    True
    Left 7
    (,)5'f'
    (与
    (5,'f')
    相同)或
    (:)3[]
  • 但在Haskell中,值有两种“形状”

    • 已知并经过充分评估(如C或python)
    • 尚未计算-一个函数“thunk”,当您调用它时,它将以一种更具计算性的方式返回值
    在Haskell中有一个称为弱头范式的概念,其中:

    • 任何基本的东西都会被完全评估
    • 对于具有构造函数的任何内容,构造函数是已知的,并且
      [1..] == [1..]
      (1 == 1) && ([2..] == [2..])
      True && ([2..] == [2..])
      [2..] == [2..]
      ... and so on
      
      True && x = x
      False && _ = False