Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
MakeevGA-Haskell-a4-shortcode_2014_05_31.doc
Скачиваний:
15
Добавлен:
19.01.2023
Размер:
1.79 Mб
Скачать

Функция foldr

И третий пример, третий набор функций. Потратьте время, разберитесь, что делает каждая из этих функций:

foo [] = 0

foo (x:xs) = x + foo xs

boo [] = 1

boo (x:xs) = x * boo xs

goo [] = True

goo (x:xs) = x && goo xs

moo [] = []

moo (x:xs) = x ++ moo xs

hoo [] acc = acc

hoo (x:xs) acc | x < acc = hoo xs x

| otherwise = hoo xs acc

doo [] acc = acc

doo (x:xs) acc | x > 0 = doo xs (acc+1)

| otherwise = doo xs acc

zoo [] acc = acc

zoo (x:xs) acc = zoo xs (x:acc)

Я специально выбрал аж семь функций для того, чтобы дать вам побольше материала для анализа. Вы, конечно, уже понимаете, что требуется: найти в этих всех функциях общий шаблон поведения, общую абстракцию, общую идею – найти и формализовать в виде еще одной функции высших порядков.

Что делает каждая из этих функций? Первая находит сумму списка (то есть, другими словами, это функция sum), вторая – произведение (product), третья результат логического "и" всего списка (and), четвертая – конкатенация всех подсписков (concat). Пятая функция находит минимальное число из списка (minimum), шестая находит количество положительных элементов, а седьмая просто переворачивает список (reverse).

А что делают все эти функции? Опять-таки, они работают со списками, и обрабатывают элементы списка по одному, пока список не кончится. Первые четыре функции, вроде как, больше похожи друг на друга. Рассмотрим их пока отдельно.

Они "прибавляют" очередной элемент к результату вызова той же функции от хвоста списка, и отличаются смыслом этого "прибавляют" – это, соответственно: бинарное сложение, бинарное умножение, бинарное логическое "и" и бинарная конкатенация списков.

Очевидно, что именно эта отличающаяся операция, назовем ее f, и будет являться параметром новой функции высшего порядка. Вторым отличием первых четырех функций является то, что они возвращают при пустом списке. Это будет второй параметр функции, назовем его – нулевой элемент z.

Если выписать все элементы списка и посмотреть, как к ним применяется эта операция, то окажется, что результат формируется из вычисления вот такого выражения:

x1 f (x2 f (x3 f … (xN f z)

На каждом шаге работы функции бинарная функция f применяется к очередному элементу и результату вызова той же самой функции от хвоста списка. Математики такую операцию называют сверткой (причем правой сверткой). Давайте, наконец, напишем функцию:

foldr f z [] = z

foldr f z (x:xs) = f x (foldr f z xs)

Ура! Как теперь использовать эту функцию для реализации наших семи близнецов?

foo xs = foldr (+) 0 xs

boo xs = foldr (*) 1 xs

goo xs = foldr (&&) True xs

moo xs = foldr (++) [] xs

hoo …

А что же делать с остальными функциями? Какую функцию надо передать в foldr, чтобы получилась функция hoo? Давайте немного перепишем функцию hoo!

hoo [] acc = acc

hoo (x:xs) acc = hoo xs (if x < acc then x else acc)

Вам ничего не напоминает это выражение в скобках? Переименуйте acc в y… Да это же тело бинарной функции min! Может быть, тогда так?

hoo xs = foldr min (head xs) xs

Заметьте, что мы передали в функцию foldr в качестве нулевого элемента z? Ну а что еще – не плюс бесконечность же передавать. Работает! А что с функцией doo? Перепишем:

doo [] acc = acc

doo (x:xs) acc = doo xs (if x > 0 then acc+1 else acc)

И теперь очевидно, что является функцией, передаваемой в foldr:

doo xs = foldr (\x z -> if x > 0 then z+1 else z) 0 xs

Осталась только последняя функция, сейчас мы ее тоже быстренько расколем:

zoo [] acc = acc

zoo (x:xs) acc = zoo xs (x:acc)

Тут даже ничего переписывать не надо, сразу видно, что за операция пойдет в foldr…

zoo xs = foldr (:) [] xs

Проверим… zoo [1,2,3] → [1,2,3]. Вот тебе раз, а где же reverse?