2012-04-04 21 views
9

Betrachten Sie den folgenden Code, der zum Ausdrucken von Zufallszahlen soll:Haskell Monade: IO [Double] bis [IO Double]

import System.Random.Mersenne 

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

Sobald er ausgeführt wird, ich einen Segmentation Fault Fehler. Das ist nicht überraschend, da die Funktion 'randoms' eine unendliche Liste erzeugt. Angenommen, ich möchte nur die ersten zehn Werte von xs ausdrucken. Wie könnte ich das tun? xs hat den Typ IO [Double], und ich denke, ich möchte eine Variable vom Typ [IO Double]. Welche Operatoren existieren für die Konvertierung zwischen den beiden?

+0

übrigens IO [Double] -> [IO Double] ist im Wesentlichen die umgekehrte Art Signatur 'Sequenz'. – Gautam

+3

Es segregiert hier nicht. –

+1

Klingt wie ein Missverständnis irgendeiner Art oder ein Hardware-Problem, dann ... möchten Sie vielleicht einen [memtest86 +] (http://www.memtest.org/) Check durchführen. – ehird

Antwort

11

Wenn Sie einen Segmentierungsfehler erhalten, und Sie nicht das FFI oder irgendwelche Funktionen mit unsafe in ihrem Namen verwendet haben, das ist nicht nicht überraschend, in jeder Situation! Es bedeutet, dass ein Fehler mit GHC vorliegt oder eine Bibliothek, die Sie verwenden, etwas unsicher macht.

Drucken eine unendliche Liste von Double s mit mapM_ print ist völlig in Ordnung; Die Liste wird inkrementell verarbeitet und das Programm sollte mit konstanter Speicherbelegung laufen. Ich vermute, dass es einen Fehler im System.Random.Mersenne Modul gibt, das Sie verwenden, oder einen Fehler in der C-Bibliothek, auf der es basiert, oder ein Problem mit Ihrem Computer (z. B. fehlerhaftem RAM). Hinweis dass newMTGen mit dieser Warnung kommt:

Aufgrund der aktuellen SFMT Bibliothek erheblich unreine Wesen, die derzeit nur ein einziger Generator pro-Programm erlaubt. Versuche, es neu zu initialisieren, werden fehlschlagen.

Sie könnten besser mit der bereitgestellten global MTGen statt.

Das heißt, Sie können nicht IO [Double] in [IO Double] auf diese Weise konvertieren; Es gibt keine Möglichkeit zu wissen, wie lange die resultierende Liste wäre, ohne die Aktion IO auszuführen, was unmöglich ist, da Sie ein reines Ergebnis haben (wenn auch eines, das zufällig IO Aktionen enthält). Für unendliche Listen, könnte man schreiben:

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

Aber jedes Mal, wenn Sie eine Aktion in dieser Liste ausführen, die IO [a] Aktion würde wieder ausgeführt werden; es würde nur den Rest der Liste verwerfen.

Der Grund randoms kann arbeiten und eine unbegrenzte Liste von Zufallszahlen zurückgeben, weil es Lazy IO mit unsafeInterleaveIO verwendet. (Beachten Sie, dass trotz der „unsicheren“ im Namen, dieser kann nicht Ursache segfaults, so etwas anderes ist zu Fuß.)

Andere, weniger wahrscheinlich Möglichkeiten umfassen eine miscompilation der C-Bibliothek, oder ein Fehler in GHC.

+3

Nur für den Rekord, ich denke, es ist möglich, etwas stimmt nicht mit dem Computer des Fragestellers, nicht mit der Bibliothek; Der mitgelieferte Code ist für mich nicht segfault. –

+3

+1 für "Sie können' IO [Double] 'nicht in' [IO Double] 'konvertieren ... es gibt keine Möglichkeit zu wissen, wie lange die resultierende Liste wäre, ohne die IO-Aktion auszuführen" –

+0

Also gibt es keine Möglichkeit nur auf die ersten zehn Listenelemente zugreifen? – Gautam

11

Angenommen, ich wollte nur die ersten zehn Werte von xs ausdrucken. Wie könnte ich das tun?

Nur take verwenden:

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

Sie

xs geschrieben hat IO Typ [Double]

Aber eigentlich hat randoms gIO [Double] geben, aber dank der do Notation, xs hat den Typ [Double], Sie können einfach take 10 darauf anwenden.

Sie könnten auch die Bindung mit liftM überspringen:

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