首页 > 编程语言 >读函数式编程思维笔记03_权责让渡

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

时间:2023-01-22 13:33:07浏览次数:38  
标签:闭包 03 缓存 函数 权责 编程 4.6 参数 求值

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,缓存,函数,权责,编程,4.6,参数,求值
From: https://www.cnblogs.com/lying7/p/17064393.html

相关文章

  • 探究:初学者编程语言的选择
    目录前景提要编程语言选择面临的问题零基础的理解可能不一样?真正初学者编程语言选择面临的问题编程语言难易程度问题形成编程思维的人和没有思维的入门者的区别语言难度问......
  • 【学懂Java】(四)面向对象编程-5
    一.Object类Object类是所有类的父类,一个类如果没有使用extends显性的继承另外一个类,那么这个类就继承自Object类publicclassPerson{}//等同于publicclassPersonextends......
  • P5030 题解
    前言题目传送门!更好的阅读体验?一道没啥意思的题目,但是好像很多题解都过不了现在的数据?思路只不过是把正常题目的马(\(1,2\))换成了另一种东西(\(1,3\))。很套路地,黑白......
  • day23-网络编程01
    1.网络编程入门1.1网络编程概述【理解】计算机网络是指将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路连接起来,在网络操作系统,网络管理软件及网......
  • day24-网络编程02
    1.NIO1.1NIO通道客户端【应用】客户端实现步骤打开通道指定IP和端口号写出数据释放资源示例代码publicclassNIOClient{publicstaticvoidmain(St......
  • linux环境编程(1): 实现一个单元测试框架
    写在前面在开发的过程中,大多数人都需要对代码进行测试。目前对于c/c++项目,可以采用google的gtest框架,除此之外在github上搜索之后可以发现很多其他类似功能的项目。但把别......
  • 算法编程 dfs 从先序和中序遍历还原二叉树
    105.从前序与中序遍历序列构造二叉树给定两个整数数组 preorder和inorder ,其中 preorder是二叉树的先序遍历,inorder 是同一棵树的中序遍历,请构造二叉树并返回......
  • 【学懂Java】(四)面向对象编程-3
    一.代码块1.概念使用”{}”括起来的一段代码2.分类根据位置可分类普通代码块:定义在方法中的使用{}括起来的代码publicclassCodeBlockDemo{publicvoidtest(){......
  • 【学懂Java】(四)面向对象编程-4
    一.继承(面向对象编程四大特征)继承优化前:代码重复,不利于修改继承优化后:代码简明,利于修改1.概念类是对对象的抽象,继承是对某一批类的抽象,从而实现对现实世界更好的建模。提高......
  • JavaScript 异步编程
    JavaScript是一种单线程语言,这意味着只有一个线程可以在同一时间执行JavaScript代码。在JavaScript中,异步编程是一种编写不阻塞代码的方式,它可以在不影响页面响应的情......