簡體   English   中英

索引映射/過濾的無限列表是否將映射/過濾器 function 應用於索引之前的所有元素?

[英]Does indexing a mapped/filtered infinite list apply the map/filter function to all the elements before the index?

If I define an infinite list and call map over it with a function, I can take some number of elements from the beginning and Haskell will generate and apply the function only to these elements due to lazy evaluation

-- This will generate and process 10 elements

doubledNumbers = map (*2) [0..]
take 10 doubledNumbers

如果我改為索引這個無限映射

doubledNumbers !! 9

將 Haskell 生成該點之前的列表,將映射 function 應用於所有元素並將索引 9 處的元素返回; 或者它會生成列表,將映射 function 應用到索引 9 處的元素,然后返回。 換句話說,當我調用doubledNumbers !! 9 doubledNumbers !! 9

  1. 生成列表[0,1,2,3,4,5,6,7,8,9],然后列表[0,2,4,6,8,10,12,14,16,18] ,然后從該列表中取出 18
  2. 生成列表 [0,1,2,3,4,5,6,7,8,9],然后從該列表中取出 9 並提供給 (*2),然后 (*2) 返回 18

第二種解釋是正確的(嗯,至少在道德上:見下文以獲得更好的解釋):

  1. 生成列表[0,1,2,3,4,5,6,7,8,9] ,然后從該列表中取出 9 並提供給(*2) ,然后(*2)返回 18

通過以下實驗,您可以觀察到 function(在您的情況下為(*2) )不適用於其他元素。 首先,定義一個 function,它將在除 9 之外的每個數字上崩潰:

f 9 = 100 ; f _ = error "urk!"

然后在 GHCi 中嘗試你的代碼,適應新的 function

> doubledNumbers = map f [0..]
> doubledNumbers !! 9
100

如果 GHCi 將f應用於任何其他數字,您將觀察到崩潰。 這是一種通過實驗區分惰性評估和急切評估的方法。


更迂腐的是,在 GHCi 中輸入代碼

> doubledNumbers = map (*2) [0..]
> doubledNumbers !! 9

將首先生成一個類似於

(*2) 0 : (*2) 1 : .... : (*2) 9 : map (*2) [10..]

其中表達式尚未計算。 之后,GHCi 將提取第 9 個,對其進行評估和打印。

兩種解釋都不正確。 實際上,可以認為生成的列表看起來像這樣:

[_,_,_,_,_,_,_,_,_,_...

換句話說,列表的脊椎或形狀被強制向上通過第 9 個元素(僅此而已),但列表中的實際值不是。 然后,當您請求映射列表的第 9 個元素時,除非必要,否則 GHC 在該元素上調用 function 而不對其進行評估 您可以通過以下實驗觀察到這一點:

errorList = repeat (error "fail!")

trueList = map (const True) errorList

val = trueList !! 9

如果列表實際上是首先生成的,那么val會因失敗而fail! 一旦我們嘗試打印它。 但是, val正確地是True

使用跟蹤工具:

通常可以使用 Haskell 運行時庫中包含的一個小而方便的跟蹤工具來回答此類問題。

所以有一個trace function,這樣計算表達式trace debugMsg expr將返回expr的值,並且作為副作用,將debugMsg的值打印到標准錯誤通道。

是的,副作用在 Haskell 中通常不可用,但trace function 享有特殊特權:-)

因此,您可以將(*2) function 替換為等效的:

import qualified  Debug.Trace  as  DT

doubler :: Int -> Int
doubler x = let  debugMsg = "Doubling " ++ (show x) ++ " ... "
            in   DT.trace  debugMsg  (2*x)

然后你可以在ghci解釋器下測試各種假設:

$ ghci
 GHCi, version 8.8.4: https://www.haskell.org/ghc/  :? for help
 λ> 
 λ> :load q65861408.hs
 [1 of 1] Compiling Main             ( q65861408.hs, interpreted )
 Ok, one module loaded.
 λ> 
 λ> :type doubler
 doubler :: Int -> Int
 λ> 
 λ> doubler 5
 Doubling 5 ... 
 10
 λ> 
 λ> doubledNumbers = map doubler [0..]
 λ> 
 λ> doubledNumbers !! 9
 Doubling 9 ... 
 18
 λ> 

因此,正如其他答案中所提到的,加倍 function 似乎僅針對輸入值 9 調用。

這是因為語言懶惰:用戶沒有要求查看其他加倍的值,因此不需要首先計算它們。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM