Haskell Language
Aritmetica
Ricerca…
introduzione
In Haskell, tutte le espressioni (che includono costanti e funzioni numeriche che operano su quelle) hanno un tipo decidibile. Al momento della compilazione, il type-checker deduce il tipo di un'espressione dai tipi di funzioni elementari che lo compongono. Poiché i dati sono immutabili per impostazione predefinita, non esistono operazioni di "tipo casting", ma esistono funzioni che copiano dati e generalizzano o specializzano i tipi all'interno della ragione.
Osservazioni
La gerarchia dei tipi tipici numerici
Num
trova nella radice della gerarchia dei tipi tipici numerici. Le sue operazioni caratteristiche e alcune istanze comuni sono mostrate di seguito (quelle caricate di default con Preludio più quelle di Data.Complex
):
λ> :i Num
class Num a where
(+) :: a -> a -> a
(-) :: a -> a -> a
(*) :: a -> a -> a
negate :: a -> a
abs :: a -> a
signum :: a -> a
fromInteger :: Integer -> a
{-# MINIMAL (+), (*), abs, signum, fromInteger, (negate | (-)) #-}
-- Defined in ‘GHC.Num’
instance RealFloat a => Num (Complex a) -- Defined in ‘Data.Complex’
instance Num Word -- Defined in ‘GHC.Num’
instance Num Integer -- Defined in ‘GHC.Num’
instance Num Int -- Defined in ‘GHC.Num’
instance Num Float -- Defined in ‘GHC.Float’
instance Num Double -- Defined in ‘GHC.Float’
Abbiamo già visto la classe Fractional
, che richiede Num
e introduce le nozioni di "divisione" (/)
e reciproca di un numero:
λ> :i Fractional
class Num a => Fractional a where
(/) :: a -> a -> a
recip :: a -> a
fromRational :: Rational -> a
{-# MINIMAL fromRational, (recip | (/)) #-}
-- Defined in ‘GHC.Real’
instance RealFloat a => Fractional (Complex a) -- Defined in ‘Data.Complex’
instance Fractional Float -- Defined in ‘GHC.Float’
instance Fractional Double -- Defined in ‘GHC.Float’
I Real
modelli di classe ... i numeri reali. Richiede Num
e Ord
, quindi modella un campo numerico ordinato. Come controesempio, i numeri complessi non sono un campo ordinato (cioè non possiedono una relazione di ordinamento naturale):
λ> :i Real
class (Num a, Ord a) => Real a where
toRational :: a -> Rational
{-# MINIMAL toRational #-}
-- Defined in ‘GHC.Real’
instance Real Word -- Defined in ‘GHC.Real’
instance Real Integer -- Defined in ‘GHC.Real’
instance Real Int -- Defined in ‘GHC.Real’
instance Real Float -- Defined in ‘GHC.Float’
instance Real Double -- Defined in ‘GHC.Float’
RealFrac
rappresenta numeri che possono essere arrotondati
λ> :i RealFrac
class (Real a, Fractional a) => RealFrac a where
properFraction :: Integral b => a -> (b, a)
truncate :: Integral b => a -> b
round :: Integral b => a -> b
ceiling :: Integral b => a -> b
floor :: Integral b => a -> b
{-# MINIMAL properFraction #-}
-- Defined in ‘GHC.Real’
instance RealFrac Float -- Defined in ‘GHC.Float’
instance RealFrac Double -- Defined in ‘GHC.Float’
Floating
(che implica Fractional
) rappresenta costanti e operazioni che potrebbero non avere un'espansione decimale finita.
λ> :i Floating
class Fractional a => Floating a where
pi :: a
exp :: a -> a
log :: a -> a
sqrt :: a -> a
(**) :: a -> a -> a
logBase :: a -> a -> a
sin :: a -> a
cos :: a -> a
tan :: a -> a
asin :: a -> a
acos :: a -> a
atan :: a -> a
sinh :: a -> a
cosh :: a -> a
tanh :: a -> a
asinh :: a -> a
acosh :: a -> a
atanh :: a -> a
GHC.Float.log1p :: a -> a
GHC.Float.expm1 :: a -> a
GHC.Float.log1pexp :: a -> a
GHC.Float.log1mexp :: a -> a
{-# MINIMAL pi, exp, log, sin, cos, asin, acos, atan, sinh, cosh,
asinh, acosh, atanh #-}
-- Defined in ‘GHC.Float’
instance RealFloat a => Floating (Complex a) -- Defined in ‘Data.Complex’
instance Floating Float -- Defined in ‘GHC.Float’
instance Floating Double -- Defined in ‘GHC.Float’
Attenzione: mentre espressioni come sqrt . negate :: Floating a => a -> a
sono perfettamente validi, potrebbero restituire NaN
("not-a-number"), che potrebbe non essere un comportamento previsto. In questi casi, potremmo voler lavorare sul campo Complesso (mostrato più avanti).
Esempi di base
λ> :t 1
1 :: Num t => t
λ> :t pi
pi :: Floating a => a
Negli esempi sopra, il type-checker deduce una classe tipo piuttosto che un tipo concreto per le due costanti. In Haskell, la classe Num
è la più numerica (poiché comprende interi e reali), ma pi
deve appartenere a una classe più specializzata, poiché ha una parte frazionaria diversa da zero.
list0 :: [Integer]
list0 = [1, 2, 3]
list1 :: [Double]
list1 = [1, 2, pi]
I tipi concreti sopra sono stati dedotti da GHC. Tipi più generali come list0 :: Num a => [a]
avrebbe funzionato, ma sarebbe stato anche più difficile da preservare (ad esempio se si è classificato un Double
in un elenco di Num
), a causa delle avvertenze mostrate sopra.
`Impossibile dedurre (Int frazionale) ...`
Il messaggio di errore nel titolo è un errore principiante comune. Vediamo come si presenta e come risolverlo.
Supponiamo di dover calcolare il valore medio di un elenco di numeri; la seguente dichiarazione sembrerebbe farlo, ma non verrebbe compilato:
averageOfList ll = sum ll / length ll
Il problema è con la funzione division (/)
: la sua firma è (/) :: Fractional a => a -> a -> a
, ma nel caso sopra il denominatore (dato dalla length :: Foldable t => ta -> Int
) è di tipo Int
(e Int
non appartiene alla classe Fractional
) quindi il messaggio di errore.
Possiamo correggere il messaggio di errore con fromIntegral :: (Num b, Integral a) => a -> b
. Si può vedere che questa funzione accetta valori di qualsiasi tipo Integral
e restituisce quelli corrispondenti nella classe Num
:
averageOfList' :: (Foldable t, Fractional a) => t a -> a
averageOfList' ll = sum ll / fromIntegral (length ll)
Esempi di funzioni
Qual è il tipo di (+)
?
λ> :t (+)
(+) :: Num a => a -> a -> a
Qual è il tipo di sqrt
?
λ> :t sqrt
sqrt :: Floating a => a -> a
Qual è il tipo di sqrt . fromIntegral
?
sqrt . fromIntegral :: (Integral a, Floating c) => a -> c