文档章节

初识函数式编程的一点漫谈

祁达方
 祁达方
发布于 2016/08/17 10:57
字数 2507
阅读 40
收藏 0
点赞 0
评论 0

昨天看一些函数式编程、Haskell、Scala的书和文章,到半夜还莫名其妙:

「好像除了函数是一等公民外,没发现什么好处呀……很多人说学函数式能改变思维方式,又不肯说这改变具体是什么。」

「javascript里函数也是一等公民,用着也很爽,函数式比之,还有什么好处?」

很不满意地躺下,用手机继续google之,突然发现一篇文章,醍醐灌顶,立刻爬起来仔细阅读,摸黑记录(兴奋得没工夫开灯),发微博致谢。

「函数式编程的关键是不定义变量」,这至为重要的一点,之前看的书和文章竟然都没强调——说是说过了,却没指出这是重点。

就连忙现想个功能,尝试不定义变量地实现它,发现当有了这个勒定,函数语言自然会被「倒逼」着具备lambda等特性,因为我发现只用老java,不用java8语法,很难在「不定义变量」的约束下实现之,不知道是不是不可能实现,我的数学能力不足以证明之。

今天早上起来,赶快翻开《写给大忙人看的java8》,使用java8语法完成程序。完成后重构一下,发现果然不出所料,最终代码的结构,是函数复函数,函数套函数,主函数是超级大函数,而这些函数的分隔和关联,着眼点就在其参数和返回值上。

数学里,函数的意义是定义域到值域的映射,代码里写的函数,其意义是参数集合到返回值集合的映射,为了达成这从集合到集合的映射,在不许定义变量的情况下,只有借助java8的lambda和stream才能实现。

每个函数都是从一个集合,变换到另一个集合,这样经过若干步变换,就实现了从程序的总参数,到程序的总结果的变换。

程序的意义本来就是从输入到输出的变换,输入是用户操作、网络请求、定时器等事件引起的中断,及存储器某个地方现有的数据,输出则是将一些数据写到存储器中,包括写磁盘、写显存以让屏幕显示某些图画、写数据库、发网络请求调远处接口等。可以把程序的「总输入参数」看做一个集合,「总输出结果」看做一个集合,从参数集合到结果集合的映射,就是一个「大函数」,这与函数式编程的「主函数是超级大函数」是一致的。

其实「网站」的「主函数」又何尝不是「超级大函数」呢,本质就是socket服务端的主循环,通过很复杂的策略,把「总输入」包成request对象,把「总输出」准备用response对象处置,然后路由到具体的程序上,那程序可能进一步「分治」,用框架把request的参数绑到具体处理方法的入参上,把该方法的出参用框架给到response返回,然后通过路径匹配,把各个请求给到适当的处理方法上。只是,很多时候,我们在太高层、太狭窄的上下文中工作得太久,以至于忘了自己身处一个「超级大函数」之中。

视线更大一点,计算机又何尝不是「超级大函数」呢,一通电,CPU即不停运转,这就是最大的函数,「总输入」是电能,「总输出」是电路使硬件发生的变化;在这之上一层,操作系统的主循环是次一级的「超级大函数」,「总输入」是随时监听到的硬件中断;再之上一层,才是某个进程的主循环这「大函数」,「总输入」是操作系统分配来的事件,上段说的网站服务器的socket主循环就是这种「大函数」;在这函数调用之下,Filter和Servlet这些组件的生命周期才得以运转,然后进一步才是Spring等框架的初始化,Struts或Spring等框架表现层组件的初始化和响应事件方法执行,再下一步,才是业务层,持久层,读写磁盘,读写数据库,读写API这些具体的「小函数」,小函数的输出,再经过层层包裹,成为「超级大函数」的输出的一部分,即对某处的硬件产生效果。

视线再大一点,就是宇宙大爆炸是「究极大函数」,那个瞬间一切原子的位置,是否已决定了之后的一切,这已是哲学的问题了。这个「洪荒之力」,过于究极,我们也只是这个大函数中极为渺小的一些数据啊,缘聚,就有了我,缘散,就没了我,像一滴水消失在水中,像计算机断电后,内存中的010101化为乌有,人生天地间,忽如远行客。

说远了。函数想从输入变换到输出,处理方法是当然要有的,这个方法,命令式语言着眼于「参数怎样算出结果」的流程,而函数式语言着眼于「参数与结果的对应」的关系,一个着眼于过程,一个着眼于结果,一个在厚重的书本中寻找来龙去脉,一个用搜索引擎来直指目的讯息,一个运筹帷幄,一个应变随机,一个深厚,一个扁平,一个动,一个静,一个重步骤,一个重设置,若说没奇缘,java偏又升到8,若说有奇缘,为何阵营终分化。

映射这件事,java程序员也早就熟悉啊,不论是web.xml,还是struts.xml,还是spring中bean的id到实体,还是mybatis中「函数」的id到语句,hibernate?它叫做O-R什么来着?服务器这「超级大函数」接收到的各路神仙,给分化瓦解,分而治之,你虽一路来,我却N路去,这是但凡程序员都知道的「分治」原则。顺序选择循环,顺序循环可以咬牙不要,不用选择还想达到目的的,没听说过,即使没听过「函数式」的程序员,也每天都在做把「大函数」的任务视情况分配到「小函数」来处理的工作。

那「不定义变量」的意义是什么,它的意义是,函数是纯逻辑——当然,是大逻辑套小逻辑的复杂逻辑。它不需要「知识」,即存储在存储单元中的数据。就好比形式逻辑,A为真,B为真,A且B则一定为真,至于A和B是什么,不用想。「不定义变量」从根本上避免了来自知识的污染,对程序来说,参数即是全部知识,外部知识不需要,我也没兴趣向外写,我只以这点有限的参数知识为准,返回适当的结果知识。

这个切割非常漂亮,能让程序员专注于对局部的考察,只关注当前函数这「茅庐」即可,外面「三分天下」的洪水滔天都不在意(毕竟它们渗不进来)。显然,这阅读代码、单元测试起来,都太方便了,有限知识的纯逻辑推演,正是数学和逻辑学「天然真理」在程序世界中的表现,天然真理是干净的,不需要被证明,不可能被证伪。

但科学是「有待」的,商业程序也是「有待」的,商业程序里可能没有用全局变量装状态,但最大的状态都在用N个数据库服务器或缓存服务器的集群装着呢。数据就是知识,知识就是力量,阿尔法打败了李世石,在这真正的洪荒之力面前,纯逻辑的推演显得力不从心,互联网时代,记忆力和计算力都外包,计算力这方面函数式干净利落,但记忆力即「状态」的价值同样举足轻重啊。科学知识,早晚要被证伪,并非天理,但能指导生活;商业程序,其中含有状态,并不干净,但能服务人类。函数式的思维,应该作为一种武器,纳入我们的兵器谱,在适当的时候取出来,破军杀敌。而不是奉为至宝,顶礼膜拜,一言不合,出言不逊。这才是一个客观务实的态度。

以上是初识函数式编程的一点漫谈,没有限于问题本身,「漫溢」了,不过作为散文,而非论文,「通感移觉」这种「使用知识」的修辞,很有效用,虽然形散,神却不散,不知看到这里(感谢)的你,对这会点头否?雾里看花,见识有限,全是个人思考和领悟的产物,恐怕错误不少,请多指正。

最后把开头说的用java尝试函数式编程的代码贴上来吧,java8是刚学,代码挺笨的,请指教。

public static void main(String[] args) {
    //把给定的一些字符串,统计字母出现次数,结果为:
    //a:1 b:4 k:3 o:6
    System.out.println(strs2SumGroupStr("booka", "bookb", "koob"));
}

//booka,bookb... -> 'a:1 b:4..'
public static String strs2SumGroupStr(String... strs){
    return stream2SumGroupStr(strs2Stream(strs));
}

//booka,bookb... -> b,o,o,k,a,b...
public static IntStream strs2Stream(String... strs){
    return String.join("", strs).chars();
}

//b,o,o,k,a,b... -> 'a:1 b:4..'
public static String stream2SumGroupStr(IntStream is){
    return groupMap2SumGroupStr(stream2GroupMap(is));
}

//b,o,o,k,a,b... -> {a:*,b:*..}
public static Map<Integer,IntSummaryStatistics> stream2GroupMap(IntStream is){
    return is.boxed().collect(Collectors.groupingBy(i->i, Collectors.summarizingInt(i->i)));
}

//{a:*,b:*..} -> 'a:1 b:4..'
public static String groupMap2SumGroupStr(Map<Integer,IntSummaryStatistics> map){
    return groupSet2SumGroupStr(groupMap2GroupSet(map));
}

//{a:*,b:*..} -> ('a:1','b:4'..)
public static Set<String> groupMap2GroupSet(Map<Integer,IntSummaryStatistics> map){
    return map
            .keySet()
            .stream()
            .map(k->(char)k.intValue()+":"+map.get(k).getCount())
            .collect(Collectors.toSet());
}

//('a:1','b:4'..) -> 'a:1 b:4..'
public static String groupSet2SumGroupStr(Set<String> set){
    return set
            .stream()
            .reduce((x,y) -> x + " " + y)
            .get();
}

© 著作权归作者所有

共有 人打赏支持
祁达方
粉丝 11
博文 41
码字总数 51226
作品 0
大兴
程序员
《细说 QML》

关于命名 “中文是最叼的”。。。原本想给本系列也起一个很“叼”的名字,无奈笔者语言匮乏,绞尽脑汁才想出一个还算凑合的名字 - 《细说 QML》,关于“细说”,新华词典中有记载: 细说 [x...

u011012932 ⋅ 2017/02/27 ⋅ 0

编程语言漫谈

写在前边:我们知道现有语言的编程范式有:过程式,面向对象,函数式,逻辑式。随着软件工业化程度的普及,以及软件的复杂度越来越高,编程语言的发展历程也是从最初的过程式(命令式)语言c...

oschina ⋅ 2016/06/04 ⋅ 16

Python 快速入门

学习 Python 的由来 第一次接触 Python 时,是在刚毕业不久,那时公司在做一个网盘客户端,需要调研一些 GUI 框架。由于当时 Python 很火(当然,现在也一样),便尝试了一下 PyQt(Python ...

u011012932 ⋅ 2017/07/07 ⋅ 0

对Winter-cn关于函数式编程的文章评论

Winter-cn兄关于程序设计方法论有不少知识。时常写一点自己的观点,如: 函数式编程的杂七杂八(非常乱) 我看完了之后,不太赞同他的某些观点,写下了如此评论: 首先不同意你的如下说法: ...

mikelij ⋅ 2010/07/02 ⋅ 0

漫谈C语言及如何学习C语言

为什么要学习C语言? C语言学习方法 1,参考书籍 2,动手实验环境搭建 3,网络资源 附录 一些有用的C语言网络资源: 一些值得关注及研究的C语言相关项目: 一些C语言编程时可以使用的工具软件...

地狱的烈火 ⋅ 2013/06/17 ⋅ 0

编程思想 之「初始化与清理」

温馨提示:本系列博文(含示例代码)已经同步到 GitHub,地址为「java-skills」,欢迎感兴趣的童鞋、,纠错。 与其他语言相比,Java 的一大特点就是其自动的初始化与清理功能。对于基本数据类...

qq_35246620 ⋅ 2017/12/22 ⋅ 0

漫谈C语言及如何学习C语言

在这里想比较系统的谈谈个人对C语言学习方法的理解。分别按照书籍、实验环境搭建、网络资源来分别介绍,希望能写的比较完整全面一些,给想学习C语言的朋友一个有价值的参考。 为什么要学习C...

wape-yang ⋅ 2013/06/16 ⋅ 1

Java8-初识Lambda

廉颇老矣,尚能饭否 Java,这位已经20多岁的编程语言,称得上是编程语言界的老大哥了。他曾经攻城略地,碾压各路编程语言小弟,风光无限,不可一世。现在,也是家大业大,江湖地位,很难撼动...

Jackie_Zheng ⋅ 2017/09/17 ⋅ 0

PHP函数式编程初探之“三板斧”:过滤、映射、归约

函数式编程是一种不同于对象式编程的思想,虽然PHP并不是天生就属于函数式编程的语言,也不擅长该领域,但这里希望通过PHP对函数式支持实现,加深对函数式编程的范式和思想。 PHP本身的语法、...

暗夜在火星 ⋅ 2016/11/20 ⋅ 0

iOS项目开发框架“完全体”(二)-- 终极“奥义”全解

设计思路 在实际开发中经常会遇到需要设置VC的多个属性的时候,这个时候就要写一系列比较恶心又臃肿不堪的代码,这时想到了masonry的链式语法的优雅之处,为什么我们不可以直接用这种优雅的方...

Suneday ⋅ 2017/11/29 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

Confluence 6 从其他备份中恢复数据

一般来说,Confluence 数据库可以从 Administration Console 或者 Confluence Setup Wizard 中进行恢复。 如果你在恢复压缩的 XML 备份的时候遇到了问题,你还是可以对整个站点进行恢复的,如...

honeymose ⋅ 14分钟前 ⋅ 0

myeclipse10 快速搭建spring boot开发环境(入门)

1.创建一个maven的web项目 注意上面标红的部分记得选上 2.创建的maven目录结构,有缺失的目录可以自己建立目录补充 补充后 这时候一个maven的web项目创建完成 3.配置pom.xml配置文件 <proje...

小海bug ⋅ 26分钟前 ⋅ 0

nginx.conf

=========================================================================== nginx.conf =========================================================================== user nobody; #......

A__17 ⋅ 29分钟前 ⋅ 0

645. Set Mismatch - LeetCode

Question 645. Set Mismatch Solution 思路: 遍历每个数字,然后将其应该出现的位置上的数字变为其相反数,这样如果我们再变为其相反数之前已经成负数了,说明该数字是重复数,将其将入结果r...

yysue ⋅ 42分钟前 ⋅ 0

Python这么强?红包杀手、消息撤回也可以无视,手机App辅助!

论述 标题也许有点不好理解,其实就是一款利用Python实现的可以监控微信APP内的红包与消息撤回的助手。不得不说,这确实是一款大家钟意的神器。 消息撤回是一件很让人恶心的事,毕竟人都是有...

Python燕大侠 ⋅ 58分钟前 ⋅ 0

压缩打包介绍、gzip压缩工具、bzip2压缩工具、xz压缩工具

压缩打包介绍 压缩的好处不仅能节省磁盘空间而且在传输的时候节省传输时间和网络带宽 windows系统下文件带有 .rar .zip .7z 后缀的就是压缩文件 linux系统下则是 .zip, .gz, .bz2, .xz, ...

黄昏残影 ⋅ 今天 ⋅ 0

观察者模式

1.利用java原生类进行操作 package observer;import java.util.Observable;import java.util.Observer;/** * @author shadow * @Date 2016年8月12日下午7:29:31 * @Fun 观察目标 **/......

Cobbage ⋅ 今天 ⋅ 0

Ubuntu打印服务器配置

参考:https://blog.csdn.net/gsls200808/article/details/50950586 https://blog.csdn.net/jiay2/article/details/80252369 https://wiki.gentoo.org/wiki/HPLIP 由于媳妇儿要大量打印资料,......

大熊猫 ⋅ 今天 ⋅ 0

面试的角度诠释Java工程师(二)

原文出处: locality 续言: 相信每一位简书的作者,都会有我这样的思考:怎么写好一篇文章?或者怎么写好一篇技术类的文章?我就先说说我的感悟吧,写文章其实和写程序是一样的。为什么我会...

颖伙虫 ⋅ 今天 ⋅ 0

github中SSH的Key

https://help.github.com/articles/connecting-to-github-with-ssh/ https://help.github.com/articles/testing-your-ssh-connection/ https://help.github.com/articles/adding-a-new-ssh-k......

whoisliang ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部