我之前读过几本 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。
如果你将其表达为非简写 lambda 语法或 pre-lambda Java 匿名类语法,那么发生的事情会更清楚......
原来的问题。为什么有两个箭头?很简单,定义了两个函数...第一个函数是函数定义函数,第二个函数是该函数的结果,它也恰好是函数。每个都需要一个
->
运算符来定义它。
IntFunction<IntUnaryOperator> curriedAdd = (a) -> {
return (b) -> {
return a + b;
};
};
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;
}
};
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;
}
};
}
};
添加括号可能会使这一点更清楚:
IntFunction<IntUnaryOperator> curriedAdd = a -> (b -> (a + b));
或者中间变量可能会有所帮助:
IntFunction<IntUnaryOperator> curriedAdd = a -> {
IntUnaryOperator op = b -> a + b;
return op;
};
让我们用括号重写 lambda 表达式以使其更加清晰:
IntFunction<IntUnaryOperator> curriedAdd = a -> (b -> (a + b));
因此,我们声明一个带有
int
的函数,它返回 Function
。更具体地说,返回的函数采用 int
并返回 int
(两个元素之和):这可以表示为 IntUnaryOperator
。
因此,
curriedAdd
是一个接受int
并返回IntUnaryOperator
的函数,因此它可以表示为IntFunction<IntUnaryOperator>
。
这是两个 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
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
设置为参数值。
阅读答案后,我觉得有必要使用用户界面添加示例。这些例子可以提高对柯里化的理解。
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")) ;
}
}
请注意,在接口方法链中,第一个方法返回下一个接口类型,最后一个方法返回一个对象。