首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Java 8函数风格编程与函数组合有什么区别?

Java 8函数风格编程与函数组合有什么区别?
EN

Stack Overflow用户
提问于 2017-07-30 09:31:06
回答 3查看 1.4K关注 0票数 5

我对函数式programming.Trying世界非常陌生,这是Java8附带的新的函数风格编程。最近,我了解了有关运行和方法组合的知识。理解使用java进行函数式编程的真正本质是相当困难的,现在我有几个问题要问,但是,在问所有这些问题之前,我在python上尝试过同样的方法,现在对一些核心概念有点熟悉。

1.在java中,运行和方法组合是如何不同的--事实上,我根本没有看到任何区别,特别是在阅读本文https://dzone.com/articles/higher-order-functions之后

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

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2017-07-30 15:10:51

虽然这两个操作都输出一个函数,但该示例使区别变得相当清楚:

  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。即使是斯卡拉也没有。尝试从像Haskell这样的纯函数式语言中学习它,然后您就可以回到Java中,更好地理解在其中实现了哪些FP子集以及如何实现它。

票数 8
EN

Stack Overflow用户

发布于 2017-07-31 03:21:54

我想在@biziclop的非常好的解释中添加一些代码:

函数Java中的运行示例:

代码语言:javascript
复制
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。例子:

代码语言:javascript
复制
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变成8,从tens集合检索80

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

票数 7
EN

Stack Overflow用户

发布于 2017-07-30 14:17:50

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

一个典型的例子是有一个函数(addTwoNumbers a b),它添加了两个数字,其中currying提供了较少的参数,从而得到了一个使用其余参数来执行任务的函数。例如,提供a (42)但不提供b的(addTwoNumbers 42)是接受一个参数(b)并返回42+b的函数(而不是结果),因此((addTwoNumbers 42) 10)将返回52。

如您所见,语言语法必须有助于这一工作,而Java对此帮助不大,这就是它在教程中没有显示多少的原因。Java 8中的功能方面主要是为了避免代码中使用流的for-循环,并使用合理数量的预定义函数作为带有lambda表达式的脚手架。他们在流中有懒散的评估,这是一个非常好和伟大的成就,但在代码的表现力方面并没有给程序员带来太多的好处。

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

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/45398362

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档