Опубликован: 19.09.2008 | Доступ: свободный | Студентов: 658 / 70 | Оценка: 4.50 / 5.00 | Длительность: 21:25:00
Лекция 9:

Стандартное начало (Prelude)

8.1. Prelude PreludeList

- Стандартные функции над списками

module PreludeList (
    map, (++), filter, concat, concatMap, 
    head, last, tail, init, null, length, (!!), 
    foldl, foldl1, scanl, scanl1, foldr, foldr1, scanr, scanr1,
    iterate, repeat, replicate, cycle,
    take, drop, splitAt, takeWhile, dropWhile, span, break,
    lines, words, unlines, unwords, reverse, and, or,
    any, all, elem, notElem, lookup,
    sum, product, maximum, minimum, 
    zip, zip3, zipWith, zipWith3, unzip, unzip3)
  where

import qualified Char(isSpace)

infixl 9  !!
infixr 5  ++
infix  4  `elem`, `notElem`

- Отображение (map) и добавление в конец (append)

map :: (a -> b) -> [a] -> [b]
map f []     = []
map f (x:xs) = f x : map f xs


(++) :: [a] -> [a] -> [a]
[]     ++ ys = ys
(x:xs) ++ ys = x : (xs ++ ys)


filter :: (a -> Bool) -> [a] -> [a]
filter p []                 = []
filter p (x:xs) | p x       = x : filter p xs
                | otherwise = filter p xs


concat :: [[a]] -> [a]
concat xss = foldr (++) [] xss


concatMap :: (a -> [b]) -> [a] -> [b]
concatMap f = concat . map f

- head и tail извлекают соответственно первый и последний элементы - конечного списка. last и init являются - двойственными функциями, которые выполняются с конца конечного списка, - а не с начала.

head             :: [a] -> a
head (x:_)       =  x
head []          =  error "Prelude.head: пустой список"


tail             :: [a] -> [a]
tail (_:xs)      =  xs
tail []          =  error "Prelude.tail: пустой список"


last             :: [a] -> a
last [x]         =  x
last (_:xs)      =  last xs
last []          =  error "Prelude.last: пустой список"


init             :: [a] -> [a]
init [x]         =  []
init (x:xs)      =  x : init xs
init []          =  error "Prelude.init: пустой список"


null             :: [a] -> Bool
null []          =  True
null (_:_)       =  False

- length возвращает длину конечного списка в виде Int.

length           :: [a] -> Int
length []        =  0
length (_:l)     =  1 + length l

- Оператор доступа к элементам списка по индексу, начало - в 0.

(!!)                :: [a] -> Int -> a
xs     !! n | n < 0 =  error "Prelude.!!: отрицательный индекс"
[]     !! _         =  error "Prelude.!!: слишком большой индекс"
(x:_)  !! 0         =  x
(_:xs) !! n         =  xs !! (n-1)

foldl, будучи примененной к своим аргументам: бинарному оператору, начальному значению (обычно - левому аргументу из тождества оператора) и списку, сокращает список, используя - бинарный оператор слева направо:

-  foldl f z [x1, x2, ..., xn] == (...((z `f` x1) `f` x2) `f`...) `f` xn

foldl1 является вариантом предыдущей функции, она не имеет аргумента с начальным значением, и поэтому должна - применяться к непустым спискам. scanl похожа на foldl, но возвращает - список успешно сокращенных значений слева:

-      scanl f z [x1, x2, ...] == [z, z `f` x1, (z `f` x1) `f` x2, ...]

- Обратите внимание, что last (scanl f z xs) == foldl f z xs. - scanl1 похожа на предыдущую функцию, но без начального элемента:

-      scanl1 f [x1, x2, ...] == [x1, x1 `f` x2, ...]


foldl            :: (a -> b -> a) -> a -> [b] -> a
foldl f z []     =  z
foldl f z (x:xs) =  foldl f (f z x) xs


foldl1           :: (a -> a -> a) -> [a] -> a
foldl1 f (x:xs)  =  foldl f x xs
foldl1 _ []      =  error "Prelude.foldl1: пустой список"


scanl            :: (a -> b -> a) -> a -> [b] -> [a]
scanl f q xs     =  q : (case xs of
                            []   -> []
                            x:xs -> scanl f (f q x) xs)


scanl1           :: (a -> a -> a) -> [a] -> [a]
scanl1 f (x:xs)  =  scanl f x xs
scanl1 _ []      =  []

- foldr, foldr1, scanr и scanr1 являются двойственными дополнениями описанных выше функций; - они действуют справа налево.

foldr            :: (a -> b -> b) -> b -> [a] -> b
foldr f z []     =  z
foldr f z (x:xs) =  f x (foldr f z xs)


foldr1           :: (a -> a -> a) -> [a] -> a
foldr1 f [x]     =  x
foldr1 f (x:xs)  =  f x (foldr1 f xs)
foldr1 _ []      =  error "Prelude.foldr1: пустой список"


scanr             :: (a -> b -> b) -> b -> [a] -> [b]
scanr f q0 []     =  [q0]
scanr f q0 (x:xs) =  f x q : qs
                     where qs@(q:_) = scanr f q0 xs 


scanr1          :: (a -> a -> a) -> [a] -> [a]
scanr1 f []     =  []
scanr1 f [x]    =  [x]
scanr1 f (x:xs) =  f x q : qs
                   where qs@(q:_) = scanr1 f xs

- iterate f x возвращает бесконечный список повторных применений f к x:

- iterate f x == [x, f x, f (f x), ...]

iterate          :: (a -> a) -> a -> [a]
iterate f x      =  x : iterate f (f x)

- repeat x представляет собой бесконечный список, где каждый элемент равен x.

repeat           :: a -> [a]
repeat x         =  xs where xs = x:xs

- replicate n x представляет собой список длины n, где каждый элемент равен x.

replicate        :: Int -> a -> [a]
replicate n x    =  take n (repeat x)

- cycle связывает конечный список в круговой или, что то же самое, - бесконечно повторяет исходный список. Он идентичен - бесконечным спискам.

cycle            :: [a] -> [a]
cycle []         =  error "Prelude.cycle: пустой список"
cycle xs         =  xs' where xs' = xs ++ xs'

- take n, будучи примененной к списку xs, возвращает префикс xs длины n, - или сам xs, если n > length xs. drop n xs возвращает суффикс xs - после первых n элементов, или [], если n > length xs. splitAt n xs - эквивалентна (take n xs, drop n xs).

take                   :: Int -> [a] -> [a]
take n _      | n <= 0 =  []
take _ []              =  []
take n (x:xs)          =  x : take (n-1) xs


drop                   :: Int -> [a] -> [a]
drop n xs     | n <= 0 =  xs
drop _ []              =  []
drop n (_:xs)          =  drop (n-1) xs


splitAt                  :: Int -> [a] -> ([a],[a])
splitAt n xs             =  (take n xs, drop n xs)

- akeWhile, будучи примененной к предикату p и списку xs, возвращает самый длинный - префикс (возможно пустой) xs, элементы которого удовлетворяют p. dropWhile p xs - возвращает оставшийся суффикс. span p xs эквивалентна - (takeWhile p xs, dropWhile p xs), тогда как break p использует отрицание p.

takeWhile               :: (a -> Bool) -> [a] -> [a]
takeWhile p []          =  []
takeWhile p (x:xs) 
            | p x       =  x : takeWhile p xs
            | otherwise =  []


dropWhile               :: (a -> Bool) -> [a] -> [a]
dropWhile p []          =  []
dropWhile p xs@(x:xs')
            | p x       =  dropWhile p xs'
            | otherwise =  xs


span, break             :: (a -> Bool) -> [a] -> ([a],[a])
span p []            = ([],[])
span p xs@(x:xs') 
            | p x       =  (x:ys,zs) 
            | otherwise =  ([],xs)
                           where (ys,zs) = span p xs'

break p                 =  span (not . p)

- lines разбивает строку в местах символов новой строки на список строк. - Полученные строки не содержат символов новой строки. Аналогично, words - разбивает строку на список строк в местах пробельных символов. - unlines и unwords являются обратными операциями. - unlines соединяет строки, добавляя в конец каждой символ новой строки, а unwords соединяет - слова, отделяя их друг от друга пробелами.

lines            :: String -> [String]
lines ""         =  []
lines s          =  let (l, s') = break (== '\n') s
                      in  l : case s' of
                                []      -> []
                                (_:s'') -> lines s''


words            :: String -> [String]
words s          =  case dropWhile Char.isSpace s of
                      "" -> []
                      s' -> w : words s''
                            where (w, s'') = break Char.isSpace s'


unlines          :: [String] -> String
unlines          =  concatMap (++ "\n")


unwords          :: [String] -> String
unwords []       =  ""
unwords ws       =  foldr1 (\w s -> w ++ ' ':s) ws

- reverse xs возвращает элементы списка xs в обратном порядке. Список xs должен быть конечным.

reverse          :: [a] -> [a]
reverse          =  foldl (flip (:)) []

- and возвращает конъюнкцию списка булевых значений. Для того чтобы результат - был истиной, список должен быть конечным; False является результатом наличия значения False - в элементе с конечным индексом конечного или бесконечного списка. or является двойственной к and функцией, - в ней используется дизъюнкция.

and, or          :: [Bool] -> Bool
and              =  foldr (&&) True
or               =  foldr (||) False

- Будучи примененной к предикату и списку, any определяет, есть ли хотя бы один элемент - списка, который удовлетворяет предикату. Аналогично all определяет, все ли элементы списка удовлетворяет предикату.

any, all         :: (a -> Bool) -> [a] -> Bool
any p            =  or . map p
all p            =  and . map p

- elem является предикатом, который определяет, является ли аргумент элементом списка; обычно он записывается в инфиксной форме, - например, x 'elem' xs. notElem является отрицанием предыдущей функции.

elem, notElem    :: (Eq a) => a -> [a] -> Bool
elem x           =  any (== x)
notElem x        =  all (/= x)

- lookup key assoc ищет ключ в ассоциативном списке.

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

- sum и product вычисляют соответственно сумму и произведение чисел из конечного списка.

sum, product     :: (Num a) => [a] -> a
sum              =  foldl (+) 0  
product          =  foldl (*) 1

- maximum и minimum возвращают соответственно максимальное и минимальное значение из списка, - который должен быть непуст, конечен и содержать элементы, которые можно упорядочить.

maximum, minimum :: (Ord a) => [a] -> a
maximum []       =  error "Prelude.maximum: пустой список"
maximum xs       =  foldl1 max xs

minimum []       =  error "Prelude.minimum: пустой список"
minimum xs       =  foldl1 min xs

- zip принимает в качестве аргументов два списка и возвращает список соответствующих пар. Если один из - входных списков короче, дополнительные элементы более длинного списка игнорируются. - zip3 принимает в качестве аргументов три списка и возвращает список кортежей размера 3. Функции zip для более длинных кортежей - находятся в библиотеке List.

zip              :: [a] -> [b] -> [(a,b)]
zip              =  zipWith (,)


zip3             :: [a] -> [b] -> [c] -> [(a,b,c)]
zip3             =  zipWith3 (,,)

- Семейство функций zipWith является обобщением семейства функций zip; они упаковывают элементы списков с помощью - функции, заданной в качестве первого аргумента, вместо функции создания кортежей. - Например, zipWith (+), будучи примененной к двум спискам, порождает список - соответствующих сумм.

zipWith          :: (a->b->c) -> [a]->[b]->[c]
zipWith z (a:as) (b:bs)
                 =  z a b : zipWith z as bs
zipWith _ _ _    =  []


zipWith3         :: (a->b->c->d) -> [a]->[b]->[c]->[d]
zipWith3 z (a:as) (b:bs) (c:cs)
                 =  z a b c : zipWith3 z as bs cs
zipWith3 _ _ _ _ =  []

- unzip преобразует список пар в пару списков.

unzip            :: [(a,b)] -> ([a],[b])
unzip            =  foldr (\(a,b) ~(as,bs) -> (a:as,b:bs)) ([],[])


unzip3           :: [(a,b,c)] -> ([a],[b],[c])
unzip3           =  foldr (\(a,b,c) ~(as,bs,cs) -> (a:as,b:bs,c:cs))
                          ([],[],[])