В прошлом году я закончил вводный курс по функциональному программированию на edX. Язык, используемый в курсе, был haskell . Мне было приятно работать в Хаскеле. Одна из моих любимых функций — функции, принимающие более одного параметра, которые могут частично применяться автоматически. Например, если у вас есть функция, ожидающая 3 параметра, вы можете передать только первый параметр, возвращается функция, ожидающая два других параметра. Но вы можете указать только еще один параметр, и будет возвращена функция, принимающая последний (я считаю, что это поведение по умолчанию для всех функций в haskell). Я использовал частично примененные функции, прежде чем работать в Scala, но почему-то на этот раз сила и последствия произвели на меня большее впечатление. Для лучшего объяснения функций и частично примененных функций в haskell, перейдите к Learn You a Haskell . Теперь нам нужно перейти к сути этого поста, как мы можем добиться такого поведения в Java 8.
Случай для частично примененных функций
Сначала немного дополнительной справочной информации будет полезно. Функции, которые принимают функции в качестве аргументов или возвращают функции, называются функциями более высокого порядка . Функции высшего порядка очень мощны для решения задач. Частичное применение функций означает, что нам не нужно иметь все параметры одновременно. Вместо этого мы можем собирать биты информации, когда мы идем, и когда у нас есть вся необходимая информация, мы, наконец, вычисляем наш результат. Частично примененные функции также обеспечивают большую гибкость при создании новых функций. Просто предоставляя различные начальные или начальные значения, мы получаем практически неограниченную способность обрабатывать различные ситуации в нашем коде. Давайте посмотрим на пример.
Пример частично примененных функций
Теперь мы покажем, как использовать частично примененные функции в Java 8. Вот наши базовые функции:
Function<Integer,Function<Integer,Function<BinaryOperator<Integer>,Integer>>> someComputation = i1 -> i2 -> f -> f.apply(i1,i2);
BinaryOperator<Integer> mult = (i,j) -> i * j;
BinaryOperator<Integer> divide = (i,j) -> i / j;
BinaryOperator<Integer> sumSquares = (i,j) -> (i*i) + (j*j);
У нас есть функция, someComputation
которая будет принимать два числа и применять некоторые вычисления с использованием обоих чисел. У нас также есть другой функциональный интерфейс BinaryOperator . Это BinaryOperator
сахар для функции, которая принимает два параметра и возвращает значение того же типа. Следует признать, что синтаксис частично примененных функций в Java может быть громоздким, особенно по сравнению с scala или haskell. По крайней мере, мы можем использовать этот подход, так как до java 8 использование частично примененных функций без лямбда-выражений было бы невозможным. Это очень простой пример частично примененных функций, но я думаю, что это важно. Вот наш код в действии в модульном тесте:
public class PartiallyAppliedFunctionsTest {
Function<Integer,Function<Integer,Function<BinaryOperator<Integer>,Integer>>> someComputation = i1 -> i2 -> f -> f.apply(i1,i2);
BinaryOperator<Integer> mult = (i,j) -> i * j;
BinaryOperator<Integer> divide = (i,j) -> i / j;
BinaryOperator<Integer> sumSquares = (i,j) -> (i*i) + (j*j);
int first = 10;
int second = 5;
Function<Integer,Function<BinaryOperator<Integer>,Integer>> partial1 = someComputation.apply(first);
Function<BinaryOperator<Integer>,Integer> partial2 = partial1.apply(second);
@Test
public void test_multiplication(){
assertThat(partial2.apply(mult),is(50));
}
@Test
public void test_divide(){
assertThat(partial2.apply(divide),is(2));
}
@Test
public void test_sum_squares(){
assertThat(partial2.apply(sumSquares),is(125));
}
}
Вывод
Хотя наш пример очень простой, мы надеемся, что мы увидим мощь и гибкость, предоставляемые частично примененными функциями, и то, как они могут быть отличным инструментом в вашем распоряжении. Спасибо за ваше время.