我們需要看看enumFromTo
爲整數的情況下,與去年:
last [] = errorEmptyList "last"
last (x:xs) = last' x xs
where last' y [] = y
last' _ (y:ys) = last' y ys
它在GHC定義。枚舉爲:
enumFrom x = enumDeltaInteger x 1
enumFromThen x y = enumDeltaInteger x (y-x)
enumFromTo x lim = enumDeltaToInteger x 1 lim
其中
enumDeltaInteger :: Integer -> Integer -> [Integer]
enumDeltaInteger x d = x `seq` (x : enumDeltaInteger (x+d) d)
-- strict accumulator, so
-- head (drop 1000000 [1 .. ]
-- works
和
enumDeltaToInteger :: Integer -> Integer -> Integer -> [Integer]
enumDeltaToInteger x delta lim
| delta >= 0 = up_list x delta lim
| otherwise = dn_list x delta lim
up_list :: Integer -> Integer -> Integer -> [Integer]
up_list x0 delta lim = go (x0 :: Integer)
where
go x | x > lim = []
| otherwise = x : go (x+delta)
last
是完全懶,符合市場預期。
對於整數枚舉類,我們有一個嚴格的累加器(明確)爲enumFrom
。在有界的情況下(例如[1..n]
),它調用enumDeltaToInteger
,然後調用up_list
,它使用工作人員展開列表,直到達到其限制。
但up_list
嚴格x
在go
幫手(請參閱與lim
比較)。
當在GHCi中運行時,沒有一個優化,在返回()
之前產生對enumFromTo的天真調用。
let
it_ax6 ::()
it_ax6 =
case last
@ GHC.Integer.Type.Integer
(GHC.Enum.enumFromTo
@ GHC.Integer.Type.Integer
GHC.Num.$fEnumInteger
(GHC.Integer.smallInteger 1)
(GHC.Real.^
@ GHC.Integer.Type.Integer
@ GHC.Integer.Type.Integer
GHC.Num.$fNumInteger
GHC.Real.$fIntegralInteger
(GHC.Integer.smallInteger 10)
(GHC.Integer.smallInteger 7)))
of _ -> GHC.Unit.()
in
GHC.Base.thenIO
@()
@ [()]
(System.IO.print @() GHC.Show.$fShow() it_ax6)
(GHC.Base.returnIO
@ [()] (GHC.Types.: @() it_ax6 (GHC.Types.[] @())))
那麼,爲什麼我們保留在seq
情況列表中,而不是在常規情況下?常規情況在常量空間中很好地運行,依靠enumFromTo
對Integer
和last
的懶惰。該GHCI核心爲這種情況下的樣子:
let {
it_aKj :: GHC.Integer.Type.Integer
[LclId,
Unf=Unf{Src=<vanilla>, TopLvl=False, Arity=0, Value=False,
ConLike=False, Cheap=False, Expandable=False,
Guidance=IF_ARGS [] 170 0}]
it_aKj =
GHC.List.last
@ GHC.Integer.Type.Integer
(GHC.Enum.enumFromTo
@ GHC.Integer.Type.Integer
GHC.Num.$fEnumInteger
(GHC.Integer.smallInteger 1)
(GHC.Real.^
@ GHC.Integer.Type.Integer
@ GHC.Integer.Type.Integer
GHC.Num.$fNumInteger
GHC.Real.$fIntegralInteger
(GHC.Integer.smallInteger 10)
(GHC.Integer.smallInteger 7))) } in
GHC.Base.thenIO
@()
@ [()]
(System.IO.print
@ GHC.Integer.Type.Integer GHC.Num.$fShowInteger it_aKj)
(GHC.Base.returnIO
@ [()]
(GHC.Types.:
@()
(it_aKj
`cast` (UnsafeCo GHC.Integer.Type.Integer()
:: GHC.Integer.Type.Integer ~()))
(GHC.Types.[] @())))
因此,這些都是幾乎相同的,所不同的是:
在
seq
版本
- ,
last (enumFromTo ..)
被迫一case
內。
- 在普通版中,它是一個懶惰的
let
。
- 在
seq
版本,該值被計算然後被丟棄,從而產生()
- 沒有着眼於結果在常規情況下
- ,則檢查和示出。
是什麼奇怪的是,沒有什麼神奇的約:
let x = case last (enumFromTo 1 n) of _ ->()
,使得它保留值。
正如我們看到的,up_list
實現是在其蓄能器嚴格的(因爲它比較對lim
,並且該列表懶洋洋地展開 - 這樣last
應該能夠使用它在不斷的空間)。用手書寫表達來證實這一點。
在做ghci的執行堆模式顯示整個列表被保留:
至少告訴我們,這不是的thunk鏈,而是整個列表被嚴格建造並堅守下去,直到被丟棄。
所以神祕的是:什麼是持有的ghci中的last
列表參數,而不是在ghc中?
我懷疑現在有些ghci的內部(或細微)細節 - 我認爲這值得ghci票。
難道它與[擴展的默認規則](http://www.haskell.org/ghc/docs/latest/html/users_guide/interactive-evaluation.html#extended-default-rules)有關嗎? –
@ChrisWong好吧,如果類型違約是罪魁禍首,修正'seq(last [1 :: Int ..10^8])()'等類型應該修復... – hvr
這是值得一票! – is7s