2014-12-22 9 views
14

Ich habe bereits überprüft hoogle, http://hackage.haskell.org/package/base-4.7.0.1/docs/Prelude.html#v:mapMWas ist der Unterschied zwischen mapM_ und mapM in Haskell?

Hoogle sagt mapM_ ignorieren Sie die Ergebnisse.

Aber ich habe immer noch keine Idee, wie man richtig verwendet.

main = mapM_ (putStrLn.show) [1,2] 

main = mapM (putStrLn.show) [1,2] 

main = map (putStrLn.show) [1,2] 
+2

dass der einzige Unterschied ist. 'mapM_' gibt' m() 'zurück und' mapM' gibt 'm [b]' zurück. Wenn Sie also die Ergebnisse wollen, benutzen Sie 'mapM'. Wenn Sie die Ergebnisse nicht wünschen (z. B. wenn Sie nur an den Nebenwirkungen interessiert sind), verwenden Sie 'mapM_'. –

+5

Zwei von drei dieser Codes * funktionieren *. –

+0

Entschuldigung es ist irgendwie mein Missverständnis, ich werde mich ändern. –

Antwort

24

mapM_ ist nützlich für die Ausführung etwas nur für seine Nebenwirkungen. Wenn Sie beispielsweise eine Zeichenfolge in die Standardausgabe drucken, wird nichts Nützliches zurückgegeben - sie gibt () zurück. Wenn wir eine Liste von drei Strings haben, würden wir am Ende eine Liste [(),(),()] akkumulieren. Das Erstellen dieser Liste hat Laufzeitkosten, sowohl in Bezug auf die Geschwindigkeit als auch auf die Speichernutzung, sodass wir diesen Schritt unter Verwendung von mapM_ vollständig überspringen können.

Manchmal müssen wir jedoch Nebenwirkungen ausführen und eine Liste der Ergebnisse erstellen. Wenn wir eine Funktion wie

lookupUserById :: UserId -> IO User 

Dann haben wir diese verwenden, um eine Liste von UserId s auf eine Liste von User s aufzublasen:

lookupUsers :: [UserId] -> IO [User] 
lookupUsers = mapM lookupUserById 
+0

vielen dank! –

1

mapM_ das Ergebnis ignoriert. Dies bedeutet, dass kein Rückgabewert zurückgegeben wird. Sie können dies im interaktiven Modus sehen:

$ ghci 
GHCi, version 7.6.3: http://www.haskell.org/ghc/ :? for help 
Loading package ghc-prim ... linking ... done. 
Loading package integer-gmp ... linking ... done. 
Loading package base ... linking ... done. 

Prelude> mapM (putStrLn . show) [1,2] 
1 
2 
[(),()] 
Prelude> mapM_ (putStrLn . show) [1,2] 
1 
2 
+0

Etwas wird immer zurückgegeben, es sei denn, es liegt ein Fehler vor oder die Berechnung kann nicht beendet werden. GHCi druckt das Ergebnis einer 'IO'-Aktion nur dann nicht aus, wenn das Ergebnis '()' ist. Wenn Sie 'mapM_ (putStrLn. Show) [1,2] >> = print' geschrieben haben, erhalten Sie' 1', '2' und'() '. – dfeuer

7

Die Kernidee ist, dass mapM Karten eine „Aktion“ (dh Funktion vom Typ a -> m b) über eine Liste und gibt Ihnen alle Ergebnisse als m [b]. mapM_ macht das gleiche, aber sammelt nie die Ergebnisse und gibt eine m() zurück.

Wenn Sie sich für die Ergebnisse Ihrer -Funktion interessieren (dh die b s), verwenden Sie mapM. Wenn Sie nur für den Effekt interessiert sind, was auch immer es ist, aber nicht den resultierenden Wert, verwenden Sie mapM_, weil es effizienter sein kann und, noch wichtiger, macht Ihre Absichten klar.

Sie würden immer mapM_ mit Funktionen des Typs a -> m(), wie print oder putStrLn verwenden. Diese Funktionen geben () zurück, um anzuzeigen, dass nur der Effekt von Bedeutung ist. Wenn Sie mapM verwenden, erhalten Sie eine Liste von () (dh [(),(),()]), die völlig nutzlos wäre, aber etwas Speicher verschwendet. Wenn Sie mapM_ verwenden, erhalten Sie nur eine (), aber es würde immer noch alles drucken.

Wenn Sie sich jedoch für die zurückgegebenen Werte interessieren, verwenden Sie mapM. Stellen Sie sich als eine hypothetische Beispiel eine Funktion fetchUrl :: Url -> IO Response -chances sind, kümmern Sie sich um die Antwort, die Sie für jede URL erhalten. Verwenden Sie dazu mapM, um eine Liste mit Antworten zu erhalten, die Sie dann im Rest Ihres Codes verwenden können.

Also: mapM wenn Sie sich für die Ergebnisse interessieren und mapM_ wenn Sie nicht.

Normal map ist etwas anderes: Es nimmt eine normale Funktion (a -> b) anstelle von einer mit einer Monade().Dies bedeutet, dass es kann keine Art von Wirkung neben der Rückgabe der geänderten Liste haben. Sie würden es verwenden, wenn Sie eine Liste mit einer normalen Funktion transformieren möchten. map_ existiert nicht, weil Sie, da Sie keine Effekte haben, immer über die Ergebnisse der Verwendung von map kümmern.

3

In allgemeiner ausgedrückt, ist der Unterschied, dass mapM_ muss nur "consume" alle Elemente des Eingangs, während mapM auch die Datenstruktur mit neuen Werten zu „re-build“ benötigt. Das ist ziemlich trivial für Listen: Sie müssen nur traverse entlang der Cons-Zellen, Werte mit denen von der Aktion aktualisiert, die Sie zuordnen. Bei Containern, deren Struktur von den tatsächlich enthaltenen Werten abhängt, funktioniert das nicht so einfach. So z.B. für Data.Set können Sie das Äquivalent von mapM mit Typ nicht definieren (a -> m b) -> Set a -> m (Set b) – es eine Instanz von Foldable ist, aber nicht von Traversable

Verwandte Themen