2015-09-02 5 views
8

Ich habe gerade angefangen, Haskell zu lernen und eine seltsame Sache gefunden.Warum gibt map ein zusätzliches Element zurück, wenn Bereiche in Haskell verwendet werden?

Let haben wir eine Liste:

ghci> [0,2..5] 
[0,2,4] 

Es hat 3 Elemente. Als ich map mit dieser Liste verwenden bekomme ich 3 Element als Ausgang, zum Beispiel:

ghci> map (+ 1) [0,2..5] 
[1,3,5] 
ghci> map (* 2) [0,2..5] 
[0,4,8] 
ghci> map (`div` 2) [0,2..5] 
[0,1,2] 

Aber wenn ich Bruchteilungs verwenden bekomme ich 4 Elemente in Ausgabeliste:

ghci> map (/ 2) [0,2..5] 
[0.0,1.0,2.0,3.0] 
ghci> length (map (/ 2) [0,2..5]) 
4 

Könnten Sie bitte erklären, warum map kann mehr Elemente zurückgeben, als es war?

Vielen Dank!

+1

Verbunden: http://Stackoverflow.com/q/7290438/2541573 – Jubobs

+1

Beachten Sie, dass 'Länge (Karte f xs) == Länge (Karte f 'xs')' für jede 'Länge xs == Länge xs'' . Dies muss unabhängig von der Implementierung von map sein, da es von seinem Typ abgeleitet ist. 'map' kann nicht zwischen verschiedenen Typen unterscheiden und entscheidet daher, wie viele Elemente zurückgegeben werden sollen. – Bakuriu

Antwort

11

Es ist aufgrund der Umsetzung von Enum für Float und Double:

> [0,2..5] :: [Float] 
[0.0,2.0,4.0,6.0] 

Es ist nicht map es zu tun, aber Float. Insbesondere, wenn Sie enumFromThenTo 0 2 5 :: [Float] aufrufen, erhalten Sie die gleiche Liste. Sie werden die gleichen Ergebnisse für Double sehen.

Dies ist in the haskell report angedeutet, aber das Verhalten ist definitiv nicht offensichtlich. Im Wesentlichen geht es dabei um die Umsetzung der numericEnumFromThenTo (wir bekommen in einigen Haskell Interna hier), die durch die Enum Float Instanz verwendet wird:

numericEnumFromThenTo n n' m = takeWhile p (numericEnumFromThen n n') 
    where 
     p | n' >= n = (<= m + (n' - n)/2) 
      | otherwise = (>= m + (n' - n)/2) 

numericEnumFromThen n m = iterate (+ (m - n)) n 

So haben Sie numericEnumFromThen 0.0 2.0 die Liste [0.0,2.0,4.0,6.0,8.0,...] zu erzeugen, dann tun Sie takeWhile p darauf, was in diesem Fall der Funktion \x -> x <= 5.0 + (2.0 - 0.0)/2 oder einfacher \x -> x <= 6.0 entspricht, weshalb 6.0 in der Ausgabeliste [0.0,2.0..5.0] enthalten ist.

Ich kann nicht erklären, warum es auf diese Weise implementiert ist, dass ich auch ziemlich verwirrend ist, aber hoffentlich habe ich beantworten die wie für deren Umsetzung.

+5

Der Grund ist, dass es wichtig ist, dass "[0.0.1..1]" immer elf Elemente hat, wobei es nicht wirklich wichtig ist, wie viele Elemente "[0.2..5]" hat, da es in der ersten Unsinn ist Ort. –

+2

Um @ReidBarton zu erläutern, ist die Implementierung von Gleitkommabereichen so ausgelegt, dass die Länge im allgemeinen Fall unempfindlich gegen Rundungsfehler ist, wenn die Differenz zwischen Anfangs- und Endpunkt * ungefähr * eine ganze Anzahl von Schritten ist. Der Cutoff wird stattdessen auf den unwahrscheinlichen "entgegengesetzten" Fall gesetzt, bei dem ein Halbschrittüberschuß vorliegt. –