Haskell Language
Arytmetyka
Szukaj…
Wprowadzenie
W języku Haskell wszystkie wyrażenia (w tym stałe numeryczne i działające na nich funkcje) mają typ rozstrzygalny. Podczas kompilacji moduł sprawdzania typu określa typ wyrażenia na podstawie typów elementarnych funkcji, które go tworzą. Ponieważ dane są domyślnie niezmienne, nie ma operacji „rzutowania typu”, ale istnieją funkcje, które kopiują dane i uogólniają lub specjalizują typy w rozsądnym zakresie.
Uwagi
Hierarchia liczbowa typu
Num
znajduje się u podstaw numerycznej hierarchii klas typów. Jego charakterystyczne operacje i niektóre typowe instancje pokazano poniżej (te domyślnie ładowane z Prelude plus te z 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’
Widzieliśmy już klasę Fractional
, która wymaga Num
i wprowadza pojęcia „dzielenia” (/)
i odwrotności liczby:
λ> :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’
Modele klasy Real
.. liczby rzeczywiste. Wymaga Num
i Ord
, dlatego modeluje uporządkowane pole numeryczne. Przykładowo, liczby zespolone nie są polem uporządkowanym (tzn. Nie mają naturalnej relacji uporządkowania):
λ> :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
reprezentuje liczby, które można zaokrąglić
λ> :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
(co sugeruje Fractional
) reprezentuje stałe i operacje, które mogą nie mieć skończonego rozwinięcia dziesiętnego.
λ> :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’
Uwaga: podczas wyrażeń takich jak sqrt . negate :: Floating a => a -> a
są całkowicie poprawne, mogą zwracać NaN
(„ NaN
-a-number”), co może nie być zamierzonym zachowaniem. W takich przypadkach możemy chcieć pracować nad polem Complex (pokazanym później).
Podstawowe przykłady
λ> :t 1
1 :: Num t => t
λ> :t pi
pi :: Floating a => a
W powyższych przykładach moduł sprawdzający typ podaje klasę typu, a nie konkretny typ dla dwóch stałych. W Haskell klasa Num
jest najbardziej ogólną klasą numeryczną (ponieważ obejmuje liczby całkowite i liczby rzeczywiste), ale pi
musi należeć do bardziej wyspecjalizowanej klasy, ponieważ ma niezerową część ułamkową.
list0 :: [Integer]
list0 = [1, 2, 3]
list1 :: [Double]
list1 = [1, 2, pi]
Powyższe typy betonu zostały wywnioskowane przez GHC. Bardziej ogólne typy, takie jak list0 :: Num a => [a]
, działałyby, ale byłyby również trudniejsze do zachowania (np. Gdyby ktoś rzucił Double
na listę Num
), ze względu na powyższe zastrzeżenia.
`Nie można wydedukować (Ułamkowe int) ...`
Komunikat o błędzie w tytule jest częstym błędem dla początkujących. Zobaczmy, jak powstaje i jak to naprawić.
Załóżmy, że musimy obliczyć średnią wartość listy liczb; wydaje się, że następująca deklaracja to robi, ale nie skompiluje się:
averageOfList ll = sum ll / length ll
Problem dotyczy funkcji dzielenia (/)
: jej sygnatura to (/) :: Fractional a => a -> a -> a
, ale w przypadku powyżej mianownika (podana przez length :: Foldable t => ta -> Int
) jest typu Int
(i Int
nie należy do klasy Fractional
), stąd komunikat o błędzie.
Możemy naprawić komunikat o błędzie za pomocą fromIntegral :: (Num b, Integral a) => a -> b
. Widać, że ta funkcja akceptuje wartości dowolnego typu Integral
i zwraca odpowiednie wartości w klasie Num
:
averageOfList' :: (Foldable t, Fractional a) => t a -> a
averageOfList' ll = sum ll / fromIntegral (length ll)
Przykłady funkcji
Jaki jest typ (+)
?
λ> :t (+)
(+) :: Num a => a -> a -> a
Jaki jest typ sqrt
?
λ> :t sqrt
sqrt :: Floating a => a -> a
Jaki jest typ sqrt . fromIntegral
?
sqrt . fromIntegral :: (Integral a, Floating c) => a -> c