take 2$[1..]如何在haskell工作?

take 2$[1..]如何在haskell工作?,haskell,Haskell,我们知道,$操作符绑定最松散的,并且也关联到右边,这意味着应该首先计算[1..,因此,它不应该运行到无限循环中吗?为什么它会停下来呢?哈斯克尔很懒,($)不会改变这一点。($)操作符一点也不神奇,它是一个完全普通的Haskell函数†: 由于Haskell是惰性的,参数在传递给函数之前不会进行计算,($)也不例外。因此,take 2$[1..]等同于(take 2)[1..],这当然等同于take 2[1..]。没有进行额外的评估 现在,事实证明,($)的一个严格版本被调用,它在应用函数之前对其

我们知道,$操作符绑定最松散的,并且也关联到右边,这意味着应该首先计算[1..,因此,它不应该运行到无限循环中吗?为什么它会停下来呢?

哈斯克尔很懒,
($)
不会改变这一点。
($)
操作符一点也不神奇,它是一个完全普通的Haskell函数†:

由于Haskell是惰性的,参数在传递给函数之前不会进行计算,
($)
也不例外。因此,
take 2$[1..]
等同于
(take 2)[1..]
,这当然等同于
take 2[1..]
。没有进行额外的评估

现在,事实证明,
($)
的一个严格版本被调用,它在应用函数之前对其参数进行求值。它也可以定义为普通的Haskell函数,但它必须使用神奇的
seq
函数作为其定义的一部分:

($!) :: (a -> b) -> a -> b
f $! x = x `seq` f x
然而,即使
也要2美元![1..]
将产生
[1,2]
,而不是分歧。为什么?嗯,
$只将其参数计算为WHNF,而不是标准形式,并且WHNF可以被认为是“浅”的计算。它计算第一个cons对,但仅此而已。通过使用GHCi中的
:sprint
命令可以看到这一点:

ghci> let xs = [1..] :: [Int]
ghci> xs `seq` ()
()
ghci> :sprint xs
xs = 1 : _
要递归地强制一个值,您需要使用,顾名思义,它对一个值进行深入的求值。它提供了一个更“强大”的
($)
,名为,类似于
($!)
,但使用而不是
seq
。因此,
拿2美元!![1..]
事实上会产生分歧



†这在GHC中并不完全正确,因为。但是,所有这些都与此无关,更简单的定义同样适用。

由于函数应用程序具有最高优先级,您的表达式被解析为
(取2)$[1..]
。这意味着您首先得到一个函数
(取2)
,然后将其应用于参数
[1..]

然而,这一切都无关紧要,因为Haskell是一种懒惰的语言。你可以反过来写,得到完全相同的结果:

> [1..] & take 2
[1.2]
(&)
($)
运算符的反向版本


即使
[1..]
在这里是第一位的,在需要它的内容之前也不会对它进行评估。

为了补充其他答案,让我补充一点,您混淆了评估顺序(或评估策略)和优先级。这是一种常见的误解

举例说明,考虑表达式

f 0 * g 0 + h 0
优先级告诉我们乘法必须在加法之前进行。然而,这并不意味着必须在
h0
之前对
f0
g0
进行评估!编译器可以选择先计算
h0
,然后是
g0
,然后是
f0
,最后是乘法,然后是加法

这不仅适用于Haskell,甚至适用于命令式语言,如C,它不指定求值顺序,并且允许函数产生副作用


除此之外,您还必须了解,在Haskell中“评估”某个内容大致意味着评估它,直到它的第一个构造函数出现(WHNF)。因此,计算
[1..]
大约会导致
1:[2..]
必须计算尾部的位置。如果计算
[1..]
会导致无限循环,那么在表达式中根本没有办法使用
[1..]
:只能选择放弃它而不计算它,或者永远循环。

这里的
$
是一条红鲱鱼
take 2$[1..]
take 2[1..]
完全相同。
$
只影响what的参数;它对评估事物的时间没有任何影响

(例如:

print 2 + 2         ==> (print 2) + 2 {- Doesn't work. -}
print $ 2 + 2       ==> print (2 + 2) {- Works. -}
美元会影响
print
+
的参数,还是反过来。美元本身不会评估任何东西。)

这里的“最顶端”函数是
take
,因此我们首先对其进行评估。
take
的定义可以这样写:

take 0 xs = xs
take n xs =
  case xs of
    x : xs' -> x : take (n-1) xs'
    []      -> []
假设长度不为零,首先要做的事情是……
情况xs,这意味着必须对
xs
(在本例中为
[1..]
)进行评估,以确定它是
还是
[]
。这样做,我们发现(在恒定时间内)xs=1:[2..]
,因此第一种情况下的备选方案适用

你可以这样写出来

take 2 [1..]
take 2 (1 : [2..])
1 : take (2-1) [2..]
1 : take 1 [2..]
1 : take 1 (2 : [3..])
1 : 2 : take (1-1) [3..]
1 : 2 : take 0 [3..]
1 : 2 : []
(我仍然认为很遗憾,没有人能想出一个工具来自动生成这样的跟踪……它可能需要几个人,并且可能非常适合调试……)

take 2 [1..]
take 2 (1 : [2..])
1 : take (2-1) [2..]
1 : take 1 [2..]
1 : take 1 (2 : [3..])
1 : 2 : take (1-1) [3..]
1 : 2 : take 0 [3..]
1 : 2 : []