文章目录

  • Introduction to First-Class Functions 函数是一等公民
  • Functions as Arguments
  • Polymorphic Types and Functions as Arguments
  • Anonymous Functions
  • Unnecessary Function Wrapping
  • Map and Filter
  • Generalizing Prior Topics
  • Lexical Scope
  • Lexical Scope and Higher-Order Functions
  • Why Lexical Scope
  • Closures and Recomputation
  • Fold and More Closures
  • Closure Idiom: Combining Functions
  • * Closure Idiom: Currying
  • Partial Application
  • Currying Wrapup
  • Mutable References
  • * Closure Idiom: Callbacks
  • Standard-Library Documentation
  • Optional: Abstract Data Types With Closures
  • Optional: Closure Idioms Without Closures
  • Optional: Java Without Closures
  • 高等函数和闭包的重要用法总结

Introduction to First-Class Functions 函数是一等公民

前几周的课程虽然也在接触函数式编程的一些表达方式,但本周正式进入函数式编程的主题

函数作为一等公民(一等函数),本身就是一个值(fn类型),可以作为其他函数(高等函数)的参数、返回结果、tuple的元素、变量绑定、datatype的构造器或者异常等。


Functions as Arguments

函数作为参数传递

例子

上面的三个函数具有相似的模式,都是对自身的递归进行一个运算(+,* ,tl),可以用一等函数进行简化

然后可以对这些重载函数进行封装

Polymorphic Types and Functions as Arguments

使用函数作为高等函数的参数时要注意,一等函数的参数一般需要多态类型,适用性更广泛

Anonymous Functions

匿名函数在各种语言中都很常见,SML也有类似用法

对于上一节的n_times函数,其中一种调用方式是全部定义为函数

更好的一种方式是使用嵌套helper函数

或者将它直接写在调用的位置

但事实上我们只需要在这里调用一次这个函数,所以不需要function binding,因此只需要写成匿名形式。fn表示函数值,并且用=>代替函数的等号。

匿名函数最常用在高等函数中,但无法递归(因为没有名字)

同时,由于函数是个值,函数本身可以赋值给某个val,相当于给匿名函数命名

Unnecessary Function Wrapping

不要对函数重复包装,例如hd和tl已经是函数了,不需要再在匿名函数里包装一次。例如:

Map and Filter

重要的高等函数,

map,用来对列表的每个元素进行映射:

fun map(f,xs) = case xs of[] => []|x::xs' => (f x) :: (map (f,xs'))

filter ,用来筛选符合条件的列表元素:

fun filter (f,xs) = case xs of[] => []|x::xs' => if f x then x::(filter (f,xs'))else filter(f,xs')

Generalizing Prior Topics

一等函数有四大类应用场景,前面的课程着重介绍了作为函数参数和数据结构的元素。

这里介绍第三种用法,将函数作为函数返回值的用法:

这里值得注意的是最后一句话,在REPL的函数类型显示中,对于函数的->,默认括号从最后两项->开始,例如

t1->t2->t3->t4 相当于是 t1-> (t2->(t3->t4))

下面介绍第四种用法,使用更通用的函数类型,例如包含自定义的datatype

Lexical Scope

词法范围,词法作用域(也叫静态作用域),函数所处的作用域取决于函数定义位置而不是调用的位置(也就是说绑定的变量与调用位置附近代码块中定义的局部变量无关,而只与定义时的代码块附近的变量有关)

比方说下面这个C++的例子。

例子:

函数闭包,SML中的函数闭包指的是包含函数Code(代码定义)和定义时的Environment(变量环境(静态动态))两方面情况的值。 (所以从概念上来说,SML的函数闭包比JavaScript的闭包概念范围更大,但实质上类似)

也就是说函数作为一个值,本身包含Code和Environment两方面,传递时也同时传递。

函数调用时,在Environment的条件下,根据函数参数按照Code计算函数值。

接下来介绍的内容:

Lexical Scope and Higher-Order Functions

高等函数中词法作用域的使用

例子:

例二中 let部分从没有被函数中的in部分用到,所以完全可以不写(irrelevant)

Why Lexical Scope

比较函数的Lexical Scope 和Dynamic Scope(动态作用域)

原因一,函数意味着不依赖于所用的变量名(函数的作用是一定的,与内部的变量名无关):

原因二,函数可以被type-check 并且被推出定义的位置(不会因为变量shadowing而导致类型错误)

原因三,闭包能够容易地存储需要的数据

Dynamic scope存在于某些语言(例如Racket),exception可以看成是类似dynamic scope(在raise(调用)的附近找到handler来处理,而不是在定义的位置)

Closures and Recomputation

函数闭包可以避免对某些不依赖函数参数的值重复计算(将这类参数作为变量binding,则只需要计算一次)

重要的表达式:**SML中,(e1; e2)**表示执行计算e1的值,然后将其丢弃,再执行计算e2的值作为整个表达式的值(类似C语言中的逗号表达式)。可以用来执行print语句。

例如,此时allShorterThan1方法需要执行多次print,而allShorterThan2方法不需要重复计算,只需要执行一次binding语句,print也就只执行一次:

Fold and More Closures

Fold对列表的每个值嵌套计算f的值,下面的例子从列表左侧开始,称为folds left

Fold实现了类似迭代器的功能

fold的应用例子,以及一些复杂的例子

上面的例子想说明的是,闭包为传递的函数带来了更多的优势,使用时需要考虑lexical scope,因此也能够使用某些private变量

Closure Idiom: Combining Functions

(* 组合两个函数 *)
fun compose (f,g) = fn x => f(g x)

相当于复合函数,规则和数学中相同,调用顺序从右到左

SML标准库为该用法提供了中缀运算符(infix operater)字母 o

但如果想要让复合规则从左到右,需要使用infix关键词自己定义运算符 (例如从F#中学习得到的 |>管道运算符)及其用法(例如 fun x |> f = f x)。(这里的管道运算符用法和linux的管道运算符一样)

* Closure Idiom: Currying

柯里化(Currying),是函数式编程中的一个重要概念,根据百度百科的定义:

在计算机科学中,柯里化(Currying)是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数的技术。这个技术由 Christopher Strachey 以逻辑学家 Haskell Curry 命名的,尽管它是 Moses Schnfinkel 和 Gottlob Frege 发明的。

在直觉上,柯里化声称“如果你固定某些参数,你将得到接受余下参数的一个函数”。所以对于有两个变量的函数yx,如果固定了 y = 2,则得到有一个变量的函数 2x。

在理论计算机科学中,柯里化提供了在简单的理论模型中比如只接受一个单一参数的lambda 演算中研究带有多个参数的函数的方式。

而ML语言同Haskell一样,正好属于只接受单一参数的语言,因此,想要研究多参数函数问题,除了之前使用的Tuple方法(利用一个tuple参数携带所有多参数),Currying是另一种可行的解决方案。

Currying就是将原有的多个参数的n-tuple拆分成n个真正单参数的function,然后依次返回functions,利用闭包特性,内层函数能够访问所有的n个参数

由于括号从左到右具有结合性,所以Currying的调用也可以不带括号

根据Currying的调用方式,采取了一种约定俗成的方法来简化其声明过程,即fun f p1 p2 p3 … = e 也可表示Currying的声明,注意这里因为直接是fun符号所以使用赋值符号=

利用Currying实现Fold

Partial Application

偏函数应用(Partial Application),在调用时提供少于需要的变量,可以得到一个闭包(等待剩余参数)。类似于固定某几个变量,剩下一部分变量未定的函数。

partial application 可以避免一些无意义的函数封装

exists function判断列表中是否存在某种值

值得注意的是,partial application不允许创造一个多态函数,也就是说必须要定义或者让编译器能判断创造的函数的参数类型(不能是’a),否则会给出Warning

例如

val pairWithOne = List.map(fn x => (x,1))
(* 'a list -> ('a * int) list *)
(* Value Restriction !!! *)

但可以用一些方式来避免这样的情况发生,例如显式写出函数类型,或者让编译器能判断类型

Currying Wrapup

当我们想要在Tupled function 和 Currying function之间切换或者想要交换Currying function中某几个参数的顺序时,可以通过函数包装(wrapup),用一个辅助函数来实现。

(其实我觉得设计两种需要显式转换才能共通的函数调用方式会破坏语言的一致性,不知道ML的设计人员怎么考虑的)

效率问题,tupling和currying效率总体相差不大

Mutable References

虽然SML基本上不具备可修改的特性,但还是为特殊情况提供了可修改对的数据结构,即Reference

语法:

Reference的可修改指的是其中包含的数据可修改,因此获取这个数据需要使用特殊符号 ! e。

Reference 在这里类似于C语言的指针用法,相当于一个本身不可变的指针。ref本身绑定的变量是不可变的(例如想x,y,z不能改变他们的值,只能shadowing),但ref指向的其中包含的值是可变的(! e可以通过:=来赋值),相当于改变了ref指向位置的变量的值(或者直接改变了指向的变量地址,具体底层怎么做的不太清楚)

* Closure Idiom: Callbacks

闭包典型用法:回调。这也是非常重要的一节,回调在许多语言中都十分常用

我们在回调时需要可修改状态

Standard-Library Documentation

SML提供了标准库,其文档网址为https://www.standardml.org/Basis/manpages.html

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZpSvZwSH-1663158632481)(http://47.108.198.122/images/blogimage-master/new_img/week4.assets/image-20220522091451783.png)]

Optional: Abstract Data Types With Closures

Optional: Closure Idioms Without Closures

Optional: Java Without Closures

本课程的一大重点就是将某种语言的语法semantics(特性)在其他语言中通过习惯用法idioms的方式来实现,包括之后的课程中也有很多这样的例子。

这一节就是在Java中实现闭包用法的方式

高等函数和闭包的重要用法总结

高等函数中传递一等函数作为参数,函数都具有闭包特性。

map

filter

fold

Programming Languages PartA Week4学习笔记——SML函数式编程相关推荐

  1. Programming Languages PartA Week2学习笔记——SML基本语法

    Programming Languages PartA Week2学习笔记--SML基本语法 首先简单介绍使用的SML语言,参考维基百科和百度百科: ML(Meta Language:元语言)是由爱丁 ...

  2. Programming Languages PartA Week3学习笔记——SML基本语法第二部分

    文章目录 Building Compound Types Records Tuples as Syntactic Sugar Datatype Bindings Case Expressions Us ...

  3. Programming Languages PartA Week5学习笔记——SML进阶与编程哲学

    文章目录 Week5 Introduction What is Type Inference ML Type Inference Type Inference Examples Polymorphic ...

  4. Modern C++ 学习笔记——C++函数式编程

    往期精彩: Modern C++ 学习笔记--易用性改进篇 Modern C++ 学习笔记 -- 右值.移动篇 Modern C++ 学习笔记 -- 智能指针篇 Modern C++ 学习笔记 -- ...

  5. 【廖雪峰Python学习笔记】函数式编程

    Functional Programming 高阶函数 返回函数 匿名函数 装饰器 偏函数 高阶函数 面向过程的程序设计: 把大段代码拆成函数,通过一层层函数调用,可将复杂任务分解成若干简单的任务 函 ...

  6. Python学习笔记之函数式编程

    python中的高阶函数 高阶函数就是 变量名指向函数,下面代码中的变量abs其实是一个函数,返回数字的绝对值,如abs(-10) 返回 10 def add(x,y,f):return f(x) + ...

  7. Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第十五章:第一人称摄像机和动态索引...

    Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第十五章:第一人称摄像机和动态索引 原文:Introduction to 3 ...

  8. Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第十九章:法线贴图

    Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第十九章:法线贴图 原文:Introduction to 3D Game P ...

  9. Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第十三章:计算着色器(The Compute Shader)...

    Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第十三章:计算着色器(The Compute Shader) 原文: Int ...

  10. UE4学习笔记1st:编程快速入门

    UE4学习笔记1st:编程快速入门 今天我开始学习虚幻4游戏引擎,为了此我专门买了新的电脑,我将主要配置写在这里,有想学习的同学可以参考 显卡:丽台K620 CPU:E3-1230-V3 主板:b85 ...

最新文章

  1. C++ 复制构造函数或者拷贝构造函数
  2. SQLServer查询指定日期
  3. Vue_双向绑定解析以及指令介绍
  4. DOM对象和内置对象(上)
  5. [Openwrt 项目开发笔记]:Samba服务vsFTP服务(四)
  6. 皮一皮:这位家长,建议自己退群聊...
  7. linux mamp 设备内存
  8. Redis-01Redis概述
  9. [转]Windows Shell 编程 第十三章 【来源:http://blog.csdn.net/wangqiulin123456/article/details/7988004】...
  10. JavaScript:undefined And null差异
  11. POJ-3635 Full Tank? 变形最短路
  12. 为什么大公司一定要使用DevOps
  13. javapanel根据内部组件_java gui中怎么用jpanel实现组件的绝对定位
  14. Java RandomAccessFile readChar()方法及示例
  15. 动态网页开发技术(二):Servlet
  16. UI设计素材干货模板|手机app夜间模式相关素材
  17. ajax+++fc,06. 实做AJAX(SEFC)
  18. 他实现了AlphaGo Zero的算法,发现可能还得训练1700年 | 代码
  19. kpi权重设置原则_东阳用友ERP评价体系的建立原则及过程
  20. 软件测试薪资标准新鲜出炉,你达标了吗?

热门文章

  1. 【读书笔记】两个天才 发挥孩子的创造力 4岁以上 蒲蒲兰绘本
  2. LTE上行物理层传输机制(5)-CQI的传输方式
  3. ruby_Ruby简介
  4. 四级单词pdf_英语单词里的字母到底有没有含义?
  5. 18数藏,太一捡漏,抢购,
  6. C# 命名空间中不存在类型或命名空间名System.XXX
  7. 根据地址查询经纬度Js
  8. mysql数据迁移不停机_数据迁移还需要停机?不停机上线的正确姿势你能get到吗?...
  9. 如果你想专升本那就一定要看的专升本语文_文学常识完整版(五)
  10. DataFrame-删除行列