Coursera

CSE341: Programming Languages

感谢华盛顿大学 Dan Grossman 老师 以及 Coursera

碎言碎语

  • 这只是 Programming Languages 这门课程第一部分,在 Part A 中通过 Standard ML 这门编程语言把函数式编程的美妙娓娓道来。在 Part B 以及 Part C 中分别会学习 Racket 以及 Ruby 。

  • 这是一门什么样的课呢?首先,它很好玩,虽然我学过 Scala 学过一些 Scheme,但还是能够感觉到这门语言的简洁和优雅。但是要记住,这门课最重要的不是教你学会 Standard ML 这门语言,而是函数式编程中的 immutable data 、function closures 、first-class function、higher-order function,以及这门语言中的 pattern matching、currying and partial application、type inference 等等(当然并不是这门语言所特有的,这些特性在 Scala 中都存在)。不仅仅是这些实际的东西,在 Part A 的 week 4 最后有几乎一小时的视频介绍了这门课程的动机:为什么要学习函数式编程?为什么选择这三门语言?

  • 为什么我会去学习这门课呢?首先这是华盛顿大学的一门 CS 课程,在网络上的评价很高,它的课程质量和作业质量得到了保证。再看看这门课程,它一共使用了三门编程语言,既有函数式编程也有面向对象编程,它并不会试图告诉你这门语言的全部,而是专注于语言特性,一些使得这门语言变得有力和优雅的语言特性,对于一门陌生的语言,这门课程首先会引导我们看见它、使用它,然后再尽可能地作出解释,而不仅仅是停留在使用的阶段。

  • 作业的形式是编程题目,有自动打分系统,如果额外的 Challenge Problems 以及 Extra Practice Problems 都能完成的话,还是要花不少时间的。完成要求的题目并不难,思考最简洁、最优雅的代码才是最重要的。

  • 笔记其实是没多少的,为什么呢?课程中给出的 Reading Notes 真是干货满满,值得反复阅读,它提炼了老师的讲课 ppt,不过 20 页的 pdf ,内容却充实的很,而又往往前后呼应,情节跌宕起伏,怎么像是说小说一样。举个例子,它的标题是这样的:By Name vs. By Position, Syntactic Sugar, and The Truth About Tuples ,是不是很容易有种一探究竟的冲动呢。

  • 这门课上到现在直观的感受就是:循序渐进。你不用担心它讲的是废话,因为它总能解决你在前面提出的问题(如果认真去学,问题总是很多的),编程中遇到的问题,总能在某一个知识点找到解决方法,理解其中的为什么。

  • 当你遇到问题时,几乎只能用英语去表述问题并去 google ,当然结果几乎都来自于 Stack Overflow,有时还需要 The Standard ML Basis Library

  • 并不是说有用的只有我记下的那么多(而且还加工了不少),考虑到阅读材料中的一些内容已经足够精简了。如果对这门课真有兴趣,不妨去上上看。

笔记

1. All values are expressions. Not all expressions are values.

2. SML 中没有赋值语句(assignment statement),它只有 变量绑定(variable binding) 以及 函数绑定(function binding)。

Bindings are immutable. Given val x = 8+9; we produce a dynamic environment where x maps to 17. In this environment, x will always map to 17; there is no “assignment statement” in ML for changing what x maps to. That is very useful if you are using x. You can have another binding later, say val x = 19; , but that just creates a different environment where the later binding for x shadows the earlier one. This distinction will be extremely important when we define functions that use variables.

an ML program is a sequence of bindings.Each binding adds to the static environment (for type-checking subsequent bindings) and to the dynamic environment (for evaluating subsequent bindings).

3. 它的讲述模式是这样的:先给出SML内置的常见的数据结构或语法,我们可以很容易去使用。再通过介绍核心的东西(数据结构的本质)以及特定的语法,以至于我们可以自己去定义这种数据结构(list 、option 都可以通过 datatype 定义出),重要的是核心的语法是极其少的。

定义 list :

datatype my_int_list = Empty
| Cons of int * my_int_list; fun append_mylist (xs, ys) =
case xs of
Empty => ys
| Cons(x, xs') => Cons(x, append_mylist(xs', ys)); val lst = Cons(1, Cons(2, Empty)); append_mylist(append_mylist(lst, Cons(1, Cons(3, Empty))), lst);

在这里 Empty 是 my_int_list 类型的值,而 Cons 是 int * my_int_list -> my_int_list 类型的函数。

4. 为什么函数名和参数间会有一个空格呢?看到阅读材料中的代码,心中不由产生疑惑,尤其是当一个函数只有一个参数时,我们可以直接省略其中的括号,例如:

fun f x = x + 1;
f(123);
f 123;

定义一个函数,接受参数 x 返回 x+1,原本只以为是内建语法,一个语法糖而已,等到后面看到了模式匹配,才发现并不是这样。为什么不需要括号呢?因为参数本来就是一个整体,即只有一个参数。

fun f (x, y, z) = x + y + z;

看起来很正常的一个函数,其实它只是在模式匹配下的一种简化,其实它应该是这样的:

fun f(t: int * int * int) =
case t of (x, y, z) => x + y + z;

也就是说它其实是接受一个 tuple 类型的参数,将对应位置上的值分别绑定到 x, y, z 三个变量上。

也就是说,在 SML 中一个函数只会接受一个参数(one-argument function)。难怪前面说过我们要抛却以前对于 C、Java 的观念来学习这门课。

更准确地说,函数一定会接受一个参数,那么问题来了,无参数函数该怎么定义呢?其实和其它语言类似:

fun f () = "HI";

这时候必须加上一个括号了,而这个括号其实是一个 unit 类型的值。那么对应的,在调用函数的时候,应该这样:

f ();

括号是一定不能省略的,否则它输出的是这个函数的类型:val f = fn : unit -> string

其实,datatype unit = ()在 SML 中是预定义的,用的也是前面提到的 datatype 。

5. 匿名函数

如果我们想要将一个函数作为参数去传递,但并不想把它直接绑定到当前环境中,此时就可以使用匿名函数了。

首先我们可以使用简单的语法来自己模拟匿名函数:

fun f1 (addone, x) =
addone x; f1 (let fun f x = x + 1 in f end, 11);

当然,我们也可以使用关键字去定义匿名函数。

f1 (fn x => x + 1, 11)

比起简单的关键词定义,那么我们模拟的这种匿名函数是不是就没有任何意义了呢?其实不然,匿名函数最显著的特点是什么呢,匿名,那么问题来了,没有名字,怎么递归呢。想支持递归,还得有个名字。

fun f1 (f, x) =
f x; f1 (let fun f x =
case x of
0 => 1
| q => q * f (q - 1)
in f end,
7)

6. Environments and Closures

首先,不得不提 lexical scope

我们知道,在 SML 中函数就是值,而函数这个值由两部分组成,组成这个函数的代码(即函数本身),以及我们创建这个函数时的环境(environment),当我调用这个函数时,实际上使用的是创建这个函数时的环境(environment),而那个环境里则可以进行一系列的计算,并产生结果。而这个环境和调用这个函数时的环境则是隔绝的,所以我们可以说一个函数构成了 function closure。

val x = 1
fun f y =
let
val x = y + 1
in
fn z => x + y + z
end
val x = 3
val g = f 4
val t = 5
val z = g 6

z 的值为 15 。

val x = 1
fun f y =
fn z => x + y + z
val x = 3
val g = f 4
val t = 5
val z = g 6 (* 15 *)

z 的值为 11 。

7. Currying and Partial Application

简单来说,就是一个本身具有多个参数的函数(其实仍是一个参数,即一个 tuple),我们把它变成一个只接受第一个参数,并返回一个接受第二个参数的函数,更多的参数类似。例如:

fun fold1 (f, acc, xs) =
case xs of
[] => acc
| x::xs' => fold1 (f, f(acc, x), xs') fun fold2 f = fn acc => fn xs =>
case xs of
[] => acc
| x::xs' => fold2 f (f(acc, x)) xs' val sum = fn (x, y) => x + y
val l = [1, 2, 3, 4]
val res1 = fold1 (sum, 0, l)
val res2 = (((fold2 sum) 0) l)
val res3 = fold2 sum 0 l

与 Scheme 相比,SML 看起来清爽很多(如果你以前写过那种结尾带着成吨的右括号的 Scheme 程序的话)。事实上,SML的括号是可选择的,即使不加括号,它也有默认的结合规则,关键字也起到了分割代码的作用。

由于 SML 中存在默认的结合规则,计算 res3 的表达式和计算 res2 的表达式完全相同。

而一个 curried function 的定义也可以简写成:

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

它们的类型:

val fold1 = fn : ('a * 'b -> 'a) * 'a * 'b list -> 'a
val fold2 = fn : ('a * 'b -> 'a) -> 'a -> 'b list -> 'a
val fold3 = fn : ('a * 'b -> 'a) -> 'a -> 'b list -> 'a

柯里化(Currying) 使得我们可以更加灵活的使用函数。

fun fold f acc xs =
case xs of
[] => acc
| x::xs' => fold2 f (f(acc, x)) xs' val is_even = fn (x, y) => x + (if y mod 2 = 0 then 1 else 0)
val count_even = fold is_even 0
val res1 = count_even [1, 2, 3, 4]
val res2 = count_even [2, 3, 4, 10, 11]

8. 关于 Value Restriction

参考

Coursera课程 Programming Languages, Part A 总结的更多相关文章

  1. Coursera课程 Programming Languages 总结

    课程 Programming Languages, Part A Programming Languages, Part B Programming Languages, Part C CSE341: ...

  2. Coursera课程 Programming Languages, Part B 总结

    Programming Languages, Part A Programming Languages, Part B Part A 笔记 碎言碎语 很多没有写过 Lisp 程序的人都会对 Lisp ...

  3. Coursera课程 Programming Languages, Part C 总结

    碎言碎语 和前面的 ML 和 Racket 感觉明显不一样了,一边学着一边觉得这真是一门奇怪的语言,有着各种奇怪的语法,不过真的算是一个奇妙的体验(相比前面的两门语言,Ruby 的学习资源多了不少). ...

  4. 【Python学习笔记】Coursera课程《Using Python to Access Web Data》 密歇根大学 Charles Severance——Week6 JSON and the REST Architecture课堂笔记

    Coursera课程<Using Python to Access Web Data> 密歇根大学 Week6 JSON and the REST Architecture 13.5 Ja ...

  5. 【C语言】Coursera课程《计算机程式设计》台湾大学刘邦锋——Week6 String课堂笔记

    Coursera课程 <计算机程式设计>台湾大学 刘邦锋 Week6 String 6-1 Character and ASCII 字符变量的声明 char c; C语言使用一个位元组来储 ...

  6. ESSENTIALS OF PROGRAMMING LANGUAGES (THIRD EDITION) :编程语言的本质 —— (一)

    # Foreword> # 序 This book brings you face-to-face with the most fundamental idea in computer prog ...

  7. Coursera课程下载和存档计划[转载]

    上周三收到Coursera平台的群发邮件,大意是Coursera将在6月30号彻底关闭旧的课程平台,全面升级到新的课程平台上,一些旧的课程资源(课程视频.课程资料)将不再保存,如果你之前学习过相关的课 ...

  8. Natural language style method declaration and usages in programming languages

    More descriptive way to declare and use a method in programming languages At present, in most progra ...

  9. The future of programming languages

    In this video from JAOO Aarhus 2008 Anders Hejlsberg takes a look at the future of programming langu ...

随机推荐

  1. Python学习之二:Python 与 C 区别

    引自http://www.lxway.com/181844.htm 从开始看Python到现在也有半个多月了,前后看了Python核心编程和Dive into Python两本书.话说半个月看两本,是 ...

  2. Android_简易的短信发送器

    这个随笔将介绍如何完成一个简单的第三方的短信发送器(不打开短信界面,调用android的api完成功能) 1.首先,我们来做布局 由于我这里写的是一个简易的,,短信发送,所以只是一个LinearLay ...

  3. 深度学习系列 Part (1)

    传统机器学习的回顾 近年来,深度学习的概念十分火热,人工智能也由于这一技术的兴起,在近几年吸引了越来越多的关注.我们这里,将结合一些基本的用例,简要的介绍一下这一新的技术. 我们首先需要明确人工智能. ...

  4. Leetcode题解(一)

    1.Two Sum 题目 此题第一解题思路,就是最常见的方法,两个指针嵌套遍历数组,依次判断当前指针所指向的值是否满足条件.代码如下; class Solution { public: vector& ...

  5. 磁盘管理 之 parted命令添加swap,文件系统

    第1章 磁盘管理 1.1 必须要了解的. 1.1.1 ps aux 命令中 RSS 与VSZ的含义 rss 进程占用的物理内存的大小 单位:kb : vsz 进程占用的虚拟的内存大小(物理内存+swa ...

  6. G彩娱乐网【分享】想要开源自己的代码可以参考一下

    作为一个开发者,如果你打算开源自己的代码,千万不要忘记,选择一种开源许可证(license). 许多开发者对开源许可证了解很少,不清楚有哪些许可证,应该怎么选择.本文介绍开源许可证的基本知识,主要参考 ...

  7. Linux系列教程(十七)——Linux权限管理之文件系统系统属性chattr权限和sudo命令

    上篇博客我们介绍了权限管理的ACL权限,通过设定 ACL 权限,我们为某个用户指定某个文件的特定权限.这篇博客我们将介绍权限管理中用的比较多的两个命令 chattr 和 sudo . 1.设定文件系统 ...

  8. Java设计模式相关面试

    1.接口是什么?为什么要使用接口而不是直接使用具体类? 接口用于定义 API.它定义了类必须得遵循的规则.同时,它提供了一种抽象,因为客户端只使用接口,这样可以有多重实现,如 List 接口,你可以使 ...

  9. Velocity(7)——velocity进阶用法

    1.截取部分字段substring 1 原始字符串:$!ag.tagValue,也许很长,前端页面展示时需要截取字符串. 2 #set($str=$!ag.tagValue) 3 4 #if($str ...

  10. Java多线程与并发模型之锁

    这是一篇总结Java多线程开发的长文.文章是从Java创建之初就存在的synchronized关键字引入,对Java多线程和并发模型进行了探讨.希望通过此篇内容的解读能帮助Java开发者更好的理清Ja ...