Számok és típusozás

A Haskell értelmező

A leggyakrabban használt Haskell értelmező a GHCi.

Ebben Haskell-kifejezéseket értékelhetünk ki vagy kifejezés típusát kérdezhetjük meg:

Test>
12 :: Integer
Test>
2 * 6 :: Num a => a

Alapesetben a Prelude modulban található műveleteket használhatjuk, ezt jelzi az értelmező elindítása után a súgóban a Prelude> (prompt).

A :set +t parancs hatására a kifejezések kiértékelése után kiírja azok típusát is. :set +t jelentése “set type on”.

Számtípusok

Test>
59049 :: Int
Test>
59049 :: Integer
Test>
-2984622845537545263 :: Int
Test>
515377520732011331036461129765621272702107522001 :: Integer

Néhány számtípus:

Integer egész szám
Int korlátos egész szám (-2147483648..2147483647)
Rational racionális szám
Double dupla pontosságú lebegőpontos szám
Test>
88817841970012523233890533447265625 :: Integer
Test>
1 :% 3 :: Rational

A megadott Int korlátok 32 bites gépen, GHC esetén igazak.

A Double típus használata általában kerekítési hibákkal jár:

Test>
0.10000000000000009 :: Double

Egyéb számtípusok:

Ratio Integer (= Rational) Két Integer hányadosa
Ratio Int Két Int hányadosa
Fixed E6 (= Micro) Tizedestört 6 tizedesjeggyel
Fixed E12 (= Pico) Tizedestört 12 tizedesjeggyel
Complex Float Komplex szám (Float pár)
Complex Double Komplex szám (Double pár)

Esetleges (,,ad hoc’’) polimorfizmus

Test>
(+) :: Num a => a -> a -> a -- típusinformáció kérés

Num a jelentése: a kicserélhető az Int, Integer, Rational, Double, … típusokra:

(+) :: Int      -> Int      -> Int
(+) :: Integer  -> Integer  -> Integer
(+) :: Rational -> Rational -> Rational
(+) :: Double   -> Double   -> Double

Num egy típusosztály.

A Num típusosztálynak van példánya az Int típusra.

A típusokban a kisbetűs nevek típusváltozók. A típusváltozókat tartalmazó típusokat specializálhatjuk, azaz a típusváltozókat más típusokra cserélhetjük a megkötések szerint.

Az N paraméterű függvény típusa:
t1 -> t2 ->-> tN -> tN+1.

Kivonás, szorzás

(-) :: Num a => a -> a -> a
(*) :: Num a => a -> a -> a

Osztás

(/) :: Fractional a => a -> a -> a

Fractional a jelentése: a kicserélhető a Rational, Double, … típusokra.

Test>
0.3333333333333333 :: Double
Test>
<interactive>:1:1: error:
    • No instance for (Fractional Int) arising from a use of ‘/’
    • In the expression: 1 / 3 :: Int

Maradékos osztás

div :: Integral a => a -> a -> a
mod :: Integral a => a -> a -> a

Integral a jelentése: a kicserélhető az Int, Integer, … típusokra.

Test>
3 :: Integer
Test>
10 :: Integer
Test>
3 :: Integer
Test>
<interactive>:1:1: error:
    • No instance for (Integral Double) arising from a use of ‘div’
    • In the expression: 53 `div` 5 :: Double

Feladat: Másodpercek száma

Körülbelül hány másodperc van egy évben?

Solution>

Feladat: Gömb térfogata

Számítsuk ki az 1.01 sugarú gömb térfogatát!

Test>

Megjegyzés: pi értéke π, a gömb térfogata 4r3π/3.

Feladat: Oszthatóság

Döntsük el, hogy a 23 osztja-e a 532253373-at!

Test>

Hatványozás

(^) :: (Integral b, Num a) => a -> b -> a
(^^) :: (Fractional a, Integral b) => a -> b -> a
(**) :: Floating a => a -> a -> a

Floating a jelentése: a kicserélhető a Double, … típusokra.

Test>
2.718282043475248 :: Double
Test>
0.5 :: Double
Test>
1.4142135623730951 :: Double

Három féle hatványozás van aszerint, hogy a kitevő nemnegatív egész, egész vagy lebegőpontos szám.

Polimorf számliterálok és konstansok

Test>
1230 :: Num p => p
Test>
1.1 :: Fractional p => p
Test>
pi :: Floating a => a

Példa specializálásra:

Test>
1230 :% 1 :: Rational

Elméleti feladatok:

  1. Csak azonos típusú számokat adhatunk össze.
    Miért típushelyes 1 + 1.1?
  2. Mi a 3 típusa az (1 + 3) / 4 kifejezésben?

Egy kifejezés típushelyes, ha kifejezések típusának specializálásával elérhetjük, hogy minden függvényargumentum típusa megegyezzen a függvény által kívánt típussal.

A típusspecializáció során a típusváltozókat tetszőleges típusra cserélhetjük:

a -> a specializációi: Char -> Char, [b] -> [b], …

A típusosztályok közt egy alosztály reláció van:

Syntax42e12d624072be4cba0c94e761443455.png

Ez lehetőséget biztosít egy másik fajta típusspecializációra. Például a Fractional a tulajdonságból következik Num a.
Ezért a típusspecializáció során Num kicserélhető Fractional-re:

Test>

specializált típusa

Test>

Zárójelezés

Az előbbi operátorok csoportosítása kötési erősség szerint:

^, ^^, ** irány: (.(.))
*, / irány: ((.).)
+, - irány: ((.).)

Például 3 - 3 ^ 3 ^ 3 - 3 automatikus zárójelezése:

Test>

A kötési erősség értelmezése a szokásos: 1 + 2 * 4 helyes zárójelezése 1 + (2 * 4).

A balra és jobbra kötésnek akkor van jelentősége, ha azonos kötési erősségű operátorok kerülnek egymás mellé. 1/2/4 helyes zárójelezése (1/2)/4, míg 3^3^3 helyes zárójelezése 3^(3^3).

Feladat

Írjuk ki a rejtett zárójeleket!

Test>
33.4 :: Double

A negálás

A negálás az egyetlen prefix operátor. A negálás jele, kötési erőssége és kötése megegyezik a kivonáséval.

- 6 - 2 zárójelezése (- 6) - 2,
- 6 ^ 2 zárójelezése - (6 ^ 2).

Hibás kifejezés:

Test>
<interactive>:1:1: error:
    Precedence parsing error
        cannot mix ‘+’ [infixl 6] and prefix `-' [infixl 6] in the same infix expression

Helyesen:

Test>
-8 :: Integer

A következőképpen tudjuk megkülönböztetni a negálást a kivonástól: Ha a '-' jel kifejezés után szerepel, akkor kivonást, egyébként negálást jelöl.

Negatív számliterál nincs, például a -13 kifejezés a 13 negáltja, ezért sokszor zárójellel kell védeni a negatív számokat.

Függvényalkalmazás

A függvényalkalmazáskor nem kell zárójel és vessző:

Test>

Hibás kifejezés: sin cos 1

Helyesen:

Test>

A függvényalkalmazás minden operátornál erősebb:
sin pi + 1 zárójelezése (sin pi) + 1.


A függvény és az operátor szintaktikus kategória. A függvénynevek betűkből állnak és kisbetűvel kezdődnek, az operátorok grafikus karakterekből állnak.

Grafikus karakterek a !?.#$%@&*+-~^/|\<=> ASCII-karakterek és a nem ASCII-karakterek közül a szimbólumok.

A zárójelek kirakása megengedett, mivel tetszőleges kifejezés zárójelbe rakható, ha ez nem befolyásolja a műveleti sorrendet: sin(pi).

sin cos 1 azért hibás kifejezés, mert azt állítja hogy sin egy két argumentumú függvény, amelynek átadjuk a cos és 1 argumentumokat.

Függvények és operátorok

A kétparaméteres függvények is használhatók infix módon:

Test>

Az operátorok is használhatók prefix módon:

Test>

A függvényneveket `` jelek közé téve infix módon használhatjuk.

Az infix módon használt div és mod kötési erőssége és zárójelezése ugyanaz, mint a * és / operátoroké.

Test>
1 :: Integer
Test>
25 :: Integer
Test>
0 :: Integer

Az operátorneveket zárójelek közé téve prefix módon használhatjuk.

A prefix módon használt operátorok kötési erőssége a függvényalkalmazások erősségével egyezik meg (azaz a legerősebb).

Test>
9 :: Integer
Test>
5 :: Integer
Test>
8 :: Integer