函数式接口、默认方法、纯函数、函数的副作用、高阶函数、可变的和不可变的、函数式编程和 Lambda 表达式 - 响应式编程 [Android RxJava2](这到底是什么)第三部分

本文讲的是函数式接口、默认方法、纯函数、函数的副作用、高阶函数、可变的和不可变的、函数式编程和 Lambda 表达式 - 响应式编程 [Android RxJava2](这到底是什么)第三部分,


太棒了,我们又来到新的一天。这一次,我们要学一些新的东西让今天变得有意思起来。

大家好,希望你们都过得不错。这是我们的 RxJava2 Android 系列的第三篇文章.

在这篇文章中,我们将讨论函数式的接口,函数式编程,Lambda 表达式以及与 Java 8 的相关的其它内容。这对每个人近期都是有帮助的。

动机:

动机和我在分享第一部分时一致。Lambda 表达式、函数式编程、高阶函数等等总是让我在使用 Java 时很痛苦,因为大家都知道,Java 是面向对象编程的。所以,Java 怎么可能支持函数式编程。那么,在函数式编程里,Lambda 表达式的角色是什么呢?为了让所有问题变得简单明了,我会从函数式接口开始。重要的是,我向你们保证,只要你们 100% 看完这部分,你们将会对最近我们听到的所有名字都感觉自在很多。函数式接口,默认方法,纯函数,函数的副作用,高阶函数,可变的与不可变的,函数式编程与 Lambda 表达式。我觉得很多人最近都在使用 Lambda 表达式,但或许在读完这篇文章后,他们会更了解 Lambda 表达式。攻克难题的时刻到了。

修改:

第一部分,我们讨论了 Rx 最重要、最基础也最核心的概念,那就是观察者模式。在第二部分,我们讨论了拉模式和推模式,以及命令式和响应式编程。

介绍:

今天我们将会弄清楚所有关于函数式接口、默认方法、纯函数、函数的副作用、高阶函数、可变的和不可变的、函数式编程以及 Lambda 表达式的所有困惑。所以为了方便理解 Lambda 表达式的概念,我要先解释什么是函数式接口。

函数式接口:

一言以蔽之,函数式接口是有且只有一个抽象方法的接口。换言之,任何拥有唯一抽象方法的接口都可以被称为函数式接口。这里我想分享一些背景知识,这些知识不属于这个系列,但是对你面试尤其有用。如果你读过我的定义。我用了关键词抽象,众所周知的是接口里的方法都是抽象的,但那是 Java 8 出现之前的情况。在 Java 8 里,我们可以在接口中定义一个包含方法体的方法,这个方法叫默认方法,正如下面所示。

public interface Account {

   void name();

   default void showTyepOfAccount(){
      System.out.println("Don't know :(" );
   }
}

现在我们要回顾一下定义。函数式接口是个拥有一个抽象方法的接口。

所以现在,如果我问你上面的接口是不是一个函数式接口,你的答案是什么?根据定义,答案应该是:不是。但那却是一个有效的函数式接口,为什么呢……

现在,如果接口定义默认方法或者继承并重写 java.lang.Object 类里的任何方法。那个接口还是函数式接口,这是因为java.lang.Object 方法并不算数。正如我在下面展示给你的真正的函数式接口。

public interface Add {
    void add(int a, int b);

    @Override
    String toString();

    @Override
    boolean equals(Object o);
}

所以,任何有多于一个抽象方法的接口不能被称为函数式接口,正如下面所示。

public interface Do {

    void why();

    void sorry();
}

我相信你已经理解了函数式接口的概念。这也是 Lambda 表达式重要的核心概念,一定要好好记住。

一些我们现在日常开发使用的函数式接口的例子:

public interface Runnable {
    public abstract void run();
}

public interface OnClickListener {
    void onClick(View v);
}

现在是时候向你展示 Java 7 和 8 的 Comparator 接口了。它们都是有效的函数式接口。

Java 7 的比较器:

在 Java 8 里:

可别搞混了。它们都是有效的函数式接口。只要记住函数式接口的三点原则。

只有一个抽象方法 - 可以有默认方法 - 可以使用 java.lang.Object 方法。

如果任何接口满足这三点,那就一定是有效的函数式接口,反之则不是。

在 Java 8 里有一个新的工具包 java.util.function。在这个包里,所有的接口都是函数式接口。当我们需要用到流(Stream) API 时,这个工具包很有用。当我们开始学习 Rx Android 的时候,这个包会让我们学到更多。

很重要的一点。当我们要开始使用 Rx Android 时,我们会使用很多这样的函数式接口。基本上,在安卓平台中,我们依赖于 Rx Java 和 Rx Android。现在,我将要给你看一看 Rx Java 1.0 和 2.0 包里的函数式接口。没有必要去记住这个,也没有必要紧张,这只是通用知识。只要试着记得函数式接口的概念就可以了。当你开始使用 Rx,这些你都会在潜移默化中记住的。

RxJava 1:

RxJava2:

哇哦!我们该庆祝一下我们已经知道什么是函数式接口,以及在 Java 8 里什么是默认方法。我在介绍一栏中写到的本章需要探讨的概念,已经解释完两个了。函数式接口、默认方法、纯函数、函数的副作用、高阶函数、可变的和不可变的、函数式编程和 Lambda 表达式。

函数式编程:

说实话,我的大多数工作都是用 Java 和 C++ 完成的,而这两种语言都是命令式的而非纯函数式的。所以我打算尽力解决所有的我面对的困惑。如果我有什么地方弄错了,请不要介意。不过务必在回复里提醒我,这样我就可以修正我的文章了。

在进入无聊的定义之前,我打算回顾我们在学校里学习到的理论。这对接下来阐释剩下模棱两可的名词是很有帮助的。

每个做开发的人都知道函数。但是现在请试着忘记我们学习过的所有编程知识,重回学校。

好孩子。

数学概念上的函数是什么?[现在请忘掉你所知道的 Java 或者 C++ 等任何编程语言关于函数的所有知识。]

什么是函数?一个根据输入决定输出的方程式。挺无聊的,好的,那忘记这个。

有多少人听过下面的句子。

f(x) = x+3

如果 x = 2,答案是什么。

f(x) 等于 y。

y = x+3

x = 2

y = 2+3

y = 5

所以,f(x) = x+3 是一个函数。当你给同一个输入,会给你同样的输出。

再来一个例子。

有多少人记得 Sin(x) [ 三角函数 ]

我们当然记得。在学校的时候,对一个 45° 的角取正弦,我会得到 1/2 的答案,如下所示。

y = Sin(45deg)

y = 1/2

后来我在大学时代里也用过相同意义上的“函数”。对于给定的输入值,会得到唯一的结果。这就叫纯函数。我会在接下来解释。

我们回顾了在大学里常用的一些函数。现在,当我们在编程中用同样的思想,这就叫函数式编程。不要紧张,我马上就会解释。我们从儿时的回忆里回来看看。

首先,我们要讨论一些困惑。比如当我们刚开始写程序的时候,都写过一个计算圆面积的函数。

public double areaOfACircle(int radius){
    return radius*radius*3.14;
}

很好。随着我变得更专业,我对函数认识也不同了。比如,写一个美元转巴基斯坦卢比的汇率计算器。

public float convertUSDIntoPKR(int USD){
    return USD*getTodayPKRValueFromAPI();
}

在编程中,上面的是一个函数。但是在数学中,这就有问题了。因为在数学中,我们总是说,同一个输入对应同样的输出。但是编程中的函数给同样的输入可以有不同的输出,因为它依赖于其它数值。所以这里,我们又要介绍一个名词,叫纯函数。在数学概念里,我们知道每个函数都是纯函数,如 Sin(),但是,在我们的编程语言里,我们有很多函数给我们不同的数值。所以,这就是我们要介绍的,编程语言里的纯函数。 纯函数的返回值由它的输入值决定,而且没有明显可见的副作用。

下一个名词,副作用。任何不纯的函数叫非纯函数,它可能产生副作用。或者一些函数本身是纯函数(指对于给定的输入值可以得出相同的输出值),但是如果它在产生结果的时候与外界发生了数据交换,那么我们就不能说这是一个纯函数。

第一类非纯函数的典型就是 Random 函数。对于给定的一个输入值,它总是返回不同的结果。

第二类副作用的典型是 println() ,它是一个非纯的函数。因为它将输出值转去了输入输出设备(而不是作为函数返回值输出),所以产生了副作用。任何纯函数一旦用 println() 来注释打印,那它就不再是纯函数了。

一些例子:

纯函数:

public int squre(int x){
    return x*x;
}

因为副作用而非纯的的函数;

public int squre(int x){
    System.out.println(x*x);
    return x*x;
}

非纯函数:

public void login(String username, String password, Callback c){
    API.login(username, password, callback);
}

现在我们又理解了两个名词。纯函数和副作用。

函数式接口、默认方法、纯函数、函数的副作用、高阶函数、可变的和不可变的、函数式编程和 Lambda 表达式。

接下来,我们准备讨论可变的不可变的。在数学中,我们记得,当我给函数一个值,我总能获得新的值,而我原来的值还是一样的。但是,在编程中,那个概念就变了。这时为什么我们有两种不同的定义。可变的和不可变的。在面向对象中,我们几乎无时不刻不在破坏不可变性。这可能导致很多问题,但是函数式编程总是利用不可变性。正如每个人都知道在 Java 里,String 是不可变的。

String s = "Hello";
s = "World";

这里,我们本来的字符串从未改变。虽然第二行我们创建了新的字符串并且把它赋给我的 s 对象。

所以,什么是可变的?给你一个例子。

int array []= {1,2,3,4,5};
for (int i = 0; i < array.length; i++) {
    array[i] = array[i] * 2;
}

在 Java 或者命令式编程中,我认为上面的代码基本上是可变的。它改变了原本的数组值。但是在函数式编程里,如果我做了同样的事情,我总是获得与 2 相乘后的数值组成的新数组,而我原来的数据仍然保持不变。

Integer array []= {1,2,3,4,5};
Arrays.stream(array).map(v->v*2).forEach(i-> System.out.print(i+" "));
System.out.println();
for (int i = 0; i < array.length; i++) {
    System.out.print(array[i]+ " ");
}
Output:
2 4 6 8 10
1 2 3 4 5

上面的例子是用 Java 8 写的,但是那跟之后讲 Rx 是一样的。举出这个例子,只是为了帮助你理解可变和不可变的概念。正如你所看到的,所输出的原本的数组值并没有改变。

现在可能你在想这样的好处是什么。我这里用另外一个例子来解释。如果我知道我所有的函数都是纯的并且是不可变的,我可以做很多事情而不用管我数据的状态。例如,我要使用线程。

public class FunctionalLambda {

    public static void main(String[] args) {

        Integer array []= {1,2,3,4,5};
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < array.length; i++) {
                    array[i] = array[i]+1;
                }
            }
        }).start();
        for (int i = 0; i < array.length; i++) {
            System.out.println(square(array[i]));
        }
    }
    public static int square(int a){
        return a*a;
    }
}

在这个例子里,基本上我用到了线程。子线程让数组里的每一个数据 + 1,而主线程或者其他子线程则对数组中的数据做平方运算。作为一个开发者,我期望数值应如下所示。

1
4
9
16
25

但是,当我执行这段代码时,得到的结果如下。

4
9
16
25
36

结果和期望并不相同,因为我没有管数据可变性。现在我准备写一个合适的函数式程序,对数据的不可变性进行严格控制。

public class FunctionalLambda {

    public static void main(String[] args) {

        Integer array []= {1,2,3,4,5};
        new Thread(new Runnable() {
            @Override
            public void run() {
                Observable.from(array)
                        .map(integer -> integer+1)
                        .subscribe(integer -> {});
            }
        }).start();

        Observable.from(array)
                .map(integer -> square(integer))
                .subscribe(integer -> System.out.println(integer));

    }

    public static int square(int a){
        return a*a;
    }
}

注意:如果要运行上面的例子,你需要下载 rxjava 的 jar 包

运行完这段例子后,我所期望的和实际输出的是一致的,因为我的程序没有对数组做直接改变,而是拷贝了我的数据。这就是为什么我可以说我的数组是不可变的。对不起,我也用 Rx 了。但是从现在开始,我会加一点 Rx 到我的例子里。我会在接下来的文章中解释清楚。但是,请相信我,那是一个函数式程序。在程序里,我有一个纯函数做平方运算,并且我的数组不改变,因为我将使用函数式范式。

函数式接口、默认方法、纯函数、函数的副作用、高阶函数、可变的和不可变的、函数式编程和 Lambda 表达式。

是时候解释清楚高阶函数 (HOF) 的含义了。

拥有至少一个函数类型为参数的函数,或着返回一个函数的函数叫做高阶函数。

那简直太简单了,并且我们在 Rx 编程中用了很多这个概念。在 Java 8 之前,展示 HOF 还是有点困难的,但是我们使用匿名类作为 HOF。我们大多在 C++ 中使用这个概念,把函数作为一个参数。在安卓中,这就类似于添加一个匿名类为点击事件监听者。所以你可以说,这是 HOF 的一个例子。我会在介绍 Rx 的文章中更详细地解释这个。

函数式接口、默认方法、纯函数、函数的副作用、高阶函数、可变的和不可变的、函数式编程和 Lambda 表达式。

现在,如果我们使用这些概念,在任何语言中,我们所讨论的纯函数,HOF,不可变的都是接下来的函数式范式。那就是函数式编程。在面向对象编程时我们经常要管理对象的状态,但是在函数式程序里,我们有数据,管理好了不可变性,我们可以大胆地做运算。

函数式接口、默认方法、纯函数、函数的副作用、高阶函数、可变的和不可变的、函数式编程和 Lambda 表达式。

加油呀!我们已经弄清楚了很多关于函数式编程模棱两可的概念。现在我们要用学习 Lambda 表达式来结束这篇文章。

在进入 Lambda 的章节前,我想复习一下前面的内容。

函数式接口 - 有且仅有一个抽象方法的接口。

默认方法 - 在 Java 8 里,我们可以在接口中定义有方法体的方法,这些叫默认方法。

纯函数 - 一个函数的返回值仅由输入值决定,没有明显可见的副作用。

Lambda 表达式:

“在计算机编程中,lambda 表达式,也叫匿名函数,是指一类无需定义标识符(函数名)的函数或子程序。”(Wiki)

首先,RxJava 并不依赖于 Lambda 表达式。实际上,函数式编程与 Lambda 表达式没有关系,正如你在我以上的例子中看到的那样,我从来没有说过我用了 lambda。只是 IDE 在某些地方可能把我的代码转换成了 lambda 表达式,但我可以不用它来写代码。那么,问题是,为什么在每一篇关于 Rx 或者函数式编程的博客里,我们看到 lambda 表达式总是核心内容。在我看来,你可以把它们理解为简洁高效的匿名函数语法。

在我详细介绍 Lambda 表达式前,有个先决条件。我们已经知道 Java 是一个静态类型语言。它意味着所有的 java 程序对象和变量总是在编译时间里知道数据类型,如下面的例子所示。

int i = 1;
float j = 3;
Person person = new Person();
String s = "Hello";

同样的,在 Java 7 之前,我们准备用 Collections 来写一个完整的 List 对象初始化,如下所示。

List<String > list = new ArrayList<String >();

但在 Java 7,我们有类型引用的概念。使用这个概念,我们可以写出如下简洁的代码。

List<String > list = new ArrayList<>();

所以现在,编译器在编译时根据上下文决定数据类型。这样,我们就节省了很多时间。

再一次,数据类型引用非常重要。所以我们要关注这个。在 Lambda 表达式中,我们要用到很多次,但是大家因为缺少这个概念而感到困惑。

我们继续用另外一个例子来描述同一个概念。

我写了一个方法,整数作为参数传入,而这个方法将不改变任何东西,返回同样的数值给我,如下所示。

public static void main(String [] args){
    System.out.println(giveMeBack(1));
}

public static int  giveMeBack(int a){
    return a;
}

这是简单的例子。现在我想传个 3.14 给这个方法,有没有人告诉我,会发生什么呢?

是的,你的程序将无法编译。我已经说过了,这个方法只能传入整数。我的下一个要求是,我要使得这个方法适用于所有数据类型。作为一个开发者,我是一个懒人。我不想写重复的代码。这里我想利用 Java 的引用。

public static<T> T  giveMeBack(T a){
    return a;
}

这也叫泛型。利用泛型,我节省了很多时间。这个方法可以适用于任何数据类型,如下图所示。

现在我从 Java 引用中获得了好处。怎么样获得的呢?我的编译器,编译我的程序,为我的所有数据类型生成了代码。现在,编译器可以很容易地从我的参数的数据类型做决定。这里没有什么神奇的地方。每当我没有提到数据结构,我的编译器就从上下文中提取并且赋予其数据类型,因为 Java 是一个静态类型语言。

再重复一遍,Java 是一个静态类型语言。所以如果你觉得你在 IDE 中写的代码没有任何类型。你可能会认为你使用的是一个动态类型语言。你错了,你只是在利用 Java 类型推断而已。

现在,我们可以开始写 Lambda 表达式了。目前,Lambda 表达式只支持 Java 8。在安卓中,如果我们想用它,我们可以用 Retrolambda 库。现在们来解释一下 lambda 表达式。

在安卓中,我想要一个可监听点击事件的按钮,如下面代码所示。

Button button = new Button(this);
button.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        // Click
    }
});

这里我们传入了一个 OnClickLisetener 的匿名对象。当用户点击,onClick 方法就会被调用。现在我们要用 Lambda 表达式改变这个匿名的,恶心的,复杂的代码。

Button button = new Button(this);
button.setOnClickListener((View v)->{
    // Click
});

通过使用 Lambda 表达式,我的代码可读性更强了。我准备再重构一下上面的例子。

button.setOnClickListener(v -> /* Click */);

我真的很喜欢写类似上面的代码,但是在开始的时候,我真的很困惑,编译器是如何知道我这里在做什么。首先,我利用了 Java 引用。就像编译时,Java 自动知道‘v'是一个 View,因为我们用的是函数式接口。这个接口只有一个抽象方法,它的参数是一个 view,如下所示。

/**
 * Interface definition for a callback to be invoked when a view is clicked.
 */
public interface OnClickListener {
    /**
     * Called when a view has been clicked.
     *
     * @param v The view that was clicked.
     */
    void **onClick**(View v);
}

还记得接口函数的概念吗?现在所有的线索都被串起来了。我们已经讨论了函数式接口。它意味着任何以函数式接口为参数的方法,我就可以写成 Lambda 表达式。这意味着,Lambda 表达式是一个语法糖。我觉得你们现在已经知道 Lambda 表达式是个什么东西了。这就是为什么我要关注函数式接口和其它名词了。

再来一个例子。

Without Lambda:

Thread thread = new Thread(new Runnable() {
    @Override
    public void run() {
        // Without Lambda
    }
});
thread.start();

使用 Lambda 表达式:

Thread thread = new Thread(()->{});
thread.start();

在 Java 8 或者 Rx Java 中,我们会使用很多函数式接口,因为我们想写出简单明了的代码,并且寥寥数语就可以完成一个大功能。现在我觉得所有的困惑都已经清晰了。这里有一些关于 Lambda 表达式更重要的点。

如果当按钮被按下时,我想写一行代码,我可以写成下面这样。

button.setOnClickListener(v -> System.out.println());

但如果我想写不止一行,那么我需要把它们写进花括号里,如下所示。

button.setOnClickListener(v -> {
    System.out.println();
    doSomething();
});

我可以明确提及数据类型,如下所示。

button.setOnClickListener((View v) -> System.out.println());

现在,如何返回 Lambda 表达式类型呢?再给你一个例子。

public interface Add{
    int add(int a, int b);
}

private Add add= new Add() {
    @Override
    public int add(int a, int b) {
        return a+b;
    }
};

int sum = add.add(1,2);

现在我使用 Lambda 表达式来表现同一个例子。

public interface Add{
    int add(int a, int b);
}

private Add add = (a, b) -> a+b;

int sum = add.add(1,2);

现在可以看到我写的代码有多简洁了。它们的功能是一样的。我没有提及任何返回的数据类型,因为 Java 的类型引用自动帮我决定了这是一个整型。现在,如果我想添加更多的代码到 add 方法的实现中,只需要像下面那样写就行了。

public interface Add{
    int add(int a, int b);
}

private Add add = (a, b) -> {
    System.out.println();
    return a+b;
};

int sum = add.add(1,2);

现在我们知道函数式接口、默认方法、纯函数、函数的副作用、阶函数、可变的和不可变的、函数式编程和 Lambda 表达式。

结论:

大家都太棒了。今天我们到达了一个 Rx 学习中的里程碑。下一篇文章是 War against Learning Curve of Rx Java 2 + Java 8 Stream [ Android RxJava2 ] ( What the hell is this ) Part4。到现在为止,我们了解了观察者模式、拉模式与推模式、响应式与命令式、函数式接口、默认方法、纯函数、函数的副作用、高阶函数、可变的和不可变的、函数式编程和 Lambda 表达式。我认为,如果你都了解了上述名词,Rx 的学习将会越来越简单。现在我感觉你们都已经了解了,所以接下来 Rx 的学习对于我们都会更简单。

祝你们有个愉快的周末。让我们下周再见吧。






原文发布时间为:2017年5月10日


本文来自合作伙伴掘金,了解相关信息可以关注掘金网站。

时间: 2024-11-03 22:27:32

函数式接口、默认方法、纯函数、函数的副作用、高阶函数、可变的和不可变的、函数式编程和 Lambda 表达式 - 响应式编程 [Android RxJava2](这到底是什么)第三部分的相关文章

匿名方法,Lambda表达式,高阶函数

原文:匿名方法,Lambda表达式,高阶函数 匿名方法 c#2.0引入匿名方法,不必创建单独的方法,因此减少了所需的编码系统开销. 常用于将委托和匿名方法关联,例如 1. 使用委托和方法关联: this.btnRefresh.Click += new System.EventHandler(this.btnRefresh_Click);private void btnRefresh_Click(object sender, EventArgs e){    BindData();} 2. 使用委

高阶函数、委托与匿名方法

高阶函数(higher-order function)是指把另一个函数作为参数或返回值的函数.例如 在JavaScript语言中,Function是顶级类型.一个函数就是类型为 Function的顶级对象,自 然就可以作为另一个函数的参数或返回值.例如在Microsoft AJAX Library(ASP.NET AJAX 的客户端类库)中有一个被广泛使用的createDelegate方法.该方法接受一个对象A和一个函 数F作为参数,并返回一个函数R.当调用函 数R时,F函数将被调用,并且保证无

JS编程建议——74:使用高阶函数

建议74:使用高阶函数高阶函数作为函数式编程众多风格中的一项显著特征,经常被使用.实际上,高阶函数即对函数的进一步抽象.高阶函数至少满足下列条件之一:接受函数作为输入.输出一个函数. 在函数式语言中,函数不但是一种特殊的对象,还是一种类型,因此函数本身是一个可以传来传去的值.也就是说,某个函数在刚开始执行的时候,总可以送入一个函数的参数.传入的参数本身就是一个函数.当然,这个输入的函数相当于某个函数的另外一个函数.当函数执行完毕之后,又可以返回另外一个新的函数,这个返回函数取决于return f

python精简笔记(四)——高阶函数

以Python内置的求绝对值的函数abs()为例 >>> f = abs >>> f(-10) 10 变量f现在已经指向了abs函数本身.直接调用abs()函数和调用变量f()完全相同. 函数名其实也是变量 abs指向10后,就无法通过abs(-10)调用该函数了!因为abs这个变量已经不指向求绝对值函数而是指向一个整数10 >>> abs = 10 >>> abs(-10) Traceback (most recent call l

高阶函数一点通

本文讲的是高阶函数一点通, 理解快速变化的 React 最佳实践 如果你刚开始接触 React,你可能已经听说过 "高阶组件" 和 "容器" 组件.你也许会奇怪这都什么鬼东西.或者你已经开始使用库提供的 API 了,但对于这些个术语还有些疑惑. 作为 Apollo 的 React 集成 - 一个重度使用高阶组件的热门开源库 - 的维护者和文档作者,我花了些时间来理清这些概念. 我希望这篇文章能够帮你对这一主题有更进一步的了解. 重识 React 本文假定你已对 Re

不学点高阶函数,如何愉快的装逼!

如果你开始接触函数式编程,你一定听说过高阶函数.在维基百科它的中文解释是这样的: 在数学和计算机科学中,高阶函数是至少满足下列一个条件的函数: 接受一个或多个函数作为输入 输出一个函数 看起它就是ObjC语言中入参或者返回值为block的block或者函数,在Swift语言中即为入参或者返回值为函数的函数.那它们在实际的开发过程中究竟起着什么样的作用呢?我们将从入参.返回值和综合使用三部分来看这个问题: 函数作为入参 函数作为入参似乎无论在ObjC时代还是Swift时代都是司空见惯的事情,例如A

高阶函数与JavaScript

简单的 JavaScript? Web 语言 JavaScript 的原意被很多人曲解了,绝大部分的人(包括以前我在做项目的时候)其实都没有正确地使用 HTML 和 CSS.几乎没有人是通过FP的特性规范来学习 JavaScript 的,他们都是通过流行的 OO 设计教材和阅读以前遗留的代码来学习的.很遗憾的是 OO 这两种学习途径对于他们真正理解 JavaScript 理念都没有多少帮助,甚至还存在着大量的误导.比如在编码中仍未摆脱大量的过程式编程,或着更"高级一点",大量使用伪类来

ES6中的高阶函数:如同 a =&gt; b =&gt; c 一样简单

作者:Sequoia McDowell 2016年01月16日 ES6来啦!随着越来越多的代码库和思潮引领者开始在他们的代码中使用ES6,以往被认为是"仅需了解"的ES6特性变成了必需的代码常识.这不仅仅是新的语法学习 - 在许多范例中, ES6中新的语言特性可以让在ES5中写起来非常麻烦的表达变得更加简单,进而鼓励了新表达方式的使用.下面我们将关注一个这样简洁表达的使用范例:ES6中的箭头函数如何使高阶函数的书写更加简便. 高阶函数是至少具有以下两种功能之一的函数: 使用一个或多个函

Coursera Scala 5-4:List的高阶函数

Coursera Scala 5-4:List的高阶函数 Recurring Patterns for Computations on Lists 重复出现的Lists计算模式 lists的很多函数有相似的结构,重复出现的模式有: 用某个方法转换每个元素 用某个条件提取元素 用某种方法链接元素 函数式编程语言,让程序员能写出更通用的计算模式,通过使用高阶函数. Applying a Function to Elements of a List 将一个list的所有元素,进行转换.例子:返回一个新