草庐IT

读函数式编程思维笔记01_演化的语言

躺柒 2023-03-28 原文

1. 范式转变

1.1. 学习一种全新的编程范式,困难并不在于掌握新的语言

1.1.1. 真正考验人的,是怎么学会用另一种方式去思考

1.2. 计算机科学的间歇式进步,好思路有时搁置数十年后才变成主流

1.3. 第一种面向对象的语言Simula 67是1967年发明的,可是直到1983年诞生的C++终于流行起来以后,面向对象才真正成为主流

1.4. 早年Java总被认为太慢,内存耗费太高,不适合高性能的应用,如今硬件市场的变迁把它变成了极具吸引力的选择

1.5. 命令式编程风格常常迫使我们出于性能考虑,把不同的任务交织起来,以便能够用一次循环来完成多个任务

1.6. 函数式编程用map()、filter()这些高阶函数把我们解放出来,让我们站在更高的抽象层次上去考虑问题,把问题看得更清楚

2. 跟上语言发展的潮流

2.1. 函数式编程

2.2. Groovy已经具备了丰富的函数式特性,包括像“记忆”这样的高级特性在内

2.2.1. (memoization,指运行时自动缓存函数返回值的能力)

2.3. lambda块(也就是高阶函数)被纳入Java 8

2.4. C++在2011年版的语言标准里增加了lambda块,还有引人关注的Boost.Phoenix等类库

3. 把控制权让渡给语言/运行时

3.1. 第四代编程语言4GL下的一行命令,3GL可能要用很多行才写得出来,因为4GL自带了更丰富的编程环境

3.2. Java虚拟机(JVM)平台

3.2.1. Scala

3.2.2. Clojure

3.2.3. Groovy

3.3. NET平台

3.3.1. F#

3.4. 把时间花在更高层次的抽象上,多考虑怎样解决复杂的业务场景,少去费心复杂的底层运作

3.4.1. Java接管内存分配减轻了我们的负担

3.4.1.1. 人生苦短,远离malloc

3.4.2. 函数式编程语言用高阶抽象从容取代基本的控制结构

3.4.2.1. 将琐碎的细节交托给运行时,令繁冗的实现化作轻巧

4. 简洁

4.1. 面向对象编程通过封装不确定因素来使代码能被人理解

4.1.1. 精细地控制谁能够感知状态和改变状态

4.1.2. 大粒度的框架式的重用

4.1.2.1. 从类的关系中发现重复出现的模式并加以重用

4.1.2.1.1. 重用的单元是类和用作类间通信的消息
4.1.2.1.2. 用类图(class diagram)来表述

4.1.3. 喜欢大量地建立有很多操作的各种数据结构

4.1.3.1. Java世界的数十种XML类库,每一种都有自己定义的内部数据结构

4.2. 函数式编程通过尽量减少不确定因素来使代码能被人理解

4.2.1. 与其建立种种机制来控制可变的状态,不如尽可能消灭可变的状态这个不确定因素(moving parts)

4.2.2. 假如语言不对外暴露那么多有出错可能的特性,那么开发者就不那么容易犯错

4.2.3. 比较细小的层面上重用代码

4.2.3.1. 比起一味创建新的类结构体系,把封装的单元降低到函数级别,更有利于达到细粒度的、基础层面的重用

4.2.4. 函数式语言有很多的操作,但对应的数据结构却很少

4.2.4.1. 用很少的一组关键数据结构(如list、set、map)来搭配专为这些数据结构深度优化过的操作

4.2.4.2. 这些关键数据结构和操作组成的一套运转机构上面,按需要“插入”另外的数据结构和高阶函数来调整操作,以适应具体的问题

4.2.5. 函数式语言共同的一点小习惯:可以用表达式(expression)的地方就不用语句(statement)

5. 让语言去迎合问题

5.1. 让程序去贴合问题,不要反过来

5.2. Java不是一种特别灵活的语言,我们只能死板地用一些现成结构来拼凑自己的设计

5.2.1. 关键字蕴含了开发者无法从其他地方获得的语义

5.3. 另一些语言可塑性更强,他们不会拿问题去硬套语言,而是想法揉捏手中的语言来迎合问题

5.3.1. Scala

5.3.2. Clojure

5.3.3. Groovy

6. 分发机制

6.1. Java

6.1.1. 要表述“条件执行”

6.1.1.1. 很少的一些情况适用switch语句

6.1.1.2. 大多if语句

6.1.2. 依赖GoF模式集里面的Factory模式(或者Abstract Factory模式)来缓解问题

6.1.3. 没办法创造基本的语法构造单元,因此必须把问题翻译成符合编程语言语法的陈述

6.2. Groovy

6.2.1. switch语句在语法上模仿Java

6.2.1.1. 允许匹配区间和其他复杂类型

6.2.1.1.1. 功能要比Java的switch语句强大很多

6.3. Clojure

6.3.1. 开发者可以根据问题来修改语言,并没有一条明确的界线隔开语言设计者和使用语言来进行创作的开发者

6.3.2. 函数是比类更优先的语言成分

6.3.2.1. 它的函数调用用Java的眼光来看,就像内外颠倒了一样

6.3.2.2. Java下的score.toUpperCase()调用

6.3.2.3. 在Clojure下的等价写法是(.toUpperCase score)

6.3.3. 承载多态语义的多重方法(multimethod)特性允许开发者使用任意特征(及其组合)来触发分发

6.3.4. 多重方法赋予了Clojure构造强大分发机制的能力,其适应性不输于Java的多态,而且限制更少

6.3.5. 切断多态和继承之间的耦合关系

7. 运算符重载

7.1. Java

7.1.1. 没有运算符重载特性

7.1.1.1. 这是它的设计者在语言形成阶段就刻意作出的决定

7.2. Groovy

7.2.1. 将运算符自动映射成方法,从而令运算符重载变成了方法的实现问题

7.3. Scala

7.3.1. 完全不区分运算符和方法

7.3.2. 运算符不过是一些名字比较特别的方法

7.4. 现代语言大多已经相当程度地消除了定义上的复杂性,但以往关于滥用运算符重载的告诫都还是成立的

7.5. 要想契合问题域的表达习惯,可以利用运算符重载来改变语言的外貌,不必创造全新的语言

8. 函数式的数据结构

8.1. Java语言习惯使用异常来处理错误,语言本身提供了异常的创建和传播机制

8.2. “异常”违背了大多数函数式语言所遵循的一些前提条件

8.2.1. 函数式语言偏好没有副作用的纯函数

8.2.1.1. 抛出异常的行为本身就是一种副作用,会导致程序路径偏离正轨(进入异常的流程)

8.2.2. 引用的透明性(referential transparency)

8.2.2.1. 发出调用的例程不必关心它的访问对象真的是一个值,还是一个返回值的函数

8.2.2.2. 如果函数有可能抛出异常的话,用它来代替值就不再是安全的了

8.3. 函数式的错误处理

8.3.1. Either类

8.3.1.1. 不相交联合体(disjoint union)

8.3.1.2. 异常(如果有的话)置于Either类的左值上

8.3.1.3. 正常结果则放在右值

8.3.1.4. 可以容纳任意的内容

8.3.1.5. 返回结果(无论异常还是正常结果)需从Either中取出

8.3.1.5.1. 多了一道间接层
8.3.1.5.2. 实现缓求值的空间
8.3.1.5.3. 用来提供默认值
8.3.1.5.4. 用来包装异常
8.3.1.5.4.1. 将结构化的异常处理机制转化成函数式风格

8.3.2. Option类

8.3.2.1. 可以近似地看作Either类的一个子集

8.3.2.2. 表述了异常处理中较为简化的一种场景

8.3.2.2.1. none,表示不存在有效值
8.3.2.2.2. some,表示成功返回
8.3.2.2.3. 一般只用来表示成功和失败两种情况

有关读函数式编程思维笔记01_演化的语言的更多相关文章

  1. ruby - 如何将脚本文件的末尾读取为数据文件(Perl 或任何其他语言) - 2

    我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚

  2. ruby - 在没有 sass 引擎的情况下使用 sass 颜色函数 - 2

    我想在一个没有Sass引擎的类中使用Sass颜色函数。我已经在项目中使用了sassgem,所以我认为搭载会像以下一样简单:classRectangleincludeSass::Script::FunctionsdefcolorSass::Script::Color.new([0x82,0x39,0x06])enddefrender#hamlengineexecutedwithcontextofself#sothatwithintemlateicouldcall#%stop{offset:'0%',stop:{color:lighten(color)}}endend更新:参见上面的#re

  3. ruby-on-rails - 在 ruby​​ 中使用 gsub 函数替换单词 - 2

    我正在尝试用ruby​​中的gsub函数替换字符串中的某些单词,但有时效果很好,在某些情况下会出现此错误?这种格式有什么问题吗NoMethodError(undefinedmethod`gsub!'fornil:NilClass):模型.rbclassTest"replacethisID1",WAY=>"replacethisID2andID3",DELTA=>"replacethisID4"}end另一个模型.rbclassCheck 最佳答案 啊,我找到了!gsub!是一个非常奇怪的方法。首先,它替换了字符串,所以它实际上修改了

  4. ruby - 在 Ruby 中有条件地定义函数 - 2

    我有一些代码在几个不同的位置之一运行:作为具有调试输出的命令行工具,作为不接受任何输出的更大程序的一部分,以及在Rails环境中。有时我需要根据代码的位置对代码进行细微的更改,我意识到以下样式似乎可行:print"Testingnestedfunctionsdefined\n"CLI=trueifCLIdeftest_printprint"CommandLineVersion\n"endelsedeftest_printprint"ReleaseVersion\n"endendtest_print()这导致:TestingnestedfunctionsdefinedCommandLin

  5. ruby - 寻找通过阅读代码确定编程语言的ruby gem? - 2

    几个月前,我读了一篇关于ruby​​gem的博客文章,它可以通过阅读代码本身来确定编程语言。对于我的生活,我不记得博客或gem的名称。谷歌搜索“ruby编程语言猜测”及其变体也无济于事。有人碰巧知道相关gem的名称吗? 最佳答案 是这个吗:http://github.com/chrislo/sourceclassifier/tree/master 关于ruby-寻找通过阅读代码确定编程语言的rubygem?,我们在StackOverflow上找到一个类似的问题:

  6. ruby - 在 Ruby 中按名称传递函数 - 2

    如何在Ruby中按名称传递函数?(我使用Ruby才几个小时,所以我还在想办法。)nums=[1,2,3,4]#Thisworks,butismoreverbosethanI'dlikenums.eachdo|i|putsiend#InJS,Icouldjustdosomethinglike:#nums.forEach(console.log)#InF#,itwouldbesomethinglike:#List.iternums(printf"%A")#InRuby,IwishIcoulddosomethinglike:nums.eachputs在Ruby中能不能做到类似的简洁?我可以只

  7. Unity 热更新技术 | (三) Lua语言基本介绍及下载安装 - 2

    ?博客主页:https://xiaoy.blog.csdn.net?本文由呆呆敲代码的小Y原创,首发于CSDN??学习专栏推荐:Unity系统学习专栏?游戏制作专栏推荐:游戏制作?Unity实战100例专栏推荐:Unity实战100例教程?欢迎点赞?收藏⭐留言?如有错误敬请指正!?未来很长,值得我们全力奔赴更美好的生活✨------------------❤️分割线❤️-------------------------

  8. 7个大一C语言必学的程序 / C语言经典代码大全 - 2

    嗨~大家好,这里是可莉!今天给大家带来的是7个C语言的经典基础代码~那一起往下看下去把【程序一】打印100到200之间的素数#includeintmain(){ inti; for(i=100;i 【程序二】输出乘法口诀表#includeintmain(){inti;for(i=1;i 【程序三】判断1000年---2000年之间的闰年#includeintmain(){intyear;for(year=1000;year 【程序四】给定两个整形变量的值,将两个值的内容进行交换。这里提供两种方法来进行交换,第一种为创建临时变量来进行交换,第二种是不创建临时变量而直接进行交换。1.创建临时变量来

  9. 网络编程套接字 - 2

    网络编程套接字网络编程基础知识理解源`IP`地址和目的`IP`地址理解源MAC地址和目的MAC地址认识端口号理解端口号和进程ID理解源端口号和目的端口号认识`TCP`协议认识`UDP`协议网络字节序socket编程接口`sockaddr``UDP`网络程序服务器端代码逻辑:需要用到的接口服务器端代码`udp`客户端代码逻辑`udp`客户端代码`TCP`网络程序服务器代码逻辑多个版本服务器单进程版本多进程版本多线程版本线程池版本服务器端代码客户端代码逻辑客户端代码TCP协议通讯流程TCP协议的客户端/服务器程序流程三次握手(建立连接)数据传输四次挥手(断开连接)TCP和UDP对比网络编程基础知识

  10. C51单片机——实现用独立按键控制LED亮灭(调用函数篇) - 2

    说在前面这部分我本来是合为一篇来写的,因为目的是一样的,都是通过独立按键来控制LED闪灭本质上是起到开关的作用,即调用函数和中断函数。但是写一篇太累了,我还是决定分为两篇写,这篇是调用函数篇。在本篇中你主要看到这些东西!!!1.调用函数的方法(主要讲语法和格式)2.独立按键如何控制LED亮灭3.程序中的一些细节(软件消抖等)1.调用函数的方法思路还是比较清晰地,就是通过按下按键来控制LED闪灭,即每按下一次,LED取反一次。重要的是,把按键与LED联系在一起。我打算用K1来作为开关,看了一下开发板原理图,K1连接的是单片机的P31口,当按下K1时,P31是与GND相连的,也就是说,当我按下去时

随机推荐