函数式编程:一等函数(First-class Function)
函数式编程:一等函数(First-class Function)
说起函数式编程,不得不提的是First-class Function
的概念,有些文章把它翻译成“第一类函数”,有些是“一等函数”,我更倾向于“一等函数”,因为这种名字更能体现出“一等数据类型”的概念。那么什么是一等函数呢?它指的是带有最少限制的函数,它们的“权利或者特权包括”:
- 可以用变量命名
- 可以提供给过程作为参数
- 可以由过程作为结果返回
- 可以包含在数据结构中
这样的函数就跟字符串一样,可以传来传去,也就成了一种“数据”。接下来分别介绍这四个特征的一些应用。(本文示例以最常见的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
把两个单参函数square
和triple
组合,得到一个新的函数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)相关推荐
- 什么是函数式编程,函数合并与柯里化又是什么意思?
函数式编程在耳边回响了多年,今天就来详细了解一下它吧. 函数式编程的主要特征是:函数是一等公民.它建议大家写纯函数.没有副作用的函数. 讨论完纯函数的内容,我们会看一下最重要的应用:函数的柯里化. 纯 ...
- 函数式编程 -- 纯函数、柯里化函数
文章内容输出来源:拉勾教育 大前端高薪训练营 前言 学习函数式编程,要知道什么是纯函数,使用纯函数的好处,了解有关副作用的相关信息,以及纯函数相关的功能库Lodash,还有什么是函数的柯里化. 一.纯 ...
- 函数式编程-纯函数篇
概念: 相同的输入永远会得到相同的输出,且没有任何可观察的副作用 纯函数就类似数学中函数(用来描述输入和输出之间的关系)例如y=f(x) 举例 数组中的slice和splice分别是纯函数和不纯的函数 ...
- 闭关之 C++ 函数式编程笔记(一):函数式编程与函数对象
目录 前言 第一章 函数式编程简介 1.1 命令式与声明式编程比较 1.2 纯函数(Pure functions) 1.2.1 避免可变状态 1.3 以函数方式思考问题 1.4 函数式编程的优点 1. ...
- JavaScript 中的函数式编程:函数,组合和柯里化
作者:Fernando Doglio 译者:前端小智 来源:medium 移动端阅读:点这里 点赞再看,微信搜索 [大迁世界] 关注这个没有大厂背景,但有着一股向上积极心态人.本文 GitHub ht ...
- 【基于JS 函数式编程 -1】什么是函数式编程 | 纯函数 | 命令式与声明式 | 优点
⭐️ 本文首发自 前端修罗场(点击即可加入),一个专注 Web 技术.答疑解惑.面试辅导.职业发展的社区. 相关文章 [函数式编程]基于JS 进行函数式编程(一)引入 | 什么是函数式编程 | 函数式 ...
- 【Python函数式编程】——偏函数(Partial function)
Python函数式编程--偏函数 Python的 functools 模块提供了很多有用的功能,其中一个就是偏函数(Partial function).要注意,这里的偏函数和数学意义上的偏函数不一 ...
- 【scala函数式编程】函数设计的通用结构——Monad
1. monad单子(monads) 定义:monad是一个满足associativity和identity法则的monadic组合的最小集合的实现,是对象类型的包装,用一个对象包装另一个对象,mon ...
- Java8 函数式编程之函数接口(上)
本文博主纯手敲,转载请注明出处. 由于最近项目中都是基于JDK8或JDK9,因此熟悉新版本中的特性尤其重要,这里主要讲解Java8中java.util.function下的的函数式接口声明. 谈到函数 ...
最新文章
- 【DAY23】JVM与反射的学习笔记
- Pascal's Triangle
- 大规模落地:AI安防仍存两大痛点
- python pandas dataframe 转json_python将相类不同key的json对象转化为pandas的dataframe(上篇)...
- python绘制蝴蝶曲线_如何编程实现蝴蝶函数曲线
- Akamai Martin Horčička:最新网络优化技术及编程语言分析
- 解读我所认知的网络营销根源
- android 安卓市场,安卓市场(Android Market).doc
- Eclipse Web开发出现莫名其妙错误
- 从开源爱好者到 Apache 董事,他花了 11 年
- 入门Python,看这一篇就够了,史上最全的Python基础语法知识清单!
- C语言scanf中%%,C语言scanf()和gets()及printf()和puts()的区别
- c语言编程流水灯与交通灯实验,C51单片机实验报告流水灯交通灯定时器双机交互时钟(10页)-原创力文档...
- Windows便签数据恢复
- 装修首页自定义全屏视频播放效果gif动态图片制作视频教程播放代码操作设置全屏居中阿里巴巴国际站
- meteor使用简介
- 看完就学会系列,小小一篇文章教会你利用Python网络爬虫抓取王者荣耀图片(建议收藏)
- Keil5 点击Debug Setting 软件崩溃解决方法
- 【STL】string详解(string类常用的操作函数、构造函数、赋值操作、子符串的拼接、查找和替换、比较、存取、插入和删除、获取)
- GIS毕业生经典笔试、面试题汇总(待续)
热门文章
- MyBatis 如果不存在则插入
- Temporal action localization in untrimmed videos via Multi-stage CNNs SCNN论文阅读笔记
- shunit2单元测试框架
- 数学专业的考计算机博士,在数字中发现乐趣 过来人谈数学专业考研
- FPGA驱动旋转编码器(Verilog)
- 中国百句经典名言浅译
- android机开应用速度慢,Android机跑好慢 学会这秘密五招手机瞬间加速!
- java B2B2C Springcloud电子商务平台源码-security简单使用
- java 实现查询近七天数据功能
- NetAlly网络测试仪