带有 2 个箭头的 lambda 在 Java 8 中意味着什么?

问题描述 投票:0回答:7

我之前读过几本 Java 8 教程。

现在我遇到了以下主题: java支持柯里化吗?

在这里,我看到以下代码:

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

我知道这个例子对 2 个元素求和,但我无法理解其结构:

a -> b -> a + b;

根据表达式左边部分,这一行应该实现以下功能:

R apply(int value); 

在此之前,我只遇到过只有一个箭头的 lambda。

java lambda java-8 currying
7个回答
120
投票

如果你将其表达为非简写 lambda 语法或 pre-lambda Java 匿名类语法,那么发生的事情会更清楚......

原来的问题。为什么有两个箭头?很简单,定义了两个函数...第一个函数是函数定义函数,第二个函数是该函数的结果,它也恰好是函数。每个都需要一个

->
运算符来定义它。

非速记

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

Java 8 之前的 Pre-Lambda

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;
    }
};

49
投票

IntFunction<R>
是一个函数
int -> R
IntUnaryOperator
是一个函数
int -> int

因此

IntFunction<IntUnaryOperator>
是一个以
int
作为参数并返回一个以
int
作为参数并返回
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

如果使用匿名类来“分解”lambda,也许会更清楚:

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

30
投票

添加括号可能会使这一点更清楚:

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

或者中间变量可能会有所帮助:

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

24
投票

让我们用括号重写 lambda 表达式以使其更加清晰:

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

因此,我们声明一个带有

int
的函数,它返回
Function
。更具体地说,返回的函数采用
int
并返回
int
(两个元素之和):这可以表示为
IntUnaryOperator

因此,

curriedAdd
是一个接受
int
并返回
IntUnaryOperator
的函数,因此它可以表示为
IntFunction<IntUnaryOperator>


10
投票

这是两个 lambda 表达式。

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>
是一个
FunctionalInterface
。它表示一个接受
int
并返回
R
类型值的函数。

在这种情况下,返回类型

R
也是一个
FunctionalInterface
,即
IntUnaryOperator
。所以 first (外部)函数本身返回一个函数。

在这种情况下:当应用于

int
时,
curriedAdd
应该返回一个再次接受
int
的函数(并再次返回
int
,因为这就是
IntUnaryOperator
的作用)。

在函数式编程中,通常将函数的类型写为

param -> return_value
,您可以在这里看到这一点。所以
curriedAdd
的类型是
int -> int -> int
(或者
int -> (int -> int)
,如果你更喜欢的话)。

Java 8 的 lambda 语法与此一致。要定义这样的函数,您可以编写

a -> b -> a + b

这与实际的 lambda 演算非常相似:

λa λb a + b

λb a + b
是一个采用单个参数
b
并返回一个值(总和)的函数。
λa λb a + b
是一个接受单个参数
a
并返回单个参数的另一个函数的函数。
λa λb a + b
返回
λb a + b
,其中
a
设置为参数值。


0
投票

阅读答案后,我觉得有必要使用用户界面添加示例。这些例子可以提高对柯里化的理解。

interface SetLeft {
    SetRight addLeft(int left);
}

interface SetRight {
    int addRight(int right);
}
public static void main(String[] args) {

        SetLeft lambdaSum = l -> r -> l + r;
        int summa = lambdaSum
                .addLeft(2)
                .addRight(4);
        System.out.println(summa);

        //This is the same expression, expanded into anonymous classes
        SetLeft lambdaDetail = new SetLeft() {
            @Override
            public SetRight addLeft(int l) {
                return new SetRight() {
                    @Override
                    public int addRight(int r) {
                        return l + r;
                    }
                };
            }
        };

        summa = lambdaDetail
                .addLeft(2)
                .addRight(4);
        System.out.println(summa);
    }

还有使用用户界面和构建器模式的另一个示例:

@FunctionalInterface
interface AddPersonId {
    AddPersonName withId(Integer id);
}

@FunctionalInterface
interface AddPersonName {
    AddPersonDoB withName(String name);
}

@FunctionalInterface
interface AddPersonDoB {
    Person withDoB(LocalDate dob);
}

class Person {
    private Integer id;
    private String name;
    private LocalDate datOfBirth;

    public Person(Integer id, String name, LocalDate datOfBirth) {
        this.id = id;
        this.name = name;
        this.datOfBirth = datOfBirth;
    }

    static AddPersonId build() {
        return userid -> username -> dob -> new Person(userid, username, dob);
    }
}


public class Main {
    public static void main(String[] args) {
        Person person = Person.build()
                .withId(1)
                .withName("Bob")
                .withDoB(LocalDate.parse("1985-01-01"))                ;
    }
}

请注意,在接口方法链中,第一个方法返回下一个接口类型,最后一个方法返回一个对象。

© www.soinside.com 2019 - 2024. All rights reserved.