Java 8 Function Style Programming 柯里化和函数组合有什么区别
Java 8 Function Style Programming what is the difference between currying and Functions Composition
我对函数式世界还很陌生 programming.Trying Java 附带的新函数式编程 8. 最近我开始了解柯里化和方法组合。理解使用 java 的函数式风格编程的真正本质是相当困难的,现在我有几个问题,但是,在问所有这些问题之前,我已经在 python 上尝试了同样的事情,现在有点熟悉几个核心概念。
1.In java Currying 和 method composition 有何不同其实我完全看不出有什么区别,尤其是看完这篇文章后 https://dzone.com/articles/higher-order-functions
2.As 一个程序员(从我的 java 编程角度来看)为什么我更喜欢柯里化。例如为什么我要这样做
f(x){ return g(y) }
而不是 f(x,y){ return x(y)}
有什么区别?
柯里化是一种通过现有函数的 "baking in" 个参数创建新函数的方法。这通常是在像 Haskell 这样的语言中完成的,其中语言语法倾向于轻松完成。
一个典型的例子是有一个函数 (addTwoNumbers a b)
将两个数字相加,其中 currying 是提供 less 个参数来获得一个函数,该函数接受剩余的参数来做事物。例如 (addTwoNumbers 42)
其中提供了 a (42) 但没有提供 b,是一个接受 one 参数 (b) 和 returns 的函数(不是结果) 42+b。所以 ((addTwoNumbers 42) 10)
会 return 52.
如您所见,语言语法必须对此有帮助才能正常工作,而 Java 帮助不大,这就是为什么它在教程中没有出现太多的原因。 Java 8 中的功能方面主要是为了避免在使用 Streams 的代码中出现 for 循环,并拥有合理数量的预定义函数以用作 lambda 表达式的脚手架。他们在 Streams 中有懒惰的评估,这是非常好的和伟大的成就,但在代码的表达能力方面并没有给程序员带来很多好处。
有关更多技术说明,请参阅 https://wiki.haskell.org/Currying。
虽然这两个操作都输出一个函数,但这个例子非常清楚地表明了区别:
- 柯里化采用单个函数
f()
并生成一个 "intermediate" 函数 f'()
,它与 f()
相同,但一些参数已经固定。当您最终填写其余参数时,您将评估原始 f()
.
- 而组合将使用两个函数
f()
和 g()
并创建一个完全不同的函数 g(f())
。
举个简单的例子:f(x,y) = x+y
,其中x
和y
是整数。这个函数的柯里化的数量和组合都不会产生一个永远 return 非整数结果的函数。但是将它与 g(x) = x/2
组合起来,你会得到 g(f(x,y)) = (x+y)/2
,这当然会很高兴 return 非整数。
那你为什么要使用柯里化?
例如,Java 实例方法是一个非常相似的过程的结果。实例方法与静态方法的不同之处在于它们有一个名为 this
的额外隐藏参数。当您说 new Foo()
时,实际上您将这个隐藏参数绑定到新创建的 Foo
对象。因此,不必调用函数 void bar(Foo this, int x)
,您只需将其引用为 void bar(int x)
,第一个参数已经固定到位。 (顺便说一句,void bar(Foo this, int x)
实际上是完全有效的 Java 语法,我们只是几乎从不使用它。)
这不完全是巧合,因为纯函数式语言只能具有输出仅取决于其输入的函数(与 OO 语言相反,在这种语言中,方法的输出也可能取决于对象的内部状态)该方法所属的对象。)
作为一般建议,如果你想学习函数式编程的精髓,最好不要从Java开始。甚至不是来自 Scala。尝试从像 Haskell 这样的纯函数式语言中学习它,然后您可以回到 Java 并更好地理解其中实现了 FP 的哪个子集以及如何实现。
我想添加一些代码来很好地解释@biziclop:
函数式柯里化示例 Java :
BiFunction<Integer, Integer, IntFunction<Integer>> currying = (x, y) -> z -> x * y / z;
System.out.println(currying.apply(5, 6).apply(2)); // 15
如您所见,lambda 已参数化。在此示例中,我们将 5 乘以 6,然后除以 2。
首先调用 apply(5)
,变量 x
获得值 5
,函数变为 5 * y / z
然后 apply(6)
被调用并且变量 'y' 获得值 '6' 并且函数变为 5 * 6 / z
然后 apply(2)
被调用并且变量 'z' 获得值 '2' 并且函数变为 5 * 6 / 2
因为您可以使用柯里化,所以这种方式在 Java 中用处不大。
柯里化在纯函数式语言中很有用,其中函数仅限于单个参数,它们受益于柯里化,柯里化转换采用多个参数的函数,因此可以多次调用单个参数调用。
那么如何从 Java 中的柯里化中获益?
当您需要在多个级别对函数进行参数化时,它很有用。例如,假设我们有几个集合,每个集合代表不同的类别,我们想从每个类别中检索特定元素。下面是一个简单的例子,给定两个集合,代表拼写的数字,分类为 ones
和 tens
。
示例:
public class Currying {
private static List<String> ones =
Arrays.asList("Zero", "One", "Two", "Three", "Four",
"Five", "Six", "Seven", "Eight", "Nine");
private static List<String> tens =
Arrays.asList("Zero", "Ten", "Twenty", "Thirty", "Forty",
"Fifty", "Sixty", "Seventy", "Eighty", "Ninety");
public static Function<String, Function<Integer, String>> getNumbers() {
return units -> number -> {
return units == "Ones" ? ones.get(number % 10)
: tens.get(number % 10);
};
}
public static void main(String[] args) {
Function<String, Function<Integer, String>> currying = getNumbers();
System.out.println(currying.apply("Tens").apply(8)); // 80
System.out.println(currying.apply("Ones").apply(2)); // 2
}
}
在上面的示例函数中 currying
returns 另一个函数
currying.apply("Ones").apply(2))
;
首先调用 apply("Tens")
并且变量 units
变为 Tens
然后 apply(2)
被调用,变量 number
变为 8
从 tens
集合中检索 80
。
同样的逻辑适用于 currying.apply("Ones").apply(2))
。
我对函数式世界还很陌生 programming.Trying Java 附带的新函数式编程 8. 最近我开始了解柯里化和方法组合。理解使用 java 的函数式风格编程的真正本质是相当困难的,现在我有几个问题,但是,在问所有这些问题之前,我已经在 python 上尝试了同样的事情,现在有点熟悉几个核心概念。
1.In java Currying 和 method composition 有何不同其实我完全看不出有什么区别,尤其是看完这篇文章后 https://dzone.com/articles/higher-order-functions
2.As 一个程序员(从我的 java 编程角度来看)为什么我更喜欢柯里化。例如为什么我要这样做
f(x){ return g(y) }
而不是 f(x,y){ return x(y)}
有什么区别?
柯里化是一种通过现有函数的 "baking in" 个参数创建新函数的方法。这通常是在像 Haskell 这样的语言中完成的,其中语言语法倾向于轻松完成。
一个典型的例子是有一个函数 (addTwoNumbers a b)
将两个数字相加,其中 currying 是提供 less 个参数来获得一个函数,该函数接受剩余的参数来做事物。例如 (addTwoNumbers 42)
其中提供了 a (42) 但没有提供 b,是一个接受 one 参数 (b) 和 returns 的函数(不是结果) 42+b。所以 ((addTwoNumbers 42) 10)
会 return 52.
如您所见,语言语法必须对此有帮助才能正常工作,而 Java 帮助不大,这就是为什么它在教程中没有出现太多的原因。 Java 8 中的功能方面主要是为了避免在使用 Streams 的代码中出现 for 循环,并拥有合理数量的预定义函数以用作 lambda 表达式的脚手架。他们在 Streams 中有懒惰的评估,这是非常好的和伟大的成就,但在代码的表达能力方面并没有给程序员带来很多好处。
有关更多技术说明,请参阅 https://wiki.haskell.org/Currying。
虽然这两个操作都输出一个函数,但这个例子非常清楚地表明了区别:
- 柯里化采用单个函数
f()
并生成一个 "intermediate" 函数f'()
,它与f()
相同,但一些参数已经固定。当您最终填写其余参数时,您将评估原始f()
. - 而组合将使用两个函数
f()
和g()
并创建一个完全不同的函数g(f())
。
举个简单的例子:f(x,y) = x+y
,其中x
和y
是整数。这个函数的柯里化的数量和组合都不会产生一个永远 return 非整数结果的函数。但是将它与 g(x) = x/2
组合起来,你会得到 g(f(x,y)) = (x+y)/2
,这当然会很高兴 return 非整数。
那你为什么要使用柯里化?
例如,Java 实例方法是一个非常相似的过程的结果。实例方法与静态方法的不同之处在于它们有一个名为 this
的额外隐藏参数。当您说 new Foo()
时,实际上您将这个隐藏参数绑定到新创建的 Foo
对象。因此,不必调用函数 void bar(Foo this, int x)
,您只需将其引用为 void bar(int x)
,第一个参数已经固定到位。 (顺便说一句,void bar(Foo this, int x)
实际上是完全有效的 Java 语法,我们只是几乎从不使用它。)
这不完全是巧合,因为纯函数式语言只能具有输出仅取决于其输入的函数(与 OO 语言相反,在这种语言中,方法的输出也可能取决于对象的内部状态)该方法所属的对象。)
作为一般建议,如果你想学习函数式编程的精髓,最好不要从Java开始。甚至不是来自 Scala。尝试从像 Haskell 这样的纯函数式语言中学习它,然后您可以回到 Java 并更好地理解其中实现了 FP 的哪个子集以及如何实现。
我想添加一些代码来很好地解释@biziclop:
函数式柯里化示例 Java :
BiFunction<Integer, Integer, IntFunction<Integer>> currying = (x, y) -> z -> x * y / z;
System.out.println(currying.apply(5, 6).apply(2)); // 15
如您所见,lambda 已参数化。在此示例中,我们将 5 乘以 6,然后除以 2。
首先调用 apply(5)
,变量 x
获得值 5
,函数变为 5 * y / z
然后 apply(6)
被调用并且变量 'y' 获得值 '6' 并且函数变为 5 * 6 / z
然后 apply(2)
被调用并且变量 'z' 获得值 '2' 并且函数变为 5 * 6 / 2
因为您可以使用柯里化,所以这种方式在 Java 中用处不大。 柯里化在纯函数式语言中很有用,其中函数仅限于单个参数,它们受益于柯里化,柯里化转换采用多个参数的函数,因此可以多次调用单个参数调用。
那么如何从 Java 中的柯里化中获益?
当您需要在多个级别对函数进行参数化时,它很有用。例如,假设我们有几个集合,每个集合代表不同的类别,我们想从每个类别中检索特定元素。下面是一个简单的例子,给定两个集合,代表拼写的数字,分类为 ones
和 tens
。
示例:
public class Currying {
private static List<String> ones =
Arrays.asList("Zero", "One", "Two", "Three", "Four",
"Five", "Six", "Seven", "Eight", "Nine");
private static List<String> tens =
Arrays.asList("Zero", "Ten", "Twenty", "Thirty", "Forty",
"Fifty", "Sixty", "Seventy", "Eighty", "Ninety");
public static Function<String, Function<Integer, String>> getNumbers() {
return units -> number -> {
return units == "Ones" ? ones.get(number % 10)
: tens.get(number % 10);
};
}
public static void main(String[] args) {
Function<String, Function<Integer, String>> currying = getNumbers();
System.out.println(currying.apply("Tens").apply(8)); // 80
System.out.println(currying.apply("Ones").apply(2)); // 2
}
}
在上面的示例函数中 currying
returns 另一个函数
currying.apply("Ones").apply(2))
;
首先调用 apply("Tens")
并且变量 units
变为 Tens
然后 apply(2)
被调用,变量 number
变为 8
从 tens
集合中检索 80
。
同样的逻辑适用于 currying.apply("Ones").apply(2))
。