Home
Mibori Sources [entries|archive|friends|userinfo]
Mibori Sources

[ userinfo | livejournal userinfo ]
[ archive | journal archive ]

Фунциональные зависимости (FunDeps) [Авг. 11, 2006|03:41 pm]

mibori
[Tags|, , ]

бывшая ссылка. За ссылку спасибо [info]lomeo.

Определим целочисленный вектор и матрицу на основе него:
data Vector = Vector Int Int deriving (Eq, Show)
data Matrix = Matrix Vector Vector deriving (Eq, Show)


Отнесем вектор к классу чисел Num:
instance Num Vector where
  Vector a1 b1 + Vector a2 b2 = Vector (a1 + a2) (b1 + b2)
  Vector a1 b1 - Vector a2 b2 = Vector (a1 - a2) (b1 - b2)
  -- и т. д.
и матрицу:
instance Num Matrix where
  Matrix a1 b1 + Matrix a2 b2 = Matrix (a1 + a2) (b1 + b2)
  Matrix a1 b1 - Matrix a2 b2 = Matrix (a1 - a2) (b1 - b2)
  -- и т. д.
Теперь Int, Vector и Matrix являются экземплярами класса Num.
Проблема возникает тогда, когда мы хотим выполнить умножение этих величин. При умножении тип результата различен и зависит от типа аргументов:
(*) :: Matrix -> Matrix -> Matrix -- умножение матрицы на матрицу = матрица
(*) :: Matrix -> Vector -> Vector -- умножение матрицу на вектор = вектор
(*) :: Matrix -> Int -> Matrix -- умножение матрицу на число = матрица
(*) :: Int -> Matrix -> Matrix -- умножение числа на матрицу = матрица

тогда, как мы имеем строгое (*) :: (Num a) => a -> a -> a

Мы можем попробывать разрешить это так:
Определить класс, в котором типы аргументов операции (*) разняться:
class Mult a b c where (*) :: a -> b -> c

И задать необходимые экземпляры:
instance Mult Matrix Matrix Matrix where ... -- умножение матрицы на матрицу
instance Mult Matrix Vector Vector where ... -- умножение матрицы на вектор
...


В этом случае в реальности мы получим не то, что хотели: для каждого результата x * y
нам придется указывать тип явно:
(m1 * m2) :: Matrix * m3 -- m1 * m2 имеет тип Matrix

в противном случае, тип выражения невозможно вывести на этапе трансляции:
(m1 * m2) * m3 -- type error; type of (m1*m2) is ambiguous
поскольку не известно какого типа m1 * m2, чтобы скомпилировать (* m3)

Для разрешения этой проблемы в определение класса вводится функциональная зависимость:
class Mult a b c | a b -> c where
  (*) :: a -> b -> c
Запись class Mult a b c | a b -> c where ... можно прочитать так:
Класс Mult из типов a b с, в котором комбинация экземпляров a b определяет экземпляр c, где ...

Это определение, вовсе не означает, что внутриклассовые определения экземпляров Mult должны каким-либо образом возвращать результ типа c. Оно означает, что при появлении в внутриклассовом определении типов a и b одновременно, определит тип c, независимо от структуры этого внутриклассового определения (пусть даже это будет не функция). Можно сказать, что комбинация a и b является ключем, который однозначно определит тип c в каждом мультиклассовом определении класса Mult.

Подробнее ...
ссылкаОставить комментарий

Простой ввод/вывод [Авг. 9, 2006|11:47 am]
k0l7gxb1kicxm
[Tags|, , , , , , , , , ]

  • putChar :: Char -> IO () -- выводит символ
    putStr :: String -> IO () -- выводит строку
    putStrLn :: String -> IO () -- выводит строку + символ перевода строки
    print :: Show a => a -> IO () -- выводит структуру данных типа a + символ перевода строки

  • getChar :: IO Char -- ввод символа
    getLine :: IO String -- Ввод строки
    getContents :: IO String -- лениво считывает весь поток ввода в строку до его окончания
    readLn :: Read a => IO a --> вводит константу типа a

  • interact :: (String -> String) -> IO () -- интерактивно, вводит/выводит строку
  • их определения... )
  • Пример. Нахождение корней квадратного трехчлена.
    main = do 
    	putStr "Ax + Bx + C = 0\n Input A:"; a <- readLn
    	putStr "Input B:"; b <- readLn
    	putStr "Input C:"; c <- readLn
    	let d = b ^ 2 + 4 * a * c
    	putStr "x1 = "; print ((b + sqrt d) / (2 * a))
    	putStr "x2 = "; print ((b - sqrt d) / (2 * a))
    	
    main2 = do
    	putStr "Ax + Bx + C = 0\n Input A:"; a <- readLn
    	putStr "Input B:"; b <- readLn
    	putStr "Input C:"; c <- readLn
    	let d = b ^ 2 + 4 * a * c; x f =  (b `f` (sqrt d)) / (2 * a)
    	putStr "x1 = "; print $ x (+)
    	putStr "x2 = "; print $ x (-)
ссылкаОставить комментарий

do-конструкция. Четыре правила трансляции. [Авг. 4, 2006|07:05 pm]
k0l7gxb1kicxm
[Tags|, , , , , ]

В YAHT поясняются четыре правила трансляции do-конструкции.
  • do e ==> e -- при этом выражение должно возвращать объект монады, иначе произойдет ошибка.

    do [1,2,3] --> [1,2,3]
    [1,2,3] --> [1,2,3] -- :)
    do Just 8 --> Just 8
  • Если es - это список выражений e1; e2; ...
    do e; es ==> e >> do es -- e и es вовзр. объекты монад (возможно разных типов внутренних объектов)

    do [1, 2]; [4, 5, 6] --> [4,5,6,4,5,6]
    [1, 2] >> do [4, 5, 6] --> [4,5,6,4,5,6]
  • В do-конструкции можно применить let-конструкцию без in-слова:
    do let {определения}; es ==> let {определения} in do {es}

    do let {x = 3; y = 4}; [x, y] --> [3,4]
  • do p <- e; es ==>
    	let
    		ok p = do es
    		ok _ = fail " текст пользовательского исключения "
    	in 
    		e >>= ok

    это же, но без учета fail, переписывается таким образом: do p <- e; es ==> e >>= (\p -> do es)

    Исключение может проявляться в таких определениях, как это:
    Prelude> do (’a’:’b’:’c’:x:xs) <- getLine; putStrLn (x:xs)
    abcMyau!
    Myau!
    Prelude> do (’a’:’b’:’c’:x:xs) <- getLine; putStrLn (x:xs)
    AbcMyau
    *** Exception: user error (Pattern match failure in do expression at :1:3-20)


    Переопределим это:
    (0) do (’a’:’b’:’c’:x:xs) <- getLine; putStrLn (x:xs)
    (1) getLine >>= \(’a’:’b’:’c’:x:xs) -> putStrLn (x: xs)


    Посмотрим, что ответит ghci:
    Prelude> getLine >>= \(’a’:’b’:’c’:x:xs) -> putStrLn (x: xs)
    abcMyau
    Myau
    Prelude> getLine >>= \(’a’:’b’:’c’:x:xs) -> putStrLn (x: xs)
    wedwe
    *** Exception: :1:12-50: Non-exhaustive patterns in lambda


    Именно поэтому последнее выражение нам не подходит и мы его заменяем на
    let ok (’a’:’b’:’c’:x:xs) = putStrLn (x: xs); ok _ = fail "My Error" in getLine >>= ok
    меняя лямбду на ok.

    Теперь проверим:
    Prelude> let ok (’a’:’b’:’c’:x:xs) = putStrLn (x: xs); ok _ = fail "My Error" in getLine >>= ok
    abcMyau
    Myau
    Prelude> let ok (’a’:’b’:’c’:x:xs) = putStrLn (x: xs); ok _ = fail "My Error" in getLine >>= ok
    asdasd
    *** Exception: user error (My Error)
    -- что и требовалось доказать!
Выражения, которые возвращают объекты типа m a, где m - монадический контейнер, называются действиями.
ссылкаОставить комментарий

do-конструкция [Авг. 4, 2006|04:16 pm]
k0l7gxb1kicxm
[Tags|, ]

do-конструкция является синтаксическим сахаром (другим синтаксическим представлением чего-либо с целью удобства в использовании) для операции связывания (>>=):
do 
	x <- m
	e x
Одно и тоже, что и m >>= \x -> e x
Где m - это выражение, возвращающее объект внутри монадического контейнера, a x - свободная переменная, которой, в императивной семантике, присваиваится объект из монадического контейнера возвращаемого m.

Примеры работы с монадой []

do x <- [3,4,-2]; return x --> [3,4,-2]
do x <- [3,4,-2]; [x] --> [3,4,-2] -- return :: a -> [a]
do x <- [3,4,-2]; [x + 3] --> [6,7,1]
do x <- [3,4,-2]; return (x + 3) --> [6,7,1]

do x <- [3,4,-2]; (\v -> [-2, -3, v + 3]) x --> [-2,-3,6,-2,-3,7,-2,-3,1]
do x <- [3,4,-2]; [x + 3, x - 2] --> [6,1,7,2,1,-4]

do x <- [3,4,-2]; if x == 3 then [0] else [x + 1, x + 2] --> [0,5,6,-1,0] -- даёт concat [[0], [5, 6, -1], [0]]

do x <- [1, 2]; y <- [x + 4]; return y --> [5,6]
do x <- [1, 2]; y <- [x + 4, 0, 0]; return y --> [5,0,0,6,0,0]
do x <- [1, 2]; y <- [x + 4, 0, 0]; return [y] --> [[5],[0],[0],[6],[0],[0]] -- может быть другого типа

Примеры работы с монадой Maybe

do x <- Just 8; return x --> Just 8
do x <- Just 3; Just x --> Just 3
do x <- Just 3; Nothing --> Nothing
do x <- Just 3; Just (Just x) --> Just (Just 3)
do x <- Just 3; return (Just x) --> Just (Just 3)
do x <- Just 3; Just (Just (x + 1)) --> Just (Just 4)
ссылкаОставить комментарий

О монадах. [Авг. 4, 2006|01:38 pm]
k0l7gxb1kicxm
[Tags|, , , , , , , ]

Монады - тип данных, принадлежащие двум классам:
class Monad m where
  (>>=) :: m a -> (a -> m b) -> m b
  (>>) :: m a -> m b -> m b
  return :: a -> m a
  fail :: String -> m a
class Functor f where fmap :: (a -> b) -> f a -> f b

Вместо какого-либо одного класса можно поставить наследника.
Например, вместо Monad - MonadPlus:
data m a = C1 ... | C2 ... | ... deriving (Monad, Functor)
data m a = C1 ... | C2 ... | ... deriving (MonadPlus, Functor)


Где m - будет монадой потому, что
class (Monad m) => MonadPlus m where
  mzero :: m a
  mplus :: m a -> m a -> m a
Рассмотрим [] и Maybe для (>>=), (>>) и return
instance Monad [] 
	(>>=) :: [a] -> (a -> [b]) -> [b]
	(>>) :: [a] -> [b] -> [b]
	return :: a -> [a]

(>>=) :: [a] -> (a -> [b]) -> [b]	
l >>= f = concatMap f l
	concatMap :: (a -> [b]) -> [a] -> [b]
	concatMap f l = concat (map f l)
	
	concat :: [[t]] -> [t] -- объединяет элементы списков в общий список.
	concat [] = []
	concat (xs: xss) = xs ++ concat xss
		-- concat [[1], [2,3], [3,4,7]] --> [1,2,3,3,4,7]
	
	map :: (a -> b) -> [a] -> [b] -- применяет к каждому элементу списка функцию,
	map _ [] = [] -- генерируя из результатов новый список
	map f (x: xs) = f x : map f xs
		-- map (show . (+ 3)) [1,2,3] --> ["4","5","6"]
-- [3, 5, 7] >>= (\x -> [x * 2, x ^ 2]) --> [6,9,10,25,14,49]

Для всех монад (>>) определено
(>>) :: m a -> m b -> m b
ma >> mb = ma >>= (\_ -> mb)
т. е.
(>>) :: [a] -> [b] -> [b]
	(1) la >> lb = la >>= (\_ -> lb)
	(2) la >> lb = concatMap (\_ -> lb) la
	(3) la >> lb = concat (map (\_ -> lb) la) -- для кажого элемента la вставляет весь lb
-- ['a', '4', '7'] >> [1, 10, 30, -5] --> [1,10,30,-5,1,10,30,-5,1,10,30,-5]
return :: a -> [a]
	return x = [x]
-- (return :: t -> [t]) "Hello!" --> ["Hello!"]

instance Monad Maybe
	(>>=) :: Maybe a -> (a -> Maybe b) -> Maybe b
	(>>) :: Maybe a -> Maybe b -> Maybe b
	return :: a -> Maybe a
(>>=) :: Maybe a -> (a -> Maybe b) -> Maybe b
	Nothing  >>= _ = Nothing
	(Just x) >>= f = f x
-- Nothing >>= (\x -> Just x) --> Nothing
-- Just Nothing >>= (\x -> Just x) --> Just Nothing
-- Just 123 >>= (\x -> Just x) --> Just 123
(>>) :: Maybe a -> Maybe b -> Maybe b -- ma >> mb = ma >>= (\_ -> mb)
	Nothing >> _ = Nothing
	Just a >> x = x
-- Just 3 >> Just "c" --> Just "c"
-- Nothing >> Just "c" --> Nothing
-- Just 3 >> Nothing --> Nothing
return :: a -> Maybe a
	return = Just
-- (return :: t -> Maybe t) 123 --> Just 123
ссылкаОставить комментарий

navigation
[ viewing | most recent entries ]