函数式编程:一等函数(First-class Function)

说起函数式编程,不得不提的是First-class Function的概念,有些文章把它翻译成“第一类函数”,有些是“一等函数”,我更倾向于“一等函数”,因为这种名字更能体现出“一等数据类型”的概念。那么什么是一等函数呢?它指的是带有最少限制的函数,它们的“权利或者特权包括”:

  1. 可以用变量命名
  2. 可以提供给过程作为参数
  3. 可以由过程作为结果返回
  4. 可以包含在数据结构中

这样的函数就跟字符串一样,可以传来传去,也就成了一种“数据”。接下来分别介绍这四个特征的一些应用。(本文示例以最常见的Javascript为主,但也会给出Java的示例,作为强类型语言的参考)。

特征一:可以用变量命名

首先来看一个js的例子

function hello(){console.log("hello")
}let funHello = hello
funHello();

这里定义了一个函数hello,然后将其和变量funHello绑定,然后对变量funHello进行函数调用,结果就是hello函数被调用。

可以用变量命名这点非常重要,因为变量可以作为参数传递,可以用来接收过程调用的返回值,也可以作为例如json对象、结构体的属性,所以函数也可以被用来干这些事。例如:


当然在js里还可以这样编写:

let funHello1 = function (){console.log("hello")
}let funHello2 = () => {console.log("hello")
}

但是到Java这边,由于Java是强类型语言,而Java实现上并没有对一等函数的直接支持,所以只能用Java 8的函数式接口来实现这样的表达:

public class FunMain {static void hello(){System.out.println("hello");}public static void main(String[] args) {Runnable r = FunMain::hello;r.run();Runnable r1 = () -> {System.out.println("hello");};r1.run();}
}

特征二:可以提供给过程作为参数

这点应用就比较广泛了,各种高阶函数,例如回调函数:

function testCallback(callback){console.log("do something....")callback()
}testCallback(() => {console.log("complete...")
})

另外还有一个经典例子,比如,首先我们编写一个过滤出一个数组中偶数的数字:

function filterEven(nums){let result = []for(let i = 0;i < nums.length;i++){if(nums[i] %2 == 0){result.push(nums[i])}}return result;
}
filterEven([1,2,3,4,5,6]) // => [2,4,6]

然后来了个新的功能,要编写一个过滤出1-10之间数:

function filter1to10(nums){let result = []for(let i = 0;i < nums.length;i++){if(nums[i] >= 1 && nums[i] <= 10){result.push(nums[i])}}return result;
}
filter1to10([1,22,3,44,5,66]) //=> [1,3,5]

其实大家会发现代码结构非常类似,只有判断条件不同,我们将其提取成参数后:

function filter(datas, predicte){let result = []for(let i = 0;i < datas.length;i++){if(predicte(datas[i])){result.push(datas[i])}}return result;
}
filter([1,2,3,4,5,6], i => i % 2 == 0)
filter([1,22,3,44,5,66], i => i >= 1 && i <= 10)

这样代码的复用性就得到了很大提升,下面要过滤不同类型的数值,只需要传入不同的条件就可以了。

Java版本的如下:

public class FunMain {public static void main(String[] args) {List<Integer> list1 = filterInts(Arrays.asList(1, 2, 3, 4, 5), i -> i % 2 == 0);}public static List<Integer> filterInts(List<Integer> list, Predicate<? super Integer> predicate) {List<Integer> result = new ArrayList<>();for (int i = 0; i < list.size(); i++) {Integer data = list.get(i);if (predicate.test(data)) {result.add(data);}}return result;}
}

还可以利用泛型做进一步优化:

public class FunMain {public static void main(String[] args) {List<Integer> list1 = filter(Arrays.asList(1, 22, 3, 44, 5,66), i -> i >= 1 && i <= 10);}public static <T> List<T> filter(List<T> list, Predicate<? super T> predicate) {List<T> result = new ArrayList<>();for (int i = 0; i < list.size(); i++) {T data = list.get(i);if (predicate.test(data)) {result.add(data);}}return result;}
}
public class FunMain {public static void main(String[] args) {List<Integer> list1 = filter(Arrays.asList(1, 22, 3, 44, 5,66), i -> i >= 1 && i <= 10);}public static <T> List<T> filter(List<T> list, Predicate<? super T> predicate) {List<T> result = new ArrayList<>();for (int i = 0; i < list.size(); i++) {T data = list.get(i);if (predicate.test(data)) {result.add(data);}}return result;}
}

特征三:可以由过程作为结果返回

这一点说得通俗点就是返回函数的函数,或者说函数生成器。例如,我们考虑下面这样的一个加法器:

function adder(n){return i => {return i + n}
}let add3 = adder(3);
add3(4)  // => 7
add3(10) // => 13

这里函数adder接受一个数字n,返回一个将参数加n的函数。

对应Java版本为:

public class FunMain {public static void main(String[] args) {Function<Integer, Integer> add3 = adder(3);int r = add3.apply(4);// => 7System.out.println(r);}static Function<Integer, Integer> adder(int n){return i -> {return i + n;};}
}

我们还可以编写反函数的生成器:

function complement(f){return n => !f(n)
}function odd(n){return n % 2 != 0;
}let even = complement(odd)
odd(1)
even(2)

对应的Java版本为:

public class FunMain {public static void main(String[] args) {Predicate<Integer> odd = i -> i % 2 != 0;Predicate<Integer> even = complement(odd);System.out.println(odd.test(1)  + " - " + even.test(2));}static <T> Predicate<T> complement(Predicate<T> predicate) {return t -> !predicate.test(t);}
}

另外,我们还可以和特征二结合起来实现函数组合的效果:

function compose (f1, f2){return i => f2(f1(i))
}function square (n){return n * n
}function triple(n){return 3 * n
}let squareAndTriple = compose(square, triple);
squareAndTriple(4) // => 48

这里我们利用compose把两个单参函数squaretriple组合,得到一个新的函数squareAndTriple。对应Java版本为:

public class FunMain {public static void main(String[] args) {Function<Integer, Integer> squareAndTriple = compose(FunMain::square, FunMain::triple);Integer r = squareAndTriple.apply(4);System.out.println(r);}static int square(int n) {return n * n;}static int triple(int n) {return 3 * n;}static <T, K, R> Function<T, R> compose(Function<T, K> f1, Function<K, R> f2) {return t -> f2.apply(f1.apply(t));}
}

特征四:可以包含在数据结构中

这一点意味着,函数可以作为集合的元素,也可以作为对象的属性等等,例如,我们可以得到一个函数序列:

function identity(i){return i
}let funList = [i => i * i,function (i){return i + 1},identity
]for(let i = 0;i < funList.length;i++){console.log(funList[i](10))
}

对应Java版本为:

public class FunMain {public static void main(String[] args) {List<Function<Integer, Integer>> list = Arrays.asList(i -> i * i,i -> i + 1,FunMain::identity);for (int i = 0; i < list.size(); i++) {System.out.println(list.get(i).apply(10));}}static int identity(int n) {return n;}
}

函数式编程:一等函数(First-class Function)相关推荐

  1. 什么是函数式编程,函数合并与柯里化又是什么意思?

    函数式编程在耳边回响了多年,今天就来详细了解一下它吧. 函数式编程的主要特征是:函数是一等公民.它建议大家写纯函数.没有副作用的函数. 讨论完纯函数的内容,我们会看一下最重要的应用:函数的柯里化. 纯 ...

  2. 函数式编程 -- 纯函数、柯里化函数

    文章内容输出来源:拉勾教育 大前端高薪训练营 前言 学习函数式编程,要知道什么是纯函数,使用纯函数的好处,了解有关副作用的相关信息,以及纯函数相关的功能库Lodash,还有什么是函数的柯里化. 一.纯 ...

  3. 函数式编程-纯函数篇

    概念: 相同的输入永远会得到相同的输出,且没有任何可观察的副作用 纯函数就类似数学中函数(用来描述输入和输出之间的关系)例如y=f(x) 举例 数组中的slice和splice分别是纯函数和不纯的函数 ...

  4. 闭关之 C++ 函数式编程笔记(一):函数式编程与函数对象

    目录 前言 第一章 函数式编程简介 1.1 命令式与声明式编程比较 1.2 纯函数(Pure functions) 1.2.1 避免可变状态 1.3 以函数方式思考问题 1.4 函数式编程的优点 1. ...

  5. JavaScript 中的函数式编程:函数,组合和柯里化

    作者:Fernando Doglio 译者:前端小智 来源:medium 移动端阅读:点这里 点赞再看,微信搜索 [大迁世界] 关注这个没有大厂背景,但有着一股向上积极心态人.本文 GitHub ht ...

  6. 【基于JS 函数式编程 -1】什么是函数式编程 | 纯函数 | 命令式与声明式 | 优点

    ⭐️ 本文首发自 前端修罗场(点击即可加入),一个专注 Web 技术.答疑解惑.面试辅导.职业发展的社区. 相关文章 [函数式编程]基于JS 进行函数式编程(一)引入 | 什么是函数式编程 | 函数式 ...

  7. 【Python函数式编程】——偏函数(Partial function)

    Python函数式编程--偏函数   Python的 functools 模块提供了很多有用的功能,其中一个就是偏函数(Partial function).要注意,这里的偏函数和数学意义上的偏函数不一 ...

  8. 【scala函数式编程】函数设计的通用结构——Monad

    1. monad单子(monads) 定义:monad是一个满足associativity和identity法则的monadic组合的最小集合的实现,是对象类型的包装,用一个对象包装另一个对象,mon ...

  9. Java8 函数式编程之函数接口(上)

    本文博主纯手敲,转载请注明出处. 由于最近项目中都是基于JDK8或JDK9,因此熟悉新版本中的特性尤其重要,这里主要讲解Java8中java.util.function下的的函数式接口声明. 谈到函数 ...

最新文章

  1. 【DAY23】JVM与反射的学习笔记
  2. Pascal's Triangle
  3. 大规模落地:AI安防仍存两大痛点
  4. python pandas dataframe 转json_python将相类不同key的json对象转化为pandas的dataframe(上篇)...
  5. python绘制蝴蝶曲线_如何编程实现蝴蝶函数曲线
  6. Akamai Martin Horčička:最新网络优化技术及编程语言分析
  7. 解读我所认知的网络营销根源
  8. android 安卓市场,安卓市场(Android Market).doc
  9. Eclipse Web开发出现莫名其妙错误
  10. 从开源爱好者到 Apache 董事,他花了 11 年
  11. 入门Python,看这一篇就够了,史上最全的Python基础语法知识清单!
  12. C语言scanf中%%,C语言scanf()和gets()及printf()和puts()的区别
  13. c语言编程流水灯与交通灯实验,C51单片机实验报告流水灯交通灯定时器双机交互时钟(10页)-原创力文档...
  14. Windows便签数据恢复
  15. 装修首页自定义全屏视频播放效果gif动态图片制作视频教程播放代码操作设置全屏居中阿里巴巴国际站
  16. meteor使用简介
  17. 看完就学会系列,小小一篇文章教会你利用Python网络爬虫抓取王者荣耀图片(建议收藏)
  18. Keil5 点击Debug Setting 软件崩溃解决方法
  19. 【STL】string详解(string类常用的操作函数、构造函数、赋值操作、子符串的拼接、查找和替换、比较、存取、插入和删除、获取)
  20. GIS毕业生经典笔试、面试题汇总(待续)

热门文章

  1. MyBatis 如果不存在则插入
  2. Temporal action localization in untrimmed videos via Multi-stage CNNs SCNN论文阅读笔记
  3. shunit2单元测试框架
  4. 数学专业的考计算机博士,在数字中发现乐趣 过来人谈数学专业考研
  5. FPGA驱动旋转编码器(Verilog)
  6. 中国百句经典名言浅译
  7. android机开应用速度慢,Android机跑好慢 学会这秘密五招手机瞬间加速!
  8. java B2B2C Springcloud电子商务平台源码-security简单使用
  9. java 实现查询近七天数据功能
  10. NetAlly网络测试仪