【问题标题】:Java 8 Function Style Programming what is the difference between currying and Functions Composition [closed]Java 8函数样式编程currying和函数组合有什么区别[关闭]
【发布时间】:2017-07-30 09:31:06
【问题描述】:

我对函数式编程世界很陌生。尝试 Java 8 附带的新函数式编程。最近我开始了解柯里化和方法组合。理解使用 java 进行函数式编程的真正本质非常困难,现在我有几个问题,但是,在提出所有这些问题之前,我已经在 python 上尝试过同样的问题,现在对一些核心概念有点熟悉。

1.在java中Currying和方法组合有什么不同其实我完全看不出有什么区别,尤其是看了这篇文章https://dzone.com/articles/higher-order-functions

2.作为一个程序员(从我的 java 编程角度来看)为什么我更喜欢柯里化。例如我为什么要这样做 f(x){ return g(y) } 而不是 f(x,y){ return x(y)} 有什么区别?

【问题讨论】:

  • Java 8 只有函数式编程通常允许的一小部分,因此可能很难看到好处。
  • @ThorbjørnRavnAndersen 和一般情况下?不考虑 java 我的第二个问题的答案是什么。我真的很想知道
  • 那篇文章是错误的。正如@Holger在accepted answer's comments中指出的那样,文章作者所说的currying其实是偏函数应用,也可以说他是绑定第一个或第二个参数。
  • @FedericoPeraltaSchaffner 我明白你的意思,现在我更困惑了。

标签: java lambda functional-programming java-8 currying


【解决方案1】:

虽然两个操作都输出一个函数,但该示例使区别相当明显:

  1. Currying 采用单个函数f() 并生成一个“中间”函数f'(),它与f() 相同,但一些参数已经固定。当您最终填写其余参数时,您将评估原始f()
  2. 而组合将采用两个函数 f()g() 并创建一个完全不同的函数 g(f())

举个简单的例子:f(x,y) = x+y,其中xy 是整数。此函数的任何数量和组合都不会导致函数返回非整数结果。但是用g(x) = x/2 组合它,你会得到g(f(x,y)) = (x+y)/2,它当然会很高兴地返回非整数。

你为什么要使用柯里化?

例如,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 的哪个子集在其中实现以及如何实现。

【讨论】:

  • 太好了,现在我明白了函数式风格和纯函数式编程之间的区别。
  • 柯里化(a,b) -> a + b 转换为a -> b -> a + b。目前,还没有解决任何争论。仅当您将值应用于外部函数时,返回的函数才有a 的固定值。这称为部分函数应用程序
  • 你是对的,示例一步完成。
【解决方案2】:

我想为@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 中的柯里化中受益呢?

当您需要在多个级别上参数化函数时,它很有用。例如,假设我们有几个集合,每个集合代表不同的类别,我们想要从每个类别中检索特定元素。下面是一个简单的例子,给定两个集合,代表拼写数字,分类为onestens。 示例:

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返回另一个函数 currying.apply("Ones").apply(2));

首先调用apply("Tens"),变量units变为Tens

然后apply(2) 被调用,变量number 变为8tens 集合中检索80

同样的逻辑适用于currying.apply("Ones").apply(2))

【讨论】:

  • 美丽的例子。非常感谢。
  • BiFunction&lt;Integer, Integer, IntFunction&lt;Integer&gt;&gt; 是一个很好的例子,说明为什么在 Java 中这样做相当乏味。
【解决方案3】:

Currying 是一种通过“烘焙”现有函数的参数来创建新函数的方法。这通常在 Haskell 等语言中完成,其语言语法倾向于轻松完成。

一个典型的例子是有一个函数(addTwoNumbers a b),它将两个数字相加,其中currying是提供less个参数来获得一个函数,该函数接受剩余的参数来做事。例如(addTwoNumbers 42),其中提供了 a (42) 但未提供 b,它是一个函数(不是结果),它接受 one 参数 (b) 并返回 42+b。所以((addTwoNumbers 42) 10) 会返回 52。

如您所见,语言语法必须有助于使其正常工作,而 Java 没有多大帮助,这就是为什么它在教程中没有出现太多的原因。 Java 8 中的功能方面主要是为了避免使用 Streams 的代码中的 for 循环,并有合理数量的预定义函数用作 lambda 表达式的脚手架。他们在 Streams 中进行了惰性求值,这非常好,也是一项了不起的成就,但在代码表达能力方面并没有给程序员带来太多好处。

更多技术解释请参见https://wiki.haskell.org/Currying

【讨论】:

    猜你喜欢
    • 2010-09-06
    • 1970-01-01
    • 1970-01-01
    • 2011-03-24
    • 2018-10-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多