我正在研究haskell book,我明白:sprint x
用于打印x的元素已被评估,而元素则没有(未被表达的元素由'_')。
书中提供的一个例子,
Prelude> let blah = enumFromTo 'a' 'z'
Prelude> :sprint blah
blah = _
Prelude> take 1 blah
"a"
Prelude> :sprint blah
blah = 'a' : _
为了测试不同的输入,我在GHCi中做了这个: -
prelude> let b = [1,2,3,4,5]
prelude> :sprint b
b = _
prelude> take 1 b
[1]
prelude> :sprint b
b = _
最后一个命令中:sprint b
的输出不应该是b = 1 : _
,因为我们在使用命令take 1 b
时只评估单个列表项和cons运算符?但它显示了上面的输出。这是怎么发生的?输出不应该类似于String类型的输出吗?
编辑:我一直在尝试更多并得到这个结果: -
prelude> let b = [1..10] :: [Int]
prelude> :sprint b
b = _
prelude> take 3 b
[1,2,3]
prelude> :sprint b
b = 1 : 2 : 3 : _
好吧,我最初的猜测是,这是因为我正在构建两个列表的方式?一个是使用范围而另一个是通过明确地声明它的元素(它依次通过在元素上递归使用cons':'构造函数来创建列表)
答案 0 :(得分:6)
:sprint
的行为可能有点棘手。在这种情况下,请查看x
的类型:
> :t x
x :: Num t => [t]
因为它是多态的,所产生的实际值取决于您需要的Num
的特定实例。因此,x
的行为更像是一个函数,当它可以确定您希望元素具有什么类型时,它将生成列表[1,2,3,4,5]
。
现在你可能会想,“好吧,我会制作一个非多态的列表”,所以你试试这个:
> let x = [1,2,3,4,5 :: Int]
> :t x
x :: [Int]
> :sprint x
x = [1,2,3,4,5]
到底是什么?如果你考虑一下,这是有道理的。我们已经明确告诉ghci列表是什么。它不是可以取消评估它,然后再重新评估它。 (无论如何这都是浪费。)但是让我们看看当我们尝试将函数映射到x时会发生什么:
> let y = map (+1) x
> :sprint y
y = _
> take 1 y
[2]
> :sprint y
y = 2 : _
正如所料!
希望有所帮助。懒惰的评估是关于Haskell的一个棘手的问题(当它变得很重要时)。