簡體   English   中英

Haskell monad:IO [Double] 到 [IO Double]

[英]Haskell monad: IO [Double] to [IO Double]

考慮以下應該打印出隨機數的代碼:

import System.Random.Mersenne

main =
 do g <- (newMTGen Nothing)
    xs <- (randoms g) :: IO [Double]
    mapM_ print xs  

運行時,出現分段錯誤。 這並不奇怪,因為 function 'randoms' 產生了一個無限列表。 假設我只想打印出 xs 的前十個值。 我怎么能那樣做? xs 的類型為 IO [Double],我想我想要一個 [IO Double] 類型的變量。 存在哪些運算符可以在兩者之間進行轉換。

如果您遇到分段錯誤,並且您沒有使用 FFI 或名稱中帶有unsafe的任何函數,那么這並不奇怪,在任何情況下,這都意味着 GHC 存在錯誤。 或者您正在使用的圖書館正在做一些不安全的事情。

使用mapM_ print打印出Double的無限列表非常好; 該列表將逐步處理,程序應以恆定的 memory 使用率運行。 我懷疑您正在使用的System.Random.Mersenne模塊中存在錯誤,或者它所基於的 C 庫存在錯誤,或者您的計算機存在問題(例如 RAM 故障)。 1請注意, newMTGen附帶此警告:

由於當前的 SFMT 庫非常不純,目前每個程序只允許使用一個生成器。 嘗試重新初始化它將會失敗。

您最好改用提供的全局MTGen

也就是說,您不能以這種方式將IO [Double]轉換為[IO Double] 如果不執行IO操作,就無法知道結果列表的長度,這是不可能的,因為您有一個純結果(盡管恰好包含IO操作)。 對於無限列表,您可以這樣寫:

desequence :: IO [a] -> [IO a]
desequence = desequence' 0
  where
    desequence n m = fmap (!! n) m : desequence (n+1) m

但是每次執行這個列表中的動作時, IO [a]動作都會被再次執行; 它只會丟棄列表中的 rest。

randoms可以工作並返回隨機數的無限列表的原因是因為它使用帶有 unsafeInterleaveIO 的惰性unsafeInterleaveIO (請注意,盡管名稱中帶有“不安全”字樣,但這並不會導致段錯誤,因此還有其他事情正在發生。)

1其他不太可能的可能性包括 C 庫的錯誤編譯,或 GHC 中的錯誤。

假設我只想打印出 xs 的前十個值。 我怎么能那樣做?

只需使用take

main =
 do g <- (newMTGen Nothing)
    xs <- (randoms g) :: IO [Double]
    mapM_ print $ take 10 xs  

你寫了

xs 的類型為 IO [Double]

但實際上, randoms g的類型為IO [Double] ,但由於do表示法, xs的類型為[Double] ,您只需對其應用take 10即可。

您還可以使用liftM跳過綁定:

main =
  do g <- newMTGen Nothing
     ys <- liftM (take 10) $ randoms g :: IO [Double]
     mapM_ print ys

暫無
暫無

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

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