Що означає лямбда з 2 стрілками на Java 8?


118

Я раніше читав кілька навчальних посібників Java 8.

Зараз я зіткнувся з такою темою: Чи підтримує java Currying?

Тут я бачу такий код:

IntFunction<IntUnaryOperator> curriedAdd = a -> b -> a + b;
System.out.println(curriedAdd.apply(1).applyAsInt(12));

Я розумію, що цей приклад становить 2 елементи, але я не можу зрозуміти конструкцію:

a -> b -> a + b;

Відповідно до лівої частини виразу, цей рядок повинен реалізовувати таку функцію:

R apply(int value); 

До цього я зустрів лише лямбда з однією стрілою.


6
Ви розумієте, що означає закваска? Це досить невід'ємно в цьому питанні.
BlueRaja - Danny Pflughoeft

12
Це просто лямбда, яка повертає лямбда.
Ільмарі Каронен

Відповіді:


118

Якщо ви висловите це як неметочний синтаксис лямбда або попередній лямбда синтаксис класу Java, то зрозуміліше, що відбувається ...

Оригінальне запитання. Чому дві стріли? Просто, визначено дві функції ... Перша функція - це функція, що визначає функцію, друга - результат цієї функції, яка також є функцією. Кожен вимагає, щоб ->оператор його визначив.

Нескорочення

IntFunction<IntUnaryOperator> curriedAdd = (a) -> {
    return (b) -> {
        return a + b;
    };
};

Попередньо лямбда перед Java 8

IntFunction<IntUnaryOperator> curriedAdd = new IntFunction<IntUnaryOperator>() {
    @Override
    public IntUnaryOperator apply(final int value) {
        IntUnaryOperator op = new IntUnaryOperator() {
            @Override
            public int applyAsInt(int operand) {
                return operand + value;
            }
        };
        return op;
    }
};

1
pre-lambda вимагаєfinal int value
njzk2

8
Так, ви маєте рацію, але я писав, що все-таки використовується компілятор Java 8, який дозволяє використовувати речі, які є "фактично остаточними"
Адам,

1
@gstackoverflow так, але тоді java 8 неpre-lambda
njzk2

1
ви також можете використовувати стиль пре-лямбда в java 8. Я написав це JFY
gstackoverflow

3
Хіба не лямбди були зроблені просто так, щоб люди могли набрати очки? :-)
Стефан

48

А IntFunction<R>- це функція int -> R. А IntUnaryOperator- це функція int -> int.

Таким чином, a IntFunction<IntUnaryOperator>- це функція, яка приймає intяк параметр і повертає функцію, яка приймає intяк параметр і повертає an int.

a -> b -> a + b;
^    |         |
|     ---------
|         ^
|         |
|         The IntUnaryOperator (that takes an int, b) and return an int (the sum of a and b)
|
The parameter you give to the IntFunction

Можливо, це зрозуміліше, якщо ви використовуєте анонімні класи для "розкладання" лямбда:

IntFunction<IntUnaryOperator> add = new IntFunction<IntUnaryOperator>() {
    @Override
    public IntUnaryOperator apply(int a) {
        return new IntUnaryOperator() {
            @Override
            public int applyAsInt(int b) {
                return a + b;
            }
        };
    }
};

29

Додавання дужок може зробити це більш зрозумілим:

IntFunction<IntUnaryOperator> curriedAdd = a -> (b -> (a + b));

Або, можливо, проміжна змінна може допомогти:

IntFunction<IntUnaryOperator> curriedAdd = a -> {
    IntUnaryOperator op = b -> a + b;
    return op;
};

24

Давайте перепишемо цей лямбда-вираз з дужками, щоб зробити його більш зрозумілим:

IntFunction<IntUnaryOperator> curriedAdd = a -> (b -> (a + b));

Отже, ми оголошуємо функцію, приймаючи a, intщо повертає a Function. Більш конкретно, повернута функція приймає an intі повертає an int(сума двох елементів): це може бути представлено у вигляді an IntUnaryOperator.

Отже, curriedAddце функція, яка приймає intта повертає IntUnaryOperator, тому її можна представити як IntFunction<IntUnaryOperator>.


9

Це два лямбда-вирази.

IntFunction<IntUnaryOperator> curriedAdd = 
  a -> { //this is for the fixed value
    return b -> { //this is for the add operation
      return a + b;
    };
  }

IntUnaryOperator addTwo = curriedAdd.apply(2);
System.out.println(addTwo.applyAsInt(12)); //prints 14

8

Якщо ви подивитесь, IntFunctionце може стати зрозумілішим: IntFunction<R>це a FunctionalInterface. Він являє собою функцію, яка приймає intі повертає значення типу R.

У цьому випадку тип повернення Rє також а FunctionalInterface, а саме - an IntUnaryOperator. Отже перша (зовнішня) функція сама повертає функцію.

У цьому випадку: При застосуванні до int, curriedAddповинна повертати функцію , яка знову приймає int(і повертається знову int, тому що це те , що IntUnaryOperatorробить).

У функціональному програмуванні прийнято записувати тип функції як, param -> return_valueі саме ви це бачите тут. Отже, тип curriedAddє int -> int -> int(або int -> (int -> int)якщо вам це більше подобається).

Ламбда-синтаксис Java 8 йде разом із цим. Щоб визначити таку функцію, ви пишете

a -> b -> a + b

що дуже схоже на фактичне обчислення лямбда:

λa λb a + b

λb a + b- це функція, яка приймає один параметр bі повертає значення (суму). λa λb a + bце функція, яка приймає один параметр aі повертає іншу функцію одного параметра. λa λb a + bповертається λb a + bіз aзаданим значенням параметра.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.