Основы функционального программирования/Haskell/Служебные слова и синтаксис: различия между версиями
Содержимое удалено Содержимое добавлено
м Шаблон со списком лекций |
Ramir (обсуждение | вклад) Нет описания правки |
||
Строка 1:
{{ОФП Содержание}}
= Лекция 5.
== Охрана и локальные переменные ==
<code>sign x | x > 0 = 1
| x == 0 = 0
| x < 0 = -1</code>
Функция <code>sign
Для того, чтобы облегчить написание программ
<code>Function X1 X2 ... Xk = case (X1, X2, ..., Xk) of
(P11, P21, ..., Pk1) Expression1
...
(P1n, P2n, ..., Pkn) Expressionn</code>
Так функция,
<code>takeFirst n l = case (n, l) of (0, _) -> []
(_, []) -> []
(n, (x:xs)) -> (x) : (takeFirst (n – 1) xs)</code>
И такая запись будет полностью эквивалентна обычному определению функции:
<code>takeFirst 0 _ = []
takeFirst _ [] = []
takeFirst n (x:xs) = (x) : (takeFirst (n – 1) xs)</code>
Пришло время объяснить понятие '''маски подстановки'''. В
<code>if Exp1 then Exp2 else Exp3</code>▼
есть сокращение для
▲ if Exp1 then Exp2 else Exp3
<code>case (Exp1) of (True) -> Exp2▼
(False) -> Exp3</code>▼
Естественно, что тип <code>Exp1</code> должен быть
Для использования локальных переменных (в смысле функциональной парадигмы программирования) в
▲ case (Exp1) of (True) -> Exp2
▲ (False) -> Exp3
<code>let y = a * b▼
▲Естественно, что тип Exp1 должен быть Boolean (Bool в Haskell’е), а типы выражений Exp2 и Exp3 совпадать (ведь именно значения этих выражений будет возвращено определяемой через конструкцию «if-then-else» функцией).
▲Для использования локальных переменных (в смысле функциональной парадигмы программирования) в Haskell’е существует два типа записи. Первый тип полностью соответствует математической нотации, введенной в третьей лекции:
▲ let y = a * b
f x = (x + y) / y
in f c + f d</code>
Другим способом определения локальной переменной является её описание после использования. В этом случае используется служебное слово «where», которое ставится в конце выражения:▼
▲
<code>f x y | y > z = y – z
| y == z = 0
| y < z = z – y
where z = x * x</code>
== Полиморфизм ==
В первой лекции уже было показано, что функциональная парадигма программирования поддерживает чистый или параметрический [[w:полиморфизм|полиморфизм]]. Однако большинство современных языков
*Литералы 1, 2, 3 и т.д. (т.е. цифры) используются
*Арифметические операции (например, сложение:
*Оператор сравнения (
Перегруженное поведение операций
Рассмотреть возможность использования полиморфизма ad hoc проще всего на примере операции сравнения.
Рассмотрим функцию <code>element</code>, которая возвращает значение истины
<code>x `element` []
x `element` (y:ys)
Здесь видно, что у функции <code>element</code> тип <code>(a [a] Bool)</code>, но при этом тип операции
Классы типов
<code>class Eq a where
(==) :: a -> a -> Bool</code>
В этой конструкции использованы служебные слова
Ограничение того факта, что тип a должен быть экземпляром класса <code>Eq</code> записывается как <code>(Eq a)</code>. Поэтому выражение <code>(Eq a)</code> не является описанием типа, но
<code>(==) :: (Eq a) => a -> a -> Bool</code>▼
▲ (==) :: (Eq a) => a -> a -> Bool
<code>element :: (Eq a) => a -> [a] -> Bool</code>▼
Этой записью декларируется тот
▲Эта запись может быть прочитана как «Для каждого типа a, который является экземпляром класса Eq, определена операция « == », которая имеет тип (a a Bool)». Эта операция должна быть использована в функции element, поэтому ограничение распространяется и на неё. В этом случае необходимо явно указывать тип функции:
▲ element :: (Eq a) => a -> [a] -> Bool
x == y = x `integerEq` y</code>▼
В этом выражении определение операции
▲Этой записью декларируется тот необходимый факт, что функция element определена не для всех типов данных, но только для тех, для которых определена соответствующая операция сравнения.
Таким же образом можно определить операцию сравнения и для бесконечных рекурсивных типов. Например, для типа <code>Tree</code> (см. [[Основы функционального программирования/Основы языка Haskell|лекцию 4]]) определение будет выглядеть
▲Однако теперь возникает проблема определения того, какие типы являются экземплярами класса Eq. Для этого в Haskell’е есть служебное слово «instance». Например, для того, чтобы предписать, что тип Integer является экземпляром класса Eq, необходимо написать:
<code>instance (Eq
▲ x == y = x `integerEq` y
▲В этом выражении определение операции « == » называется определением метода (как в объектно-ориентированной парадигме). Функция integerEq может быть любой (и не обязательно инфиксной), главное, чтобы у нее был тип (a a Bool). В этом случае скорее всего подойдет примитивная функция сравнения двух натуральных чисел. В свою очередь прочитать написанное выражение можно следующим образом: «Тип Integer является экземпляром класса Eq, и далее приводится определение метода, который производит сравнение двух объектов типа Integer».
▲Таким же образом можно определить операцию сравнения и для бесконечных рекурсивных типов. Например, для типа Tree (см. лекцию 4) определение будет выглядеть следующим образом:
▲ instance (Eq a) => Eq (Tree a) where
Leaf a == Leaf b = a == b
(Branch l1 r1) == (Branch l2 r2) = (l1 == l2) && (r1 == r2)
_ == _ = False</code>
Естественно, что если в языке определено понятие класса, должно быть определена и концепция наследования. Хотя в Haskell’е под классом понимается нечто более абстрактное, чем в обычных объектно-ориентированных языках, в Haskell’е также есть и наследование. Но в то же время концепция наследования определяется столь же изощренно, что и понятие класса. Например, от определённого выше класса Eq можно унаследовать класс Ord, который будет представлять сравнимые типы данных. Его определение будет выглядеть следующим образом:▼
▲Естественно, что если в языке определено понятие класса, должно быть определена и концепция наследования. Хотя в
<code>class (Eq a) => Ord a where
(<), (>), (<=), (>=) :: a -> a -> Bool
min, max :: a -> a -> a</code>
Все экземпляры класса <code>Ord</code> должны определять кроме операций «меньше», «больше», «меньше или равно», «больше или равно», «минимум» и «максимум» ещё и операцию сравнения
Самое
== Сравнение с другими языками ==
Хотя классы существуют во многих других языках программирования, понятие класса в
*
*Определения методов в
*Больше всего хаскельские классы
*Haskell не поддерживает стиль перегрузки функции, используемый в C++, когда функции с одним и тем же именем получают данные различных типов для обработки.
*Типы объектов в
*C++ и Java добавляют в [[w:компиляция|скомпилированный]] код идентифицирующую информацию (например, таблицы размещения виртуальных функций). В
*Не существует понятия контроля за доступом
== Упражнения ==
=== Ответы ===
1.
▲#Все нижеприведённые описания на Haskell’е являются лишь одними из большого ряда возможных:
getN :: [a] -> a
getN n [] = _
getN 1 (h:t) = h
getN n (h:t) = getN (n – 1) t
listSumm :: Ord (a) => [a] -> [a]
listSumm [] l = l
listSumm l [] = l
listSumm (h1:t1) (h2:t2) = (h1 + h2) : (listSumm t1 t2)
oddEven :: [a] -> [a]
oddEven [] = []
oddEven [x] = [x]
oddEven (h1:(h2:t)) = (h2:h1) : (oddEven t)
append :: [a] -> [a] -> [a]
append [] l = l
Строка 188 ⟶ 182 :
reverse [] = []
reverse (h:t) = append (reverse t [h])
map :: (a -> b) -> [a] -> [b]
map f [] = []
map f (h:t) = (f h) : (map f t)
2.
atom :: ListStr (a) -> Bool
atom a = True
Строка 202 ⟶ 196 :
reverseAll [] = []
reverseAll (h:t) = append (reverseAll t) (reverseAll h)
position :: a -> [a] -> Integer
position a l = positionN a l 0
Строка 210 ⟶ 204 :
positionN a (h:t) n = positionN a t (n + 1)
positionN a [] n = 0
set :: [a] -> [a]
set [] = []
Строка 219 ⟶ 213 :
include a (a:t) = a : t
include a (b:t) = b : (include a t)
frequency :: [a] -> [(a : Integer)]
frequency l = f [] l
Строка 231 ⟶ 225 :
corrector a (a:n):t = (a : (n + 1)) : t
corrector a h:t = h : (corrector a t)
3.
class Show a where
show :: a -> a
class Number a where
(+) :: a -> a -> a
Строка 241 ⟶ 235 :
(*) :: a -> a -> a
(/) :: a -> a -> a
class String a where
(++) :: a -> a -> a
length :: a -> Integer
4.
instance Number Integer where
x + y = plusInteger x y
Строка 257 ⟶ 251 :
...
instance Number Real where
x + y = plusReal x y
...
instance Number Complex where
x + y = plusComplex x y
...
instance String WideString where
x ++ y = wideConcat x y
|