函数式编程之-bind函数
Bind函数
Bind函数在函数式编程中是如此重要,以至于函数式编程语言会为bind函数设计语法糖。另一个角度Bind函数非常难以理解,几乎很少有人能通过简单的描述说明白bind函数的由来及原理。
这篇文章试图通过“人话”来描述bind函数,并通过浅显的实例为零函数式编程语言的开发者揭秘bind函数的作用及用法。
public string GetSomething(int id)
{var x = GetFirstThing(id);if (x != null){var y = GetSecondThing(x);if(y != null){var z = GetThirdThing(y);if (z != null){return z;}}}return null;
}
你一定写过类似的代码,估计你也明白这样的代码看起来很丑陋,一层层的判空嵌套打乱了代码的主题结构。
有没法让他变的更优雅?当然你可以通过"early return"的做法,不过这种方式不在我们的讨论范围之内。
这种风格的代码存在一个明显的code smell, GetFirstThing()/GetSecondThing()/GetThirdThing()等方法有可能返回null,我们说return null是一种不真确的做法,相关分析见拒绝空引用异常。使用Optional类型重构如下:
public Optional<string> GetSomething(int id)
{var x = GetFirstThing(id);if (x.HasValue()){var y = GetSecondThing(x);if(y.HasValue()){var z = GetThirdThing(y);if (z.HasValue()){return z;}}}return Optional.None<string>();
}
看起来代码结果跟之前一模一样,重构后的代码并没有变得更漂亮。不过现在的GetFirstThing()/GetSecondThing()/GetThirdThing()方法返回值为Optional<string>类型,不再是普通的string类型:
public Optional<string> GetFirstThing(int id)
{//...return Optional.None<string>();
}
重构后的这段代码很有意思,我们可以从函数组合的角度来让整个代码段变的更加优雅。
组合
这段代码其实做了一件事,那就是通过调用三个函数GetFirstThing()/GetSecondThing()/GetThirdThing()来完成一个业务逻辑。从函数式编程思想的角度出发,我们倾向于把若干个小的函数连接起来,根据以前学过的知识,只有一个输入和一个输出的函数才能连接起来:
他们之所以能够连接是因为这两个函数的签名一致,都拥有一个输入和一个输出。
例如:int -> string, string -> bool就可以组合为int -> bool。
而我们此时拥有的三个函数方法签名如下:
GetFirstThing: int -> Optional<string>
GetSecondThing: string -> Optional<string>
GetThirdThing: string -> Optional<string>
显然GetFirstThing和GetSecondThing是无法直接连接的,原因是GetFirstThing返回了Optional<string>类型,而GetSecondThing的输入却是一个普通的string类型。如果我们能够在Optional<T>上扩展一个函数,函数接受一个签名为T -> Optional<T>的函数,那么我们就有可能将上面的三个函数串联起来:
public static class Optional
{public static Optional<T> Bind<T>(this Optional<T> input, Func<T, Optional<T>> f){if (input.HasValue()){return f(input.Value);}return Optional.None<T>();}
}
有了上面这个神奇的bind函数你就可以将上面的三个函数连接起来了:
public string GetSomething(int id)
{return GetFirstThing(id).Bind(GetSecondThing).Bind(GetThirdThing);
}
用F#实现:
let address = getFirstThing id|> bind getSecondThing|> bind getThirdThing
通过bind函数我们成功将三个函数连接了起来, 同时将判空放在了bind函数里,从而保持主要逻辑部分更加线性和清晰。
如何编写属于自己的bind函数
- 首先需要定义一个泛型类型E<a>,例如我们上面例子中提到的Optional<T>
- 编写属于Optional<T>的bind函数,bind函数的签名为E<a> -> (f: a -> E<b>) -> E<b>。 接收一个E<a>,同时接受一个签名为a -> E<b>的函数,返回E<b>。
List<T>中的bind函数
我们经常用的List<T>就是一个典型的泛型类型,那他上面有没有bind函数?当然有,不过叫做SelectMany, Scala中也叫flatMap。
看一下SelectMany的方法签名,正好符合bind函数的签名要求:
public static IEnumerable<TResult> SelectMany<TSource,
TResult>(this IEnumerable<TSource> source, Func<TSource, IEnumerable<TResult>> selector)
{//...
}
SelectMany可以用在什么样的场景中?
例如有这样一个场景,一篇文章(paper)可以有若干章节(section)组成,每个章节(section)又有若干行(row)组成,每行(row)有若干单词(word)组成。
问:给定一篇文章(paper),请找出大于10行(row)的章节(section),里面排除注释的行(row)总共的单词(word)数量。
首先根据需求变下下面的若干函数:
private List<Paper.Section> GetSections(Paper paper)
{return paper.Sections.Where(s => s.Rows.Count > 10).ToList();
}private List<Paper.Section.Row> GetRows(Paper.Section section)
{return section.Rows.Where(r=>!r.IsComment).ToList();
}private List<Paper.Section.Row.Word> GetWords(Paper.Section.Row row)
{return row.Words;
}
且看这三个函数的签名:
GetSections: Papaer -> List<Section>
GetRows: Section -> List<Row>
GetWords: Row -> List<Word>
正好这就是就符合bind函数连接的需求:
var length = GetSections(paper).SelectMany(GetRows).SelectMany(GetWords).Count();
F#实现:
let words = getSections paper|> bind getRows|> bind getWordswords.Length
bind函数的语法糖支持
bind函数在函数式编程中如此常见,以至于需要设计单独的语法糖,Haskell中叫do natation
, Scala中叫for comprehension
,F#用Computation expressions
:
list {let! section = getSections(paper)let! row = getRows(section)let! word = getWord(row)return word
}
转载于:https://www.cnblogs.com/xiandnc/p/9684271.html
函数式编程之-bind函数相关推荐
- 高阶函数||编程范式: 命令式编程/声明式编程 || 编程范式: 面向对象编程(第一公民:对象)/函数式编程(第一公民:函数)
编程范式: 命令式编程/声明式编程 编程范式: 面向对象编程(第一公民:对象)/函数式编程(第一公民:函数) 高阶函数 filter/map/reduce filter中的回调函数有一个要求: 必须返 ...
- 函数式编程:一等函数(First-class Function)
函数式编程:一等函数(First-class Function) 说起函数式编程,不得不提的是First-class Function的概念,有些文章把它翻译成"第一类函数",有些 ...
- JavaScript函数式编程(纯函数、柯里化以及组合函数)
JavaScript函数式编程(纯函数.柯里化以及组合函数) 目录 JavaScript函数式编程(纯函数.柯里化以及组合函数) 前言 1.纯函数 1.1.纯函数的概念 1.2.副作用 1.3.纯函数 ...
- python学习——函数式编程——高阶函数
python学习--函数式编程--高阶函数 函数式编程(高阶函数):1:map && reduce; 2 : filter; 3: sorted; ------------------ ...
- Python进阶:函数式编程(高阶函数,map,reduce,filter,sorted,返回函数,匿名函数,偏函数)...啊啊啊...
函数式编程 函数是Python内建支持的一种封装,我们通过把大段代码拆成函数,通过一层一层的函数调用,就可以把复杂任务分解成简单的任务,这种分解可以称之为面向过程的程序设计.函数就是面向过程的程序设计 ...
- 函数式编程4-高阶函数
以其他函数作为参数的函数 本章的所有代码,均在github.com/antgod/func- 关于传递函数的思考 max 在很多编程语言的核心库,都包含一个叫做max的函数.包括underscore也 ...
- python3_函数_形参调用方式 / 不定长参数 / 函数返回值 / 变量作用域 / 匿名函数 / 递归调用 / 函数式编程 / 高阶函数 / gobal和nonlocal关键字 / 内置函数
1.形参的调用方式 1. 位置参数调用 2. 关键词参数调用 原则: 关键词参数调用不能写在位置参数调用的前边 def test1(name, age):print("name:" ...
- 【一天时间|JavaScript进阶】函数式编程高阶函数的应用
一天时间系列文章是博主精心整理的面试热点问题和难点问题,吸收了大量的技术博客与面试文章,总结多年的面试经历,带你快速并高效地审视前端面试知识.直击技术痛点,主动出击,精密打击,这才是面试拿到高薪的秘诀 ...
- C语言网络编程:bind函数详解
文章目录 函数功能 函数头文件 函数使用 函数参数 函数举例 为什么需要bind函数 服务器如何知道客户端的ip和端口号 htons函数 `htons`兄弟函数`htonl`,`ntohs`,`nto ...
最新文章
- chmod修改文件夹权限
- echarts label加边框_玩转ECharts之实现“动态颜色的Label”
- Python库安装相关问题
- commit git 删除文件夹_Git-git删除文件夹/文件(删除/不删除本地文件/文件夹)
- Win10文件夹大小分析、磁盘空间清理工具
- jsonp 使用选择器
- 地理信息系统导论第八版_地理信息系统导论.pdf
- Tomcat安装配置及CATALINA_HOME environment variable is not defined correctly问题的解决
- Java大作业——购物车
- 使用简单原始的办法排序CMap中的路径
- 2021-12-13 云计算平台基础架构 swift
- vue里使用echarts画世界地图
- 五年级计算机课总结,2015秋信息技术五年级上册工作总结
- python设计模式(一)创建型模式
- 湖仓一体(Lakehouse)是什么?
- 说起在职博士、博士后
- 希尔伯特黄变换matlab,HHT变换的三种方法 Matla
- gradient设置上下渐变_图解CSS: CSS渐变
- 我的世界服务器怎么制作头颅,MythicMobs/论坛教程/如何制作自定义头颅的怪物
- 正确的python变量名_正的解释|正的意思|汉典“正”字的基本解释
热门文章
- 音视频解决方案之二次开发
- linq.designer.cs学习笔记
- kalman 滤波 演示与opencv代码
- 【上】安全HTTPS-全面详解对称加密,非对称加密,数字签名,数字证书和HTTPS
- 5位Mixin Network SDK作者分别获赠价值4千美金的50个XIN token
- 生产过剩下的危机--房价上涨买不起房,房价泡沫破裂后仍然买不起房!
- Caused by:java.lang.IllegalStateException at android.media.MediaPlayer._setDataSource(Native Method)
- centos6.5环境 安装php5.5.30的redis扩展 介绍
- 关系计划笔谈(9-1):泛BOM与虚拟产品
- 为Jfinal-weixin SDK添加微信连WiFi成功通知事件