Что такое функция гражданина первого класса?
Что такое функция гражданина первого класса?
Поддерживает ли Java функцию гражданина первого класса?
Редактировать:
Как упомянуть на Википедии
Функции первого класса необходимы для стиля функционального программирования.
Есть ли другое использование первоклассных функций?
6 ответов
Язык, который рассматривает процедуры как "первоклассные", позволяет передавать функции так же, как и любое другое значение.
Такие языки, как Java 7 (и более ранние версии) и C "своего рода", имеют такую возможность: C позволяет передавать указатели на функции, но вы не можете динамически определять функцию в этих языках и внезапно передавать ее где-то еще. Java до версии 8 может имитировать это в определенной степени с анонимными классами, но технически не имеет функций первого класса.
С другой стороны, C++, D, C#, Visual Basic .NET, Java 8+ и функциональные языки (такие как Scheme и Haskell) позволяют передавать функции, подобные переменным. Например, приведенный ниже код возвращает функцию, которая добавляет addend
на его вход:
Написано в D:
int delegate(int) makeAdder(int addend) //Returns a function
{
return delegate int(int x) //Long way
{
return x + addend; //Notice that addend came from _outside_ the function
};
return (int x) { return x + addend; }; //Short way
return x => addend + x; //Super-short way, introduced in D 2.058
}
Написано в C#:
Func<int, int> MakeAdder(int addend) //Returns a function
{
return delegate(int x) //The long way. Note: Return type is implicitly 'int'
{
return x + addend;
};
return x => x + addend; //Short way: x "goes to" (x + addend); inferred types
}
Написано на C++:
#include <functional>
std::function<int(int)> make_adder(int addend)
{
return [=](int x)
{
return addend + x;
};
}
Написано в Scala:
def makeAdder(addend: Int) = (x: Int) => addend + x
Написано на Python:
def make_adder(addend):
def f(x):
return addend + x
return f
# or...
return lambda x: addend + x
Написано на эрланге:
make_adder(Addend) ->
fun(X) -> Addend + X end.
Написано на JavaScript:
function makeAdder(addend) {
return function(x) {
return addend + x;
};
}
Написано на JavaScript (синтаксис функции стрелки ES2015):
const makeAdder = addend => x => addend + x;
Написано на схеме:
(define (makeAdder addend)
(lambda (x)
(+ x addend)))
Написано на Хаскеле:
makeAdder :: Int -> (Int -> Int)
makeAdder addend = \x -> addend + x
Написано в Visual Basic 2008:
Function MakeAdder(addend As Integer) As Func(Of Integer, Integer)
Return Function(x) (x + addend)
End Function
Написано в Swift (как подробные, так и краткие реализации):
func makeAdder(append: Int) -> (x: Int) -> Int {
return { (x: Int) -> Int in
return x + append
};
}
func makeAdder(append: Int) -> (Int) -> Int {
return {$0 + append};
}
(Кстати, "лямбда" - это просто функция без имени. Лямбда поддерживается только в языках, которые поддерживают первоклассные функции.)
Рассмотрим пример парадигмы функционального программирования, в которой функции являются первоклассными гражданами. Когда мы говорим, что функции являются гражданами первого сорта, мы можем делать следующие вещи с функцией...
- Функция может быть назначена переменной
- Функция может быть сохранена в структуре данных
- Функция может быть передана в качестве аргумента другим функциям
- Функция может быть возвращена из функций
В функциональных языках программирования можно делать вышеупомянутые вещи.
Теперь давайте попробуем ответить на вопрос, поддерживает ли java функции гражданина первого класса (или нет).
В java методы эквивалентны функциям. Невозможно сделать что-либо из перечисленного выше с помощью методов. Но все вышеперечисленное возможно с объектами Java. Таким образом, объекты являются первоклассными гражданами в Java. Следует признать, что java8 поддерживает передачу методов (точнее, поведения методов) в другие методы с использованием функциональных интерфейсов и лямбда-выражений. Но это не означает, что у java есть функции граждан первого класса.
Возможность выполнять такие вещи, как передача функций, возвращение функций из функций, очень мощная и полезная. Это потому, что это позволяет нам передавать поведение, а не только данные.
Первоклассную функцию можно обойти. Типичным примером является функция карты. Вот пример в Scala, который возводит в квадрат элементы списка:
val square = (x:Int) => x*x
val squaredList = List(1,2,3,4).map(square _)
//--> List(1,4,9,16)
Здесь функция square является аргументом метода map, который применяет его к каждому элементу. Если вы хотите сделать что-то подобное в Java, вы должны использовать метод, заключенный в класс, что-то вроде этого:
interface F<A,B>{ B apply(A a); }
static <A,B> List<B> map(List<A> list, F<A,B> f) {
List<B> result = new ArrayList<B>();
for(A a:list) result.add(f.apply(a));
return result;
}
//we have to "wrap" the squaring operation in a class in order to make it a function
F<Integer,Integer> square = new F<Integer,Integer>(){
Integer apply(Integer a) { return a*a; }
}
List<Integer> ints = Arrays.<Integer>asList(1,2,3,4);
List<Integer> squares = map(ints, square);
Глядя на это, вы можете видеть, что вы можете выполнить ту же задачу, как-то выполненную в Java, но с большими накладными расходами и без "родной" поддержки языком, но с помощью обходного пути (классы-обертки). Так что Java не поддерживает функции первого класса, но может "имитировать" их.
Надеюсь, Java 8 будет поддерживать функции первого класса. Если вы хотите получить некоторую поддержку сейчас, посмотрите http://functionaljava.org/ или http://functionalj.sourceforge.net/ или посмотрите на язык Scala.
Определение Wikipedia довольно хорошее - это функция, которую можно передавать, как и любой другой фрагмент данных. Java не поддерживает их. Ближайший это Runnable
а также Callable
объекты.
Приведенные выше ответы на вопросы @Alpine в основном определяют, что такое функции первого класса, а также примеры. Но все же остается один вопрос, зачем использовать?
Я попытаюсь ответить на вопрос о преимуществах немного иначе в Scala, где функции первого класса используются в дальнейшем как функции более высокого порядка (map, flatMap), частично прикладные функции и каррирование:
Как мы ориентируемся на декларативном программировании как часть обработки данных остаются в качестве детали реализации на карту, flatMap, и сосредоточилась больше на работу с каким фактическим логическим потоком. Вызывающий может указать, что следует делать, и оставить функции более высокого порядка для обработки фактического логического потока.
Частично применяемые функции и каррирование: что, если вы хотите повторно использовать вызов функции и сохранить некоторые параметры, чтобы не вводить их снова?
Пример частично примененной функции:
def factorOf(x: Int, y: Int) = y % x == 0
val multipleOf3 = factorOf(3, _: Int)
val y = multipleOf3(78)
Пример каррирования:
def factorOf(x: Int)(y: Int) = y % x == 0
val isEven = factorOf(2) _
val z = isEven(32)
Приведенные выше примеры показывают, как можно повторно использовать часть первоклассных функций, не передавая все параметры и сохраняя принцип DRY вашего кода. Вот несколько преимуществ использования первоклассных функций
Ссылка для более подробной информации: https://www.oreilly.com/library/view/learning-scala/9781449368814/ch05.html
Нет, вы не можете присвоить метод переменной или передать его в качестве аргумента другому методу, например.
Вместо этого вы можете использовать интерфейсы, чтобы обернуть предполагаемое поведение, или рефлексию для определения методов.
Функции первоклассного гражданина означает, что вы можете передать функцию в любом месте, как если бы она была переменной.
Из Скалы
def isOdd(in: Int) = in % 2 == 1
val n = (1 to 10).toList
n.filter(isOdd)
see here: isOdd is a function. passed as if it's a variale.
Objects
первоклассный гражданин на Яве. Первоклассный гражданин - это тот, кого можно пропустить куда угодно. Параллели от первоклассного гражданина страны разрешены практически везде.
Читать: