2015-09-02 9 views
8

Ho appena iniziato ad imparare Haskell e ho trovato una cosa strana.Perché map restituisce un elemento aggiuntivo quando si usano gli intervalli in Haskell?

Let abbiamo una lista:

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

Dispone di 3 elementi. Quando uso map con questa lista ho 3 come elemento di uscita, ad esempio:

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] 

Ma quando io uso la divisione frazionaria ottengo 4 elementi in lista di uscita:

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

Potrebbe spiegare perché map potrebbe restituire più elementi di quanto lo fosse?

Grazie!

+1

Correlato: http://stackoverflow.com/q/7290438/2541573 – Jubobs

+1

Si noti che 'lunghezza (mappa f xs) == lunghezza (mappa f 'xs')' per ogni 'lunghezza xs == lunghezza xs'' . Questo * deve * essere vero indipendentemente dall'implementazione di 'map' poiché deriva dal suo tipo. 'map' non è in grado di distinguere tra diversi tipi e quindi decide quanti elementi restituire. – Bakuriu

risposta

11

E 'a causa della realizzazione di Enum per Float e Double:

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

Non map lo sta facendo, ma Float. Nello specifico, se chiami enumFromThenTo 0 2 5 :: [Float], otterrai lo stesso elenco. Vedrai gli stessi risultati per Double.

Questo è suggerito in the haskell report, ma il comportamento è decisamente non ovvio. In sostanza, si scende alla realizzazione di numericEnumFromThenTo (stiamo ottenendo in alcuni interni Haskell qui), che viene utilizzato dal Enum Float esempio:

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 

In modo da avere numericEnumFromThen 0.0 2.0 generare la lista [0.0,2.0,4.0,6.0,8.0,...], poi si fa takeWhile p su questo, che in questo caso è equivalente alla funzione \x -> x <= 5.0 + (2.0 - 0.0)/2, o più semplicemente \x -> x <= 6.0, ecco perché 6.0 è incluso nell'elenco di output di [0.0,2.0..5.0].

non riesco a spiegare il motivo per cui è implementato in questo modo, che è abbastanza sconcertante anche a me, ma spero di aver risposto alla come per la sua attuazione.

+5

Il motivo è che è importante che '[0,0.1..1]' abbia sempre undici elementi, mentre non importa in realtà quanti '' [0,2..5] 'ha dato che non ha senso nel primo posto. –

+2

Per elaborare su @ReidBarton, l'implementazione degli intervalli in virgola mobile è progettata in modo che la lunghezza non sia sensibile agli errori di arrotondamento nel caso comune quando la differenza tra punto iniziale e punto finale è * circa * un numero intero di passaggi. Il cutoff è invece posto nell'improbabile caso "opposto" in cui c'è un mezzo passo in più. –

Problemi correlati