Java 中的这段递归 lambda 调用如何工作
How is this piece of Recursive lambda call in Java working
我最近在 Java 中看到了这段代码。它涉及函数和打印斐波那契数并且有效。
public class AppLambdaSubstitution {
public static Function<Integer, Integer> Y(Function<Function<Integer, Integer>, Function<Integer, Integer>> f) {
return x -> f.apply(Y(f)).apply(x);
}
public static void main(String[] args) {
Function<Integer, Integer> fib = Y(
func -> x -> {
if (x < 2)
return x;
else
return func.apply(x - 1) + func.apply(x - 2);
});
IntStream.range(1,11).
mapToObj(Integer::valueOf).
map(fib).forEach(System.out::println);
}
}
让我感到困惑的部分是return x -> f.apply(Y(f)).apply(x);
。 Y(f)
不就是对方法Y
的递归调用吗?我们一直用函数 f
作为参数调用它。对我来说,这种对 return 的递归调用没有基本情况。为什么无限递归调用没有溢出?
从根本上说,您忽略了一点,即 x -> f.apply(Y(f)).apply(x);
不会调用 apply
,它会 return
调用 Function
。
这只是一种非常复杂(并且不直观?)的显示套用和递归函数 IMO 的方式。如果您替换一些东西并使其更具可读性,事情会简单得多。
这个构造:
Function<Function<Integer, Integer>, Function<Integer, Integer>>
根本不需要,因为左边的参数根本没有使用。只需要抓住正确的一个。因此 left
参数可以 是任何东西 (我稍后将用 Supplier
替换它 - 这也不需要,但只是为了证明一点) .
实际上,您在这里只关心 Function
,它对 Stream
的每个元素进行实际计算:
public static Function<Integer, Integer> right() {
return new Function<Integer, Integer>() {
@Override
public Integer apply(Integer x) {
if (x < 2) {
return x;
} else {
return apply(x - 1) + apply(x - 2);
}
}
};
}
现在您可以编写整个结构:
Supplier<Function<Integer, Integer>> toUse = () -> right();
Function<Integer, Integer> fib = curry(toUse);
IntStream.range(1, 11)
.mapToObj(Integer::valueOf)
.map(fib)
.forEach(System.out::println);
这个Supplier<Function<Integer, Integer>> toUse = () -> right();
应该让你明白为什么在前面的例子(Function<Function, Function>
)中需要左边的部分 - 只是为了掌握right
一个。
如果仔细观察,您可能会注意到 Supplier
完全不需要,因此您甚至可以进一步简化它:
IntStream.range(1, 11)
.mapToObj(Integer::valueOf)
.map(right())
.forEach(System.out::println);
我最近在 Java 中看到了这段代码。它涉及函数和打印斐波那契数并且有效。
public class AppLambdaSubstitution {
public static Function<Integer, Integer> Y(Function<Function<Integer, Integer>, Function<Integer, Integer>> f) {
return x -> f.apply(Y(f)).apply(x);
}
public static void main(String[] args) {
Function<Integer, Integer> fib = Y(
func -> x -> {
if (x < 2)
return x;
else
return func.apply(x - 1) + func.apply(x - 2);
});
IntStream.range(1,11).
mapToObj(Integer::valueOf).
map(fib).forEach(System.out::println);
}
}
让我感到困惑的部分是return x -> f.apply(Y(f)).apply(x);
。 Y(f)
不就是对方法Y
的递归调用吗?我们一直用函数 f
作为参数调用它。对我来说,这种对 return 的递归调用没有基本情况。为什么无限递归调用没有溢出?
从根本上说,您忽略了一点,即 x -> f.apply(Y(f)).apply(x);
不会调用 apply
,它会 return
调用 Function
。
这只是一种非常复杂(并且不直观?)的显示套用和递归函数 IMO 的方式。如果您替换一些东西并使其更具可读性,事情会简单得多。
这个构造:
Function<Function<Integer, Integer>, Function<Integer, Integer>>
根本不需要,因为左边的参数根本没有使用。只需要抓住正确的一个。因此 left
参数可以 是任何东西 (我稍后将用 Supplier
替换它 - 这也不需要,但只是为了证明一点) .
实际上,您在这里只关心 Function
,它对 Stream
的每个元素进行实际计算:
public static Function<Integer, Integer> right() {
return new Function<Integer, Integer>() {
@Override
public Integer apply(Integer x) {
if (x < 2) {
return x;
} else {
return apply(x - 1) + apply(x - 2);
}
}
};
}
现在您可以编写整个结构:
Supplier<Function<Integer, Integer>> toUse = () -> right();
Function<Integer, Integer> fib = curry(toUse);
IntStream.range(1, 11)
.mapToObj(Integer::valueOf)
.map(fib)
.forEach(System.out::println);
这个Supplier<Function<Integer, Integer>> toUse = () -> right();
应该让你明白为什么在前面的例子(Function<Function, Function>
)中需要左边的部分 - 只是为了掌握right
一个。
如果仔细观察,您可能会注意到 Supplier
完全不需要,因此您甚至可以进一步简化它:
IntStream.range(1, 11)
.mapToObj(Integer::valueOf)
.map(right())
.forEach(System.out::println);