2012-04-04 10 views
9

Xét đoạn mã sau đó là nghĩa vụ để in ra số ngẫu nhiên:Haskell đơn nguyên: IO [đúp] tới [IO đúp]

import System.Random.Mersenne 

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

Khi chạy, tôi nhận được một lỗi lỗi segmentation. Điều đó là không ngạc nhiên, vì hàm 'randoms' tạo ra một danh sách vô hạn. Giả sử tôi muốn in ra chỉ mười giá trị đầu tiên của xs. Làm thế nào tôi có thể làm điều đó? xs có kiểu IO [Double], và tôi nghĩ rằng tôi muốn một biến kiểu [IO Double]. Những toán tử nào tồn tại để chuyển đổi giữa hai toán tử.

+0

Ngẫu nhiên, IO [đúp] -> [IO đúp] về cơ bản là kiểu chữ ký ngược của 'chuỗi'. – Gautam

+3

Nó không segfault ở đây. –

+1

Có vẻ như một sự nhầm lẫn của một số loại hoặc một vấn đề phần cứng, sau đó ... bạn có thể muốn chạy kiểm tra [memtest86 +] (http://www.memtest.org/). – ehird

Trả lời

11

Nếu bạn gặp lỗi lỗi phân đoạn và bạn không sử dụng FFI hoặc bất kỳ chức năng nào có unsafe trong tên của họ, đó là không phải không ngạc nhiên, trong mọi tình huống! Nó có nghĩa là có một lỗi với GHC, hoặc một thư viện bạn đang sử dụng đang làm điều gì đó không an toàn.

In ra danh sách vô hạn Double s với mapM_ print hoàn toàn ổn; danh sách sẽ được xử lý gia tăng và chương trình sẽ chạy với việc sử dụng bộ nhớ liên tục. Tôi nghi ngờ có một lỗi trong mô-đun System.Random.Mersenne bạn đang sử dụng hoặc lỗi của thư viện C dựa trên hoặc sự cố với máy tính của bạn (chẳng hạn như RAM bị lỗi). Lưu ý rằng newMTGen đi kèm với cảnh báo này:

Do thư viện SFMT hiện tại là bao la bất tịnh, hiện chỉ một máy phát điện duy nhất được phép cho mỗi chương trình. Nỗ lực để khởi động lại nó sẽ thất bại.

Thay vào đó, bạn nên sử dụng số global MTGen được cung cấp.

Điều đó nói rằng, bạn không thể chuyển đổi IO [Double] thành [IO Double] theo cách đó; không có cách nào để biết danh sách kết quả sẽ mất bao lâu mà không thực hiện hành động IO, điều này là không thể, vì bạn có kết quả thuần túy (mặc dù có một hành động có chứa hành động IO). Đối với danh sách vô hạn, bạn có thể viết:

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

Nhưng mỗi khi bạn thực hiện một hành động trong danh sách này, hành động IO [a] sẽ được thực hiện một lần nữa; nó sẽ loại bỏ phần còn lại của danh sách.

Lý do randoms có thể hoạt động và trả về danh sách vô hạn các số ngẫu nhiên là vì nó sử dụng IO lười với unsafeInterleaveIO. (Lưu ý rằng, mặc dù "không an toàn" trong tên, này một không có thể nguyên nhân segfaults, vì vậy cái gì khác đang được tiến hành.)

khác, khả năng ít có khả năng bao gồm một miscompilation của thư viện C, hoặc một lỗi trong GHC.

+3

Chỉ để ghi lại, tôi nghĩ có thể có điều gì đó sai với máy tính của người hỏi, chứ không phải với thư viện; mã được cung cấp không phân biệt cho tôi. –

+3

+1 cho "bạn không thể chuyển đổi' IO [Double] 'thành' [IO Double] '... không có cách nào để biết danh sách kết quả sẽ mất bao lâu mà không thực hiện hành động IO" –

+0

Vì vậy, không có cách nào chỉ truy cập mười yếu tố danh sách đầu tiên? – Gautam

11

Giả sử tôi muốn chỉ in ra mười giá trị đầu tiên của xs. Làm thế nào tôi có thể làm điều đó?

Chỉ cần sử dụng take:

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

Bạn đã viết

xs đã gõ IO [đúp]

Nhưng trên thực tế, randoms g có kiểu IO [Double], nhưng nhờ vào sự do ký hiệu, xs có loại [Double], bạn chỉ có thể áp dụng take 10 cho nó.

Bạn cũng có thể bỏ qua các ràng buộc sử dụng liftM:

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