Я вижу java.util.function.BiFunction, поэтому могу сделать следующее:
BiFunction<Integer, Integer, Integer> f = (x, y) -> { return 0; };
Что, если этого недостаточно и мне нужна TriFunction? Его не существует!
TriFunction<Integer, Integer, Integer, Integer> f = (x, y, z) -> { return 0; };
Думаю, мне следует добавить, что я знаю, что могу определить свою собственную TriFunction, я просто пытаюсь понять причину отказа от включения ее в стандартную библиотеку.
Ответы:
Насколько мне известно, есть только два вида функций: деструктивные и конструктивные.
В то время как конструктивная функция, как следует из названия, что-то конструирует, деструктивная функция что-то разрушает, но не так, как вы сейчас думаете.
Например, функция
является конструктивным . Как вам нужно что-то построить. В этом примере вы построили кортеж (x, y) . У конструктивных функций есть проблема, заключающаяся в том, что они не могут обрабатывать бесконечные аргументы. Но хуже всего то, что нельзя просто оставить спор открытым. Вы не можете просто сказать «ну, пусть x: = 1» и пробовать каждый y, какой захотите. Вы должны каждый раз строить весь кортеж с помощью
x := 1
. Так что, если вы хотите увидеть, что возвращают функции,y := 1, y := 2, y := 3
вы должны написатьf(1,1) , f(1,2) , f(1,3)
.В Java 8 конструктивные функции должны обрабатываться (большую часть времени) с использованием ссылок на методы, потому что использование конструктивной лямбда-функции не дает особых преимуществ. Они немного похожи на статические методы. Вы можете использовать их, но они не имеют реального состояния.
Другой тип - разрушительный, он что-то берет и разбирает по мере необходимости. Например, деструктивная функция
делает то же самое, что и функция,
f
которая была конструктивной. Преимущества деструктивной функции заключаются в том, что теперь вы можете обрабатывать бесконечные аргументы, что особенно удобно для потоков, и вы можете просто оставить аргументы открытыми. Итак, если вы снова захотите увидеть, каков будет результат, еслиx := 1
иy := 1 , y := 2 , y := 3
, вы можете сказатьh = g(1)
иh(1)
является результатом дляy := 1
,h(2)
дляy := 2
иh(3)
дляy := 3
.Итак, у вас есть фиксированное состояние! Это довольно динамично, и в большинстве случаев это именно то, что мы хотим от лямбды.
Такие шаблоны, как Factory, намного проще, если вы можете просто добавить функцию, которая сделает всю работу за вас.
Деструктивные легко сочетаются друг с другом. Если тип правильный, вы можете просто составить их, как вам нравится. Используя это, вы можете легко определять морфизмы, которые значительно упрощают (с неизменяемыми значениями) тестирование!
Вы можете сделать то же самое с конструктивным, но деструктивная композиция выглядит лучше и больше похожа на список или декоратор, а конструктивная очень похожа на дерево. А такие вещи, как возврат с помощью конструктивных функций, просто нехорошие. Вы можете просто сохранить частичные функции деструктивного (динамическое программирование), а при «возврате» просто использовать старую деструктивную функцию. Это делает код намного меньше и лучше читается. С конструктивными функциями у вас есть более или менее запомнить все аргументы, которых может быть много.
Итак, почему существует необходимость,
BiFunction
должно быть больше вопросов, чем почему нетTriFunction
?Во-первых, много времени у вас есть всего несколько значений (меньше 3) и нужен только результат, поэтому обычная деструктивная функция не понадобится вообще, а конструктивная подойдет. И есть такие вещи, как монады, которые действительно нуждаются в конструктивной функции. Но помимо этого, на самом деле не так много веских причин, по которым вообще существует
BiFunction
. Это не значит, что его нужно удалить! Я борюсь за свои Монады, пока не умру!Поэтому, если у вас много аргументов, которые нельзя объединить в логический контейнерный класс, и если вам нужно, чтобы функция была конструктивной, используйте ссылку на метод. В противном случае попробуйте использовать новую полученную способность деструктивных функций, вы можете обнаружить, что делаете много вещей с гораздо меньшим количеством строк кода.
источник
BiFunction
был создан, чтобы упростить сокращение данных, и большинствоStream
операций терминала - это просто сокращение данных. Хороший примерBinaryOperator<T>
используется во многихCollectors
. Первый элемент сокращается с помощью второго, затем может быть уменьшен с помощью следующего, и так далее. Конечно, вы можете создать здесьFunction<T, Function<T, T>
func = x -> (y -> / * код сокращения * /). Но серьезно? Все это когда можно просто сделатьBinaryOperator<T> func = (x, y) -> /*reduction code here*/
. Кроме того, этот подход к сокращению данных мне очень напоминает ваш «деструктивный» подход.Function<Integer,Integer> f = (x,y) -> x + y
Java является допустимой, а это не так. Для начала это должна быть BiFunction!Если вам нужна TriFunction, просто сделайте это:
Следующая небольшая программа показывает, как ее можно использовать. Помните, что тип результата указывается в качестве последнего параметра универсального типа.
Я думаю , если есть практическое применение TriFunction в
java.util.*
илиjava.lang.*
она была бы определена. Однако я бы никогда не пошел дальше 22 аргументов ;-) Что я имею в виду под этим, весь новый код, который позволяет передавать коллекции, никогда не требовал TriFunction в качестве любого из параметров метода. Так что это не было включено.ОБНОВИТЬ
Для полноты и следования объяснению деструктивных функций в другом ответе (связанном с каррированием), вот как можно эмулировать TriFunction без дополнительного интерфейса:
Конечно, можно комбинировать функции и другими способами, например:
Хотя каррирование было бы естественным для любого языка, который поддерживает функциональное программирование помимо лямбда-выражений, Java не построена таким образом, и, хотя это достижимо, код трудно поддерживать, а иногда и читать. Однако это очень полезно в качестве упражнения, и иногда частичные функции занимают достойное место в вашем коде.
источник
TriFunction<Integer,Integer,Integer,Integer> comp = (x,y,z) -> x + y + z; comp = comp.andThen(s -> s * 2); int result = comp.apply(1, 2, 3); //12
см. Stackoverflow.com/questions/19834611/…andThen()
ответу добавлен пример использования.BiFunction
оно используется вStream
API для сокращения данных, что очень похоже на подход каррирования для меня: вы никогда не берете больше двух аргументы, и вы можете обрабатывать любое количество элементов, по одному сокращению за раз (см. мой комментарий к принятому ответу, я был бы рад узнать, ошибаюсь ли я в этом).Альтернативой является добавление приведенной ниже зависимости,
Теперь вы можете использовать функцию Vavr, как показано ниже, до 8 аргументов,
3 аргумента:
5 аргументов:
источник
vavr
библиотекой - это делает программирование в функциональном стиле на Java настолько терпимым, насколько это возможно.У меня почти такой же вопрос и частичный ответ. Не уверен, что конструктивный / деконструктивный ответ имел в виду разработчики языка. Я думаю, что у 3 и более до N есть допустимые варианты использования.
Я из .NET. а в .NET у вас есть Func и Action для функций void. Также существуют предикаты и некоторые другие частные случаи. См. Https://msdn.microsoft.com/en-us/library/bb534960(v=vs.110).aspx
Интересно, по какой причине разработчики языка выбрали Function, Bifunction и не продолжили работу до DecaExiFunction?
Ответ на вторую часть - стирание шрифта. После компиляции нет разницы между Func и Func. Поэтому следующее не компилируется:
Внутренние функции использовались, чтобы обойти еще одну незначительную проблему. Eclipse настаивал на том, чтобы оба класса в файлах с именем Function находились в одном каталоге ... Не уверен, является ли это проблемой компилятора в настоящее время. Но я не могу отключить ошибку в Eclipse.
Func использовался для предотвращения конфликтов имен с типом функции Java.
Итак, если вы хотите добавить Func от 3 до 16 аргументов, вы можете сделать две вещи.
Пример для второго способа:
и
Какой был бы лучший подход?
В приведенных выше примерах я не включил реализации методов andThen () и compose (). Если вы добавляете их, вы должны добавить по 16 перегрузок каждая: TriFunc должен иметь andthen () с 16 аргументами. Это приведет к ошибке компиляции из-за циклических зависимостей. Также у вас не было бы этих перегрузок для Function и BiFunction. Поэтому вы также должны определить Func с одним аргументом и Func с двумя аргументами. В .NET циклические зависимости можно было бы обойти, используя методы расширения, которых нет в Java.
источник
andThen
16 аргументов? Результатом функции в Java является одно значение.andThen
принимает это значение и что-то с ним делает. Также нет проблем с именованием. Имена классов должны быть разными и находиться в разных файлах с одинаковыми именами - в соответствии с логикой, установленной разработчиками языка Java с помощью Function и BiFunction. Кроме того, все эти разные имена необходимы, если типы аргументов разные. Можно создатьVargFunction(T, R) { R apply(T.. t) ... }
для одного типа.Я нашел здесь исходный код BiFunction:
https://github.com/JetBrains/jdk8u_jdk/blob/master/src/share/classes/java/util/function/BiFunction.java
Я изменил его, чтобы создать TriFunction. Как и BiFunction, он использует andThen (), а не compose (), поэтому для некоторых приложений, требующих compose (), он может не подходить. Это должно быть хорошо для обычных объектов. Хорошую статью об andThen () и compose () можно найти здесь:
http://www.deadcoderising.com/2015-09-07-java-8-functional-composition-using-compose-and-andthen/
источник
Вы также можете создать свою собственную функцию, используя 3 параметра
источник
Вы не всегда можете остановиться на TriFunction. Иногда вам может потребоваться передать вашим функциям n параметров. Затем группе поддержки необходимо будет создать QuadFunction для исправления вашего кода. Долгосрочным решением было бы создать объект с дополнительными параметрами, а затем использовать готовую функцию или биФункцию.
источник