осень 2026
осень 2026
Летучка 7 · 7 минут · без конспектов · Ваше ФИ: ____________________
Maybe: __________foldr (-) 0 [1, 2]: ______(++) на списках: ______Напишите полные инстансы Semigroup и Monoid для списка в обёртке Batch. Объединение сохраняет порядок всех элементов.
Напишите полные инстансы Semigroup и Monoid для статистики тестов. При объединении блоков складываются число ошибок и общее число тестов.
Напишите полные инстансы Semigroup и Monoid для своего варианта Maybe. Missing означает отсутствие результата; два Present объединяют содержащиеся значения. Выберите необходимые ограничения на a.
Напишите stats :: [Bool] -> Counts, используя готовый инстанс: True — успешный тест, False — ошибка. Для [True, False, True] получите Counts 1 3, для пустого списка — Counts 0 0. Обойдитесь без явной рекурсии; пригодятся map и mconcat.
Расставьте скобки и вычислите foldr (-) 0 [1,2,3] и foldl (-) 0 [1,2,3].
Список [1,2,3] — это 1 : (2 : (3 : [])).
foldr (-) 0 [1,2,3]
Скобки
В каждом (:) два аргумента: элемент и хвост.
Вместо (:) ставим (-), вместо [] — 0.
1 - (2 - (3 - 0))
Скобки
В каждом (:) два аргумента: элемент и хвост.
Шаг получает элемент и результат свёртки хвоста.
Для этого выражения арифметика требует результат хвоста.
1 - (2 - (3 - 0))
Скобки
В каждом (:) два аргумента: элемент и хвост.
Шаг получает элемент и результат свёртки хвоста.
Заменяем только выделенное поддерево его результатом.
1 - (2 - 3)
Скобки
В каждом (:) два аргумента: элемент и хвост.
Шаг получает элемент и результат свёртки хвоста.
3 - 0 = 3.
Следующая операция — 2 - 3.
1 - (2 - 3)
Скобки
В каждом (:) два аргумента: элемент и хвост.
Шаг получает элемент и результат свёртки хвоста.
3 - 0 = 3.
Полученное -1 становится правым аргументом внешнего вычитания.
1 - (-1)
Скобки
В каждом (:) два аргумента: элемент и хвост.
Шаг получает элемент и результат свёртки хвоста.
3 - 0 = 3.
2 - 3 = -1.
Осталось 1 - (-1).
1 - (-1)
Скобки
В каждом (:) два аргумента: элемент и хвост.
Шаг получает элемент и результат свёртки хвоста.
3 - 0 = 3.
2 - 3 = -1.
Возвращаем результат всей свёртки.
2
Скобки
В каждом (:) два аргумента: элемент и хвост.
Шаг получает элемент и результат свёртки хвоста.
3 - 0 = 3.
2 - 3 = -1.
1 - (-1) = 2.
Правая свёртка: элемент слева, свёрнутый хвост справа.
foldr (-) 0 [1,2,3] = 1 - (2 - (3 - 0))
Скобки
1 - (2 - (3 - 0)) = 2.
Левая свёртка: накопленный результат слева, следующий элемент справа.
foldl (-) 0 [1,2,3] = ((0 - 1) - 2) - 3
Скобки
1 - (2 - (3 - 0)) = 2.
((0 - 1) - 2) - 3 = -6.
Каждый шаг получает накопленный результат и следующий элемент.
foldl (-) 0 [1,2,3]
Состояния
Начинаем с 0.
Результат шага становится аккумулятором следующего.
foldl (-) (0 - 1) [2,3]
Состояния
Начинаем с 0.
0 - 1 = -1.
Скобки накапливаются слева.
foldl (-) ((0 - 1) - 2) [3]
Состояния
Начинаем с 0.
0 - 1 = -1.
(-1) - 2 = -3.
Дошли до []: возвращаем аккумулятор.
((0 - 1) - 2) - 3 = -6
Состояния
Начинаем с 0.
0 - 1 = -1.
(-1) - 2 = -3.
(-3) - 3 = -6.
Например, prod [1,2,3] [4,5,6] == 32.
Реализуйте скалярное произведение через zipWith и foldr. При разной длине списков используйте общий префикс.
Как с map в прошлой практике: операция одна, формы коллекций разные.
data Pair a = Pair a a
foldList :: (a -> b -> b) -> b -> [a] -> b
foldPair :: (a -> b -> b) -> b -> Pair a -> b
foldMaybe :: (a -> b -> b) -> b -> Maybe a -> b
Хотим одно имя foldr; тип коллекции выбирает реализацию.
data Pair a = Pair a a
foldr :: (a -> b -> b) -> b -> [a] -> b
foldr :: (a -> b -> b) -> b -> Pair a -> b
foldr :: (a -> b -> b) -> b -> Maybe a -> b
Foldable перегружает свёртку по конструктору типа t.
class Foldable t where
foldr :: (a -> b -> b) -> b -> t a -> b
foldMap :: Monoid m => (a -> m) -> t a -> m
Применяем f к каждому элементу и объединяем результаты через (<>). Для пустой коллекции получаем mempty.
Что вернёт foldMap (\x -> [x,x]) (Pair 1 2)?
[1,1] <> [2,2] == [1,1,2,2].
Завершите foldMapList через foldr. Шаг объединяет f x с результатом свёртки хвоста.
В каждом Node один элемент. Leaf — пустое дерево.
Как с помощью foldMap получить список элементов дерева?
Порядок выберем такой: корень, левое поддерево целиком, затем правое.
Напишите полный инстанс Foldable Tree: заголовок и метод foldMap. Порядок — корень, левое поддерево целиком, затем правое; Leaf пуст. Не используйте deriving Foldable.
Применим написанный инстанс к нашему дереву.
foldMap (\x -> [x]) sampleTree
По написанному инстансу
f x = [x]: результатом будет список.
Каждый элемент даёт одноэлементный список; пустое дерево даёт [].
f x = [x]; mempty = []
По написанному инстансу
f x = [x]: результатом будет список.
В узле: f x <> foldMap f left <> foldMap f right.
Объединяем результаты внутри левого и правого поддеревьев.
[1] <> ([2] <> [4] <> []) <> [3]
По написанному инстансу
f x = [x]: результатом будет список.
В узле: f x <> foldMap f left <> foldMap f right.
Слева — [2,4], справа — [3].
В корне соединяем его список с результатами обоих поддеревьев.
[1] <> [2,4] <> [3] = [1,2,4,3]
По написанному инстансу
f x = [x]: результатом будет список.
В узле: f x <> foldMap f left <> foldMap f right.
Слева — [2,4], справа — [3].
Для списков (<>) — это (++).
Преобразовать элементы → отобрать нужные → собрать результат.
import Data.Function ((&))
data Student = Student String [Double]
average :: [Double] -> Double
awarded :: [Student] -> Map String Stringx & f = f x: передаём данные следующей функции.
average дана: среднее оценок, для пустого списка — 0.
Вход — список студентов и их оценок.
[Student][(String, Double)][(String, Double)]Map String Stringmap преобразует каждого студента в пару имени и средней оценки.
[Student][(String, Double)][(String, Double)]Map String Stringfilter оставляет пары со средней оценкой строго больше 3.
[Student][(String, Double)][(String, Double)]Map String StringfoldMap собирает словарь наград из одноэлементных словарей.
[Student][(String, Double)][(String, Double)]Map String Stringxs = [1..] :: [Integer] задаёт бесконечный список; пока никто не запросил его элементы.
xs
Что потребовалось
Вычисление списка можно отложить.
Чтобы напечатать take 1 xs, нужны первый (:) и его элемент. Хвост не нужен.
take 1 xs = [1]
Что потребовалось
Вычисление списка можно отложить.
Получили [1]; остальной список не потребован.
Учебное определение длины списка:
Что вернёт length ys? Нужно ли вычислять элементы ys, каждый из которых — бесконечный список?
ys = [repeat 1, repeat 2] — конечный список двух бесконечных списков.
length ys
Что потребовалось
Запрашиваем первый конструктор.
Первый (:) добавляет единицу. Элемент repeat 1 не потребован.
1 + length [repeat 2]
Что потребовалось
Запрашиваем первый конструктор.
Первый (:); продолжаем по хвосту.
Второй (:) добавляет ещё единицу. Элемент repeat 2 тоже не потребован.
1 + (1 + length [])
Что потребовалось
Запрашиваем первый конструктор.
Первый (:); продолжаем по хвосту.
Второй (:); запрашиваем конец списка.
Дошли до []: результат 2. Внутренние бесконечные списки не вычислялись.
1 + (1 + 0) = 2
Что потребовалось
Запрашиваем первый конструктор.
Первый (:); продолжаем по хвосту.
Второй (:); запрашиваем конец списка.
[]: останавливаемся, длина равна 2.
length [undefined, undefined] -- 2
length (1 : undefined) -- ошибка
length ([1..] :: [Integer]) -- не завершитсяlength требует все внешние (:) и конечный [], но не значения элементов.
Планируем пять ближайших рассылок по будням, начиная с 23 октября 2026 года.
Haskell: ленивый список
Даны start :: Day и isWeekday, исключающий субботу и воскресенье. addDays — из Data.Time.
Получим 23, 26, 27, 28 и 29 октября. Проверим семь дат; дальнейший календарь не потребуется.
foldr (&&) True (True : False : undefined)
True && (False && foldr (&&) True undefined)
Что требуется
True && rest требует rest.
False && rest возвращает False, не вычисляя rest.
True && False
Что требуется
True && rest требует rest.
Первый аргумент False определяет результат.
Результат — False. Ошибочный хвост остался невостребованным.
False
Что требуется
True && rest требует rest.
Первый аргумент False определяет результат.
foldr завершился на конечном префиксе.
Суммируем [1,2,3]: оба результата равны 6.
foldl (+) 0 [1,2,3] / foldl' (+) 0 [1,2,3]
Различие
Хвостовая рекурсия сама по себе не форсирует аккумулятор.
Без принуждения выражение может расти вместе со списком.
foldl (+) 0 [1,2,3] / foldl' (+) 0 [1,2,3]
Различие
Хвостовая рекурсия сама по себе не форсирует аккумулятор.
Для конечного числового накопления обычно берём foldl'.
WHNF не означает вычислить все поля вложенной структуры.
| Задача | Выбор | Что требуется |
|---|---|---|
| Найти ответ в конечном префиксе бесконечного списка | foldr с шагом, который может не потребовать rest |
Только нужный префикс |
| Построить ленивый выходной список по частям | foldr с шагом, который выдаёт очередной конструктор |
Следующая часть входа по потребности |
| Накопить числовой результат по всему конечному списку | foldl' из Data.List |
Весь список; новый аккумулятор принуждается |
Обе левые свёртки — foldl и foldl' — возвращают итоговый аккумулятор только после [].
Укажите результат, «ошибка» или «не завершится». xs = True : False : undefined, foldl' — из Data.List.
Semigroup и Monoid и использовать их для объединения результатовFoldable для своей коллекцииmap, filter и foldMapfoldr, foldl и foldl' по задачеfoldlStrict :: (b -> a -> b) -> b -> [a] -> b
foldlStrict _ acc [] = acc
foldlStrict f acc (x:xs) =
let acc' = f acc x
in acc' `seq` foldlStrict f acc' xsВ программе используем Data.List.foldl'.
Напишите полные инстансы Semigroup и Monoid для Func: функции получают один аргумент и объединяют результаты. Выберите ограничения на a и b.
data Nat = Z | S Nat
foldNat :: r -> (r -> r) -> Nat -> r
foldNat z _ Z = z
foldNat z s (S n) = s (foldNat z s n)На место каждого конструктора ставим обработчик: для Z — значение, для S — функцию.
snd (foldl' (\_ x -> (undefined, x)) (0,0) [1..3])
-- 3: конструктор пары вычислен, первое поле не потребованоСтрогость до WHNF не делает все поля структуры строгими.
Одно дерево: DFS [1,2,4,3], BFS [1,2,3,4].
Практика 7. Свёртки