Функциональное программирование
на ЯвеDevClub
26.11.2009
Andrei Solntsev
О чём это мы?
Данный пптахдаёт представление
о методах Функционального Программированияи
их применениив ООП-языках типа Java
Кое-где придётся подумать!
Оглавление
ФП для чайников Основные свойства ФП Примеры кода на Haskell Примеры кода на Java ФП для бизнес-логики
Введение в ФП
Вычисление - последовательное выполнение команд, изменяющих состояние.
Императивное программирование
Функциональное программирование
Вычисление -нахождение значения выражения
КАК
Выраженияобразованы из функций для комбинации базовых значений и других функций
Программа состоит из нескольких последовательных команд.
ЧТО
против
Алгоритм
На каком примере всем объясняют понятие «алгоритм»?
Бутерброд!
Алгоритм бутерброда
Фунцкия createSandwich
1. Возьми кусок хлеба2. Намажь масло на хлеб3. Положи сыр на масло4. return result
Императивный стиль
return
Функциональный стиль
положи(сыр,намажь(масло, хлеб) )
Что, если мы хотим использовать колбасу вместо сыра ?
Давайте передавать колбасу/сыр как входной параметр функции
Нет проблем!
Алгоритм бутерброда
1. Возьми низ2. Намажь середину на низ3. Положи верх на середину4. return result
Function createSandwich (низ, середина, верх)
return положи(верх,намажь(середина, низ) )
Function createSandwich (низ, середина, верх)
Нет проблем!
хлеб масло колбаса
Алгоритм бутерброда
Что, если мы хотим не намазывать масло, а класть кусками?
Императивное программирование: Проблема!
Функциональное программирование: по-прежнему нет проблем
Алгоритм бутерброда
1. Возьми низ2. if способ = ‘класть’
положи середину на низ else
намажь середину на низ end if
3. Положи верх на середину4. return result
Function createSandwich (низ, середина, верх, способ)
хлеб масло колбаса класть
Альтернативный вариант: создать 2 разные функции Дублирование кода
Алгоритм бутерброда
Императивное программирование: Проблема!
Больше способов – больше IF’ов
return put ( верх,action(середина, низ) )
Function createSandwich (низ, середина, верх, action)
хлеб масло колбаса класть
Action – это функция с двумя аргументами
• Намазывать• класть• …
createSandwich – функция более высокого порядка (higher-order function), которая принимает другую функцию как аргумент
Алгоритм бутерброда
Функциональное программирование: нет проблем
Функциональный бутерброд
Основные свойства ФП
Что такое Функциональное Программмирование?
• Closures and higher order functions
• Lazy evaluation
• Recursion as a mechanism for control flow
• Enforcement of referential transparency
• No side-effects
• Lambda calculus
Функциональные языки• Lisp (AutoCad)
• Haskell, Scheme, Logo
• XSLT
Там, где традиционная императивная программа использует цикл для прохождения по списку, функциональный стиль использует функцию высокого порядка map, которая принимает другую функцию и список, применяет эту функцию к каждому элементу списка и возвращает список из результатов.
• Scala, LambdaJ, Clojure
ФП для чайников Основные свойства ФП Примеры кода на Haskell Примеры кода на Java ФП для бизнес-логики
Примеры кода на Haskell
add :: Integer -> Integer -> Integeradd x y = x + y
functions
add 1 2 = 3add 6 9 = 15
add 1 = ?
Ух ты!add 1 = функция типа Integer -> Integer
Curryinghttp://en.wikipedia.org/wiki/Currying
Примеры кода на Haskell
add :: Integer -> Integer -> Integeradd x y = x + y
functions
inc :: Integer -> Integerinc = add 1
map :: (a->b) -> [a] -> [b]map f [] = []map f (x:xs) = f x : map f xs
Uncurriedfunction
Function can bereturned as a value !
Higher-orderfunction
curriedfunction
ones = 1 : ones
Бесконечные структуры данных
numsFrom n = n : numsFrom (n+1)
squares = map (^2) (numsfrom 0)
take 5 squares => [0,1,4,9,16]
Примеры кода на Haskell
Это выражение вычисляется за конечное число шагов
Потому что Lazy Evaluation!
Примеры кода в стиле ФП в Java
java.util.Properties
Properties properties = new Properties();properties.setProperty(“firstName", groom.getFirstName());properties.setProperty(“lastName", groom.getLastName());properties.setProperty(“salary", groom.getSalary());return parameters;
return
Императивный
Функциональный
return new Properties() .setProperty(“firstName", groom.getFirstName()) .setProperty(“lastName", groom.getLastName()) .setProperty(“salary", groom.getSalary());• Плюсы?
• Минусы?
йа баго
нихт баго
java.lang.StringBuffer
StringBuffer sb = new StringBuffer();sb.append(“a”);sb.append(“b”);sb.append(“c”);return sb.toString();
return new StringBuffer() .append(“a”); .append(“b”); .append(“c”) .toString();
Императивный
Функциональный
• Плюсы? • Минусы?
Примеры кода в стиле ФП в Java
ФП: За и против
За
• Надёжный (reliable) код• Читаемый (readable) код• Многократно используемый (Reusable) код
• Неестественный для человека• Неестественный для компьютера• Отсутствие контроля над процессом• Производительность
Против
Пример: алгоритм быстрой сортировки (Quick Sort)
Quicksort на языке Haskell
qsort [] = []
qsort (x:xs) = qsort elts_lt_x ++
[x] ++
qsort elts_greq_x
where
elts_lt_x = [y | y <- xs, y < x]
elts_greq_x = [y | y <- xs, y >= x]
Сравните с Quicksort на языке C:
Quicksort на языке C
qsort( a, lo, hi ) int a[], hi, lo;{
int h, l, p, t;if (lo < hi) {
l = lo; h = hi; p = a[hi];do {
while ((l < h) && (a[l] <= p))l = l+1;
while ((h > l) && (a[h] >= p))h = h-1;
if (l < h) {t = a[l]; a[l] = a[h]; a[h] = t;
}} while (l < h);
t = a[l]; a[l] = a[hi]; a[hi] = t;qsort( a, lo, l-1 );qsort( a, l+1, hi );
}}
В языках типа Java, ФП хорошо подходит для реализации бизнес-логики и обработки списков
Проще придумывать, писать и поддерживать ПО,но программист имеет меньше контроля над тем, что происходит во время выполнения программы.
ФП: За и против
За
Вывод
ФП для чайников Основные свойства ФП Примеры кода на Haskell Примеры кода на Java ФП для бизнес-логики
25ый кадр
25ый кадр
Бизнес-логика с помощью ФП
Класс GroomFilter
List suitableGrooms = new ArrayList();
for (groom in allGrooms){ if (minAge > -1 && groom.getAge() < minAge)
continue;
if (maxAge > -1 && groom.getAge() > maxAge)continue;
suitableGrooms.add(groom);}
return suitableGrooms;
List filterGrooms(List allGrooms, int minAge, int maxAge)
Если age = -1, то возраст проверять не нужно
Отфильтровывает женихов
Как сделать более сложные проверки?
Класс GroomFilter
List suitableGrooms = new ArrayList();
for (groom in allGrooms){ if (groomChecker.apply(groom))
suitableGrooms.add(groom);}
return suitableGrooms;
List filterGrooms(List allGrooms, Predicate groomChecker)
Передаём функцию как параметр
Бизнес-логика с помощью ФП
package com.google.common.base;
public interface Predicate<T> {{ /** * Возвращает true или false для данного объекта */ boolean apply(T input);}
Google collections
http://bwinterberg.blogspot.com/2009/09/introduction-to-google-collections.htmlhttp://code.google.com/p/google-collections/
package com.google.common.base;
public class Predicates { static <T> Predicate<T> alwaysTrue(); static <T> Predicate<T> alwaysFalse();
static <T> Predicate<T> isNull(); static <T> Predicate<T> notNull();
static <T> Predicate<T> not(Predicate<T> predicate); static <T> Predicate<T> and(Predicate<? super T>... components);
…}
Google collections
Google collections
package com.google.common.collect;
public class Collections2 { static <E> Collection<E> filter(
Collection<E> unfiltered, Predicate<? super E> predicate)
static <F, T> Collection<T> transform(Collection<F> fromCollection, Function<? super F, T> function)
}
Класс GroomFilter
return Collections2.filter(allGrooms, groomChecker);
List filterGrooms(List allGrooms, Predicate groomChecker)
Функция filterGrooms становится ещё проще
Бизнес-логика с помощью ФП
Клиент 1
List suitableGrooms grooms = GroomFilter.filterGrooms(…, new Predicate<Groom>() {
public boolean apply(Groom groom){ return groom.getAge() > 23;}
});
Клиент 2
List suitableGrooms = GroomFilter.filterGrooms(…,Predicates.alwaysTrue());
Closure – Объект, представляющий функцию
Анонимные классычасто используются в качестве closures
Бизнес-логика с помощью ФП
Дано: список имён женихов.Найти: все имена, начинающиеся на “Mr.”
List gentlemen = new LinkedList();
for (Iterator it = groomsNames.iterator(); it.hasNext(); ){ String name = (String) it.next(); if (name != null &&
name.startsWith(“Mr.”)) {
gentlemen.add(name); }}
return gentlemen;
Императивное программирование:
Применение ФП: Фильтры
Функциональное программирование:
import com.google.common.collect.Collections2;
return Collections2.filter(allGrooms,StringPredicates.startsWith(“Mr.”));
Дано: список имён женихов.Найти: все имена, начинающиеся на “Mr.”
Применение ФП: Фильтры
Дано: список жениховНайти: список имён женихов
List groomsNames = new ArrayList();
for (Iterator it = allGrooms.iterator(); it.hasNext(); ){ Groom groom = (Groom) it.next(); groomsNames.add(groom.getName());}
return groomsNames;
Императивное программирование
Применение ФП: Функции
import com.google.common.collect.Collections2;
return Collections2.transform( allGrooms,new Function<Groom, String>(){ public String apply(Groom groom) {
return groom.getName(); }});
Функциональное программирование
Применение ФП: Функции
Дано: список жениховНайти: список имён женихов
На первый взгляд, не столь красиво, номожет быть вынесено в отдельный класс и многократно использовано!
Google collections
GC позволяет сделать и более элегантные конструкции
boolean result = and( not( in(list1) ), in(list2), in(list3)).apply("1");
Collection names = Joiner.on("; ").useForNull("B000").join(filtered);
Эпиlog4j
В следующий раз, прежде чем написать FOR или IF, задумайтесь!
Скачайте себе Haskell и поиграйтесь на досуге. Если это не убьёт ваш мозг, то сделает его сильнее.