FP / Štruktúry údajov

Štruktúry údajov

Funkcionálne programovanie

Alias

type String = [Char]
type Point = (Double, Double)

Štruktúra

data Point = Point Double Double

center = Point 0 0

Ako použiť?

Porovnávanie vzorov

distance :: Point -> Point -> Double
distance (Point x1 y1) (Point x2 y2) =
         sqrt ((x1 - x2)^2 + (y1 - y2)^2)

distanceFromCenter :: Point -> Double
distanceFromCenter p = distance center p

Konštruktor

Násobný typ (product type)

Množina hodnôt — karteziánsky súčin typov zložiek

data Time = Time Int Int

$$ \mathit{Time} = \mathit{Int} \times \mathit{Int} $$

Enumeračný typ

data Color = Red | Green | Blue
    deriving (Eq)

isRed :: Color -> Bool
isRed c = c == Red

isBlue :: Color -> Bool
isBlue Blue = True
isBlue _ = False

Súčtový typ

Množina hodnôt — súčet (disjunktné zjednotenie ⨆) množín zložiek

$$ \mathit{Color} = \{Red\} + \{Green\} + \{Blue\} $$

data Point = Point Double Double
data Color = Red | Green | Blue

Vieme to spojiť?

Algebraické dátové typy (ADT)

Zovšeobecnenie súčtového typu — súčet súčinov

$$ \mathit{Shape} = (\mathit{Point} \times \mathit{Double}) + (\mathit{Point} \times \mathit{Point}) + (\mathit{Point} \times \mathit{Point} \times \mathit{Point}) $$

data Shape = Circle Point Double
           | Rectangle Point Point
           | Triangle Point Point Point

type Point = (Double, Double)

Použitie

area :: Shape -> Double
area (Circle _ r) = pi * r^2
area (Rectangle (x1, y1) (x2, y2)) = abs ((x2-x1) * (y2-y1))
area (Triangle (x1, y1) (x2, y2) (x3, y3)) =
    0.5 * abs (x1*y2 - x2*y1 + x2*y3 - x3*y2 + x3*y1 - x1*y3)

Iné jazyky

C

typedef enum { Circle, Rectangle, Triangle } ShapeKind;
typedef struct { double x, y; } Point;
typedef struct {
    ShapeKind kind;
    union {
        struct { Point center; double radius; };/* Circle */
        struct { Point p, q; };              /* Rectangle */
        struct { Point a, b, c; };            /* Triangle */
    };
} Shape;

OOP

Java

interface Shape { double area(); ... }
class Circle implements Shape { ... }
class Rectangle implements Shape { ... }
class Triangle implements Shape { ... }

Kotlin — sealed classes

sealed interface Shape
class Circle(val center: Point, val radius: Double): Shape
class Rectangle(val a: Point, val b: Point): Shape

Kotlin — pattern matching

fun area(s: Shape): Double = when(s) {
    is Circle -> PI * s.radius.pow(2)
    is Rectangle -> abs((s.b.x - s.a.x) * (s.b.y - s.a.y))
}

Java 17

public sealed interface Shape {}
record Circle(Point center, double radius) implements Shape {}
record Rectangle(Point a, Point b) implements Shape {}
...
if (shape instanceof Circle c) {
    area = PI * c.radius() * c.radius();
} else if (shape instanceof Rectangle r) {
    area = abs((r.b().y() - r.a().y()) * (r.b().x() - r.a().x()));
}

Java — pattern matching

double area = switch (shape) {
    case Circle c -> PI * c.radius() * c.radius();
    case Rectangle r -> abs((r.b().y() - r.a().y())
            * (r.b().x() - r.a().x()));
};

Java 19 — record patterns

double area = switch (shape) {
    case Circle(Point c, double r) -> PI * r * r;
    case Rectangle(Point(double x1, double y1),
                   Point(double x2, double y2)) ->
            abs((y2 - y1) * (x2 - x1));
};

Python — pattern matching

def sum_first_two(a_list):
    match a_list:
        case []:
            return 0
        case [x]:
            return x
        case [x, y, *_]:
            return x + y
def area(shape):
    match shape:
        case Circle(radius=r):
            return pi * r**2
        case Rectangle((x1, y1), (x2, y2)):
            return abs((x2 - x1) * (y2 - y1))

Pre ľubovoľné triedy

class Circle:
    def __init__(self, center, radius):
        self.center = center
        self.radius = radius

@dataclass
class Rectangle:
    a: Tuple[double, double]
    b: Tuple[double, double]

Späť na Haskell!

Konštruktor ≠ Typ

Rekurzívne typy

-- Binárny strom
data Tree = Leaf Int
          | Node Tree Tree
height :: Tree -> Int
height (Leaf _) = 1
height (Node l r) = 1 + max (height l) (height r)

Parametrický polymorfizmus

data List a = Cons a (List a) | Nil
aList :: List Char
aList = Cons 'a' (Cons 'b' (Cons 'c' Nil))
listLength :: List a -> Int
listLength Nil = 0
listLength (Cons _ xs) = 1 + listLength xs

Cons ~ :
Nil ~ []

Ďalšie príklady

Maybe

data Maybe a = Nothing | Just a

Hľadanie na základe kľúča

lookup :: (Eq a) => a -> [(a,b)] -> Maybe b
lookup _ [] = Nothing
lookup key ((x,y):xys)
    | key == x  = Just y
    | otherwise = lookup key xys

Príklad

replace :: [(String, String)] -> String -> String
replace table = unwords . map (replaceWord table) . words

replaceWord table word =
    case lookup word table of
        Just replacement -> replacement
        Nothing -> word

replaceWord table word = fromMaybe word (lookup word table)

Either

data Either a b = Left a | Right b
pairOff :: Int -> Either String Int
pairOff people
    | people < 0  = Left "Can't pair off negative number of people."
    | people > 30 = Left "Too many people for this activity."
    | even people = Right (people `div` 2)
    | otherwise   = Left "Can't pair off an odd number of people."

Príklad — hľadanie indexu

elemIndex :: Eq a => a -> [a] -> Maybe Int
elemIndex el list = findIndex 0 list
    where
        findIndex _ [] = Nothing
        findIndex i (x:xs) 
            | x == el   = Just i
            | otherwise = findIndex (i+1) xs

Hľadanie indexu v štandardnej knižnici

elemIndex       :: Eq a => a -> [a] -> Maybe Int
elemIndex x      = findIndex (x==)
findIndex       :: (a -> Bool) -> [a] -> Maybe Int
findIndex p      = listToMaybe . findIndices p
findIndices     :: (a -> Bool) -> [a] -> [Int]
findIndices p xs = [ i | (x,i) <- zip xs [0..], p x]

Pokračovanie

listToMaybe :: [a] -> Maybe a
listToMaybe = foldr (const . Just) Nothing
const      :: a -> b -> a
const x _  =  x