草庐IT

读函数式编程思维笔记03_权责让渡

躺柒 2023-03-28 原文

1. 观点

1.1. 抽象隐藏了繁杂的细节,只是有时候会连同重要的考虑因素一起隐藏掉

1.2. 理解掌握的抽象层次永远要比日常使用的抽象层次更深一层

1.3. 交出控制权的观点:放弃对繁琐细节的掌控,关注问题域,而非关注问题域的实现

2. 函数式思维的好处

2.1. 将低层次细节(如垃圾收集)的控制权移交给运行时,从而消弭了一大批注定会发生的程序错误

2.2. 函数式语言的简洁语法和灵活配合,才使递归成为简单可行的代码重用选项之一

2.3. 运行时有能力在涉及效率的问题上替我们做决定

2.4. 从频繁出现的场景中消灭掉烦人的实现细节

3. 闭包(closure)

3.1. 一种特殊的函数,在生成的时候,会把引用的变量全部圈到代码块的作用域里,封闭、包围起来(故名闭包)

3.1.1. 闭包作为一种对行为的建模手段,让我们把代码和上下文同时封装在单一结构,也就是闭包本身里面,像传统数据结构一样可以传递到其他位置,然后在恰当的时间和地点完成执行

3.2. 闭包的每个实例都保有自己的一份变量取值,包括私有变量也是如此

3.2.1. 代码块实例从它被创建的一刻起,就持有其作用域内一切事物的封闭副本

3.3. 在缺乏闭包特性的旧版Java平台上,Functional Java利用匿名内部类来模仿“真正的”闭包的某些行为,但语言的先天不足导致这种模仿是不彻底的

3.4. 当作一种异地执行的机制,用来传递待执行的变换代码

3.5. 是推迟执行原则的绝佳样板

3.6. 抓住上下文,而非状态

3.6.1. “让运行时去管理状态”

4. 柯里化(currying)和函数的部分施用(partial application)

4.1. 向一部分参数代入一个或多个默认值的办法来实现的

4.1.1. 这部分参数被称为“固定参数”

4.2. 柯里化

4.2.1. 从一个多参数函数变成一连串单参数函数的变换

4.2.2. 结果是返回链条中的下一个函数

4.3. 部分施用

4.3.1. 通过提前代入一部分参数值,使一个多参数函数得以省略部分参数,从而转化为一个参数数目较少的函数

4.3.2. 把参数的取值绑定到用户在操作中提供的具体值上,因而产生一个“元数”(参数的数目)较少的函数

4.4. Groovy

4.4.1. curry()函数实现柯里化

4.5. Clojure

4.5.1. (partial f a1 a2 …)函数

4.5.2. 没有将柯里化实现成一种语言特性,相关的场景交由部分施用去处理

4.6. Scala

4.6.1. 柯里化

4.6.2. 部分施用函数

4.6.3. 偏函数

4.6.3.1. PartialFunction trait是为了密切配合语言中的模式匹配特性

4.6.3.2. trait并不生成部分施用函数。它的真正用途是描述只对定义域中一部分取值或类型有意义的函数

4.6.3.3. Case语句是偏函数的一种用法

4.6.3.4. 偏函数的参数被限定了取值范围

4.6.3.5. 可以把偏函数用在任何类型上,包括Any

4.7. 大多数函数式语言都具备柯里化和部分施用这两种特性,但实现上各有各的做法

4.8. 用途

4.8.1. 函数工厂

4.8.1.1. 工厂方法的场合,正适合柯里化(以及部分施用)表现它的才干

4.8.2. Template Method(模板方法)模式

4.8.2.1. 在固定的算法框架内部安排一些抽象方法,为后续的具体实现保留一部分灵活性

4.8.3. 隐含参数

5. 递归

5.1. 以一种自相似的方式来重复事物的过程

5.2. 对一个不断变短的列表反复地做同一件事,把递归用在这样的场合,写出来的代码就容易理解

5.3. 递归操作往往受制平台而存在一些固有的技术限制,因此这种技法绝非万灵药

5.4. 但对于长度不大的列表来说,递归操作是安全的

5.5. 语言在管理返回值,它从递归栈里收集每次方法调用的返回结果,构造出最终的返回值

5.6. 利用递归,把状态的管理责任推给运行时

6. 记忆(memoization)

6.1. 用更多的内存(我们一般不缺内存)去换取长期来说更高的效率

6.1.1. 缓存可以提高性能,但缓存有代价:它提高了代码的非本质复杂性和维护负担

6.1.2. 负责编写缓存代码的开发者不仅要顾及代码的正确性,连它的执行环境也要考虑在内

6.1.3. 代码中的状态,开发者不仅要费心照应它,还要条分缕析它的一切明暗牵连

6.2. 记忆的内容应该是值不可变的

6.3. 保证所有被记忆的函数

6.3.1. 没有副作用

6.3.2. 不依赖任何外部信息

6.4. 只有纯(pure)函数才可以适用缓存技术

6.4.1. 纯函数是没有副作用的函数

6.4.1.1. 它不引用其他值可变的类字段

6.4.1.2. 除返回值之外不设置其他的变量

6.4.1.3. 其结果完全由输入参数决定

6.4.2. 只有在函数对同样一组参数总是返回相同结果的前提下,我们才可以放心地使用缓存起来的结果

6.5. 缓存是很常见的一种需求,同时也是制造隐晦错误的源头

6.6. 两种情况

6.6.1. 类内部缓存

6.6.1.1. 类中的缓存就代表类有了状态,所有与缓存打交道的方法都不可以是静态的,于是产生了更多的连锁效应

6.6.2. 外部调用

6.7. 两种实现方式

6.7.1. 手工进行状态管理

6.7.2. 采用记忆机制

6.8. 在命令式的思路下,开发者是代码的主人(以及一切责任的承担者)

6.9. 我们写出来的缓存绝不可能比语言设计者产生的更高效,因为语言设计者可以无视他们给语言定的规矩:开发者无法触碰的底层设施,不过是语言设计者手中的玩物,他们拥有的优化手段和空间是“凡人”无法企及的

6.9.1. 上帝视角

6.10. Groovy

6.10.1. 先将要记忆的函数定义成闭包,然后对该闭包执行memoize()方法来获得一个新函数,以后我们调用这个新函数的时候,其结果就会被缓存起来

6.10.2. memoizeAtMost(1000)

6.11. Clojure

6.11.1. (memoize )

6.12. Scala

6.12.1. 没有直接提供记忆机制,但它为集合提供的getOrElseUpdate()方法已经替我们承担了大部分的实现工作

6.13. Java 8

6.13.1. 没有直接提供记忆特性,但只要借助它新增的lambda特性,就可以轻松地实现记忆功能

7. 缓求值(lazy evaluation)

7.1. 尽可能地推迟求解表达式

7.1.1. 昂贵的运算只有到了绝对必要的时候才执行

7.1.2. 可以建立无限大的集合,只要一直接到请求,就一直送出元素

7.1.3. 按缓求值的方式来使用映射、筛选等函数式概念,可以产生更高效的代码

7.1.4. 减少占用的存储空间。假如能够用推导的方法得到后续的值,那就不必预先存储完整的列表了——这是牺牲速度来换取存储空间的做法

7.2. 非严格求值(non-strict)的(也叫缓求值,lazy)

7.2.1. 常用的非严格求值语言有Haskell

7.3. Totally Lazy框架(Java)

7.4. Groovy

7.4.1. 缓求值列表是函数式语言普遍具备的特性

7.4.1.1. LazyList

7.4.2. 暂缓初始化昂贵的资源,除非到了绝对必要的时候

7.4.3. 可以用来构建无限序列,也就是没有上边界的列表

7.4.4. 缓求值列表特别适用于资源的生产成本较高的情况

7.5. Clojure

7.5.1. 数据结构都是默认缓求值的

7.6. Scala

7.6.1. 没有把一切都默认安排成缓求值的,而是在集合之上另外提供了一层缓求值的视图

7.7. 缓求值的字段初始化

7.7.1. Scala

7.7.1.1. val声明前面加上“lazy”字样

7.7.1.1.1. 令字段从严格求值变成按需要求值

7.7.2. Groovy

7.7.2.1. 抽象语法树(Abstract Syntax Tree,AST)

7.7.2.1.1. @Lazy标注

8. 元函数技法

8.1. 操纵的对象是函数本身,而非函数的结果

8.2. 柯里化

有关读函数式编程思维笔记03_权责让渡的更多相关文章

  1. 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

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

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

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

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

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

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

  5. 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中能不能做到类似的简洁?我可以只

  6. 网络编程套接字 - 2

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

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

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

  8. LC滤波器设计学习笔记(一)滤波电路入门 - 2

    目录前言滤波电路科普主要分类实际情况单位的概念常用评价参数函数型滤波器简单分析滤波电路构成低通滤波器RC低通滤波器RL低通滤波器高通滤波器RC高通滤波器RL高通滤波器部分摘自《LC滤波器设计与制作》,侵权删。前言最近需要学习放大电路和滤波电路,但是由于只在之前做音乐频谱分析仪的时候简单了解过一点点运放,所以也是相当从零开始学习了。滤波电路科普主要分类滤波器:主要是从不同频率的成分中提取出特定频率的信号。有源滤波器:由RC元件与运算放大器组成的滤波器。可滤除某一次或多次谐波,最普通易于采用的无源滤波器结构是将电感与电容串联,可对主要次谐波(3、5、7)构成低阻抗旁路。无源滤波器:无源滤波器,又称

  9. ruby-on-rails - 将字符串转换为 ruby​​-on-rails 中的函数 - 2

    我需要一个通过输入字符串进行计算的方法,像这样function="(a/b)*100"a=25b=50function.something>>50有什么方法吗? 最佳答案 您可以使用instance_eval:function="(a/b)*100"a=25.0b=50instance_evalfunction#=>50.0请注意,使用eval本质上是不安全的,尤其是当您使用外部输入时,因为它可能包含注入(inject)的恶意代码。另请注意,a设置为25.0而不是25,因为如果它是整数a/b将导致0(整数)。

  10. ruby - 我正在学习编程并选择了 Ruby。我应该升级到 Ruby 1.9 吗? - 2

    我完全不是程序员,正在学习使用Ruby和Rails框架进行编程。我目前正在使用Ruby1.8.7和Rails3.0.3,但我想知道我是否应该升级到Ruby1.9,因为我真的没有任何升级的“遗留”成本。缺点是什么?我是否会遇到与普通gem的兼容性问题,或者甚至其他我不太了解甚至无法预料的问题? 最佳答案 你应该升级。不要坚持从1.8.7开始。如果您发现不支持1.9.2的gem,请避免使用它们(因为它们很可能不被维护)。如果您对gem是否兼容1.9.2有任何疑问,您可以在以下位置查看:http://www.railsplugins.or

随机推荐