[英]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.