编程语言设计--coursera课程笔记(第一周)

630 阅读8分钟

动机

无意中看到的知乎推荐,了解到了这门课,觉得自己经常忙于学习新东西新框架,应该多去关注下那种普适性的知识,比如数据结构,语言设计什么的,所谓的基础知识。扫了下课程的大致内容里面提到了high-order-function, type inference等前端最近经常提到的概念瞬间就有了兴趣呢^_^

课程主页

课程简介

这门课聚焦在不同语言的通用核心概念上,可以帮忙更好的理解现在使用的语言以及掌握新的语言。当我们谈及语言时通长避不开下面5个方面

  1. syntax语法。语言规定怎么写的,比如在ml里布尔值连接是 andalso 其他很多语言是&&
  2. semantics语义。就是概念类的,语言里的各个特性的含义。syntax和semantics的区别 - jiyanfeng1的专栏 - CSDN博客syntax是句法,semantics是语义,不同语言针对相同的语义有不同的句法。讲义上提到了type checking rules 和evaluation rules,它们也算语义方面的嘛。我理解的是比如说’加法‘这是个语义。所以我个人感觉这里的应该是每个语言都有type checking 的语义和evaluate的语义这两个概念,只是规则不同。
  3. idioms. 风格。语言特性的组合常规使用模式。组合使用语言里的特性结果某类特定问题,比如在sml里nest function 就是利用了let expression的特性。
  4. libraries 库。访问系统文件类的语言不提供给你你就没法自己做的;还有就是那种类似util类的函数可以自己实现。
  5. tools 工具。REPL debugger 这些都不是语言的一部分,只是某个语言的具体实现为了让你工作更顺畅所提供给你的功能。比如debugger在javascript中是浏览器给了它调试的功能。

这门课聚焦在semantics和idioms上

本门课的编程语言以及工具

EMACS

教授说是不用太多时间去设置环境,但是很多快捷键很难记,其实现在vscode很好用了也是开箱即用。。。最主要的原因还是因为课程里用的是这个,为了和教授保持一致还是用吧o(╯□╰)o

ML, Racket, Ruby

这门课三部分,每个部分使用不同的编程语言进行教学。两个函数式语言一个面向对象语言,语言本身不重要,重要的是了解语言是怎么将一系列概念组合起来的,以及一些编程范式等。


ML表达式类型和值

环境

ML程序是一系列的绑定binding. 每个绑定都要进行类型检查type-checked, 如果类型检查通过后再进行求值(evaluate). 每个绑定的类型是由静态环境(static enviroment)决定的,里面存放了一些列的绑定的类型。绑定的值由由动态环境(dynamic environment)决定的,里面存放了之前一系列绑定的值。静态环境有个同义词叫上下文(context)。e.g. 值不存在的报错是由静态环境告知的

表达式expression

值value本身也是表达式,value本身没有更多的计算去做,相当于原子那样无法再精简。8+9就是一个表达式,表达式需要被求值

变量 variable binding

val x = e; 类型检查和取值都取决于表达式e。利用静态环境根据之前一系列绑定得出现在的e是个什么类型,创建了一个的静态环境,求值类似。另外ml里不存在assignment statement操作。上面的代码中x和e的值是绑定上了。如果再定义val x = e2就创造了新的环境。个人理解是创造了新的x和e2的绑定,网上说可以理解成栈那样。

加法运算 addition

e1+e2 类型:表达式e1 e2 必须是int类型,否则does not type-check. 求值:在静态环境里分别求值e1 e2,再求他们的和 减法运算类似。负数用~表示,-只能用于两数相减

条件 conditionals

if e1 then e2 else e3 类型:e1 需要是布尔。e2, e3 需要是同样的类型。上面整个代码块也是一个表达式,整个表达式的类型就是e2或者e3的类型。 值:根据e1求值的结果决定整个表达式的值是e2还是e3

布尔 boolean

true or false这两个写法中的一个 类型:布尔 值:它自己本身

小于比较 less-than comparison

e1 < e2 类型:如果e1, e2都在静态环境里计算出是数字类型的话则表达式的类型是布尔,否则does not type-check 值:求值e1和e2根据结果是true或者false

函数定义 function definition

不像其他语言没有类概念,没有this fun x0 (x1 : t1, …, xn : tn) = e 函数的类型表示是 t1* …* tn -> t。top-level的是在一个静态环境内,函数在另一个静态环境里,这个静态除了包含top-level的静态环境还包含x1,xn以及x0用于递归调用 类型:在不同于top-level的静态环境中检查x1, x2… e的类型就是整个函数表达式的类型。这个类型是由type-checker推导出来的。(type inference) 值:函数本身就是value,不能再被精简了。

函数调用 function call

e0(e1,...en) 类型:会检查参数的类型以及参数数量,整个表达式的参数就是之前函数定义时的 -> t 值:先求e1..en的值再函数运算后的值

pairs and other tuples

compound data 复合数据 (e1,e2) 类型:t1*t2 值:(e1,e2)本身就是一个value 可以用#1 #2来取得e1,e2的值。 pairs 可以理解为特殊的tuples,tuples可以嵌套。 ((7,true),9) 的type是(int * bool) * int

列表 lists

tuples的所有元素可以是不同类型,但是要指定元素数量; lists的所有元素类型必须一致,但是可以不指定元素数量; 类型:int list , bool list ….. [] 的类型是 ‘a list 表示任何类型 值:列表本身就是value

let expression

let b1 b2 … bn in e end 用于本地绑定。和局部变量很像,一个方法只用于另一个方法内的情况也可以这么使用。good style b1, b2, b3 …都是绑定。函数也是一种绑定~ 每个绑定的可使用范围都是在之后的绑定以及主体e中。和top-level的绑定的按顺序很像。 类型:e的type就是整个let表达式的值。 值:e的值

options

表示值的取值可以使空的或者有值。这里的空不一定值数组是空的就是表示carrying nothing.这里感觉就是1 0 两种取值。可以理解成某个容器里面有值和没值吧。 NONE 表示没值 SOME 将值转化为option,通过 valOf 取出 isSome 方法,参数是option,如果是true表示有值。 valOf 方法,参数是option, 返回值,如果是空值(NONE)则报错。下面的例子是用于求和的。

fun better_max (xs : int list) =
    if null xs
    then NONE
    else
        let val tl_ans = better_max(tl xs)
        in if isSome tl_ans andalso valOf tl_ans > hd xs
then tl_ans 
           else SOME (hd xs)
        end

expressions and operators

其他有些特殊操作符构成的表达式

andalso

e1 andalso e2 类型:e1 e2 必须都是布尔 值:整个表达式也是布尔,如果e1是false就直接中断 比if e1 then e2 else falsestyle更好。这个形式利用现有语法当然可以写,但ml提供了更简洁的方式 short-circuiting

orelse

e1 orelse e2 类似上面的只不过是或 比if e1 then true else e2

not

not e 类型: e是布尔 值:取反 如果自己实现一个方法则类似 fun not x = if x then false else true not 是方法,andalsoorelse是操作符,因为方法会对参数都进行取值,而andalsoorelse有中断操作。

no mutation

特性

在ML中,绑定过得数据无法更改,tuple和list内的值也无法改变。假设 val x = value1;x在其所属的环境中值不变。如果有新的绑定,在新环境中x就是对应的新值;在原来的环境中x还是value1。环境的链接个人感觉是不是和list 的::操作符一样,和栈一样。 ml中的列表的tl就是返回的引用,时间复杂度是常数级。

优点

  1. 不用担心一个绑定是否是是引用(aliases)。 举自己熟悉的js为例,引用类型是要小心的点,采用引用类型的时候担心其他使用引用的地方会受到影响。
  2. 节省空间 比如js中为了不造成影响会创建新的对象。redux中推荐的做法就是创造no mutation的数据;就是构造新的引用类型,不返回旧的
  3. 安全问题
class ProtectedResource {
   private Resource theResource = ...;
   private String[] allowedUsers = ...;
   public String[] getAllowedUsers() {
      return allowedUsers;
   }
   public String currentUser() { ... }
   public void useTheResource() {
      for(int i=0; i < allowedUsers.length; i++) {
         if(currentUser().equals(allowedUsers[i])) {
             ... // access allowed: use it
return; } 
} 
      throw new IllegalAccessException();
   }
} 

由于常用,java中有专门System.arraycopy来做copy数组的事情。

结尾

ps: 有一起学的小伙伴可以找我哈(^o^)/~~