zoukankan      html  css  js  c++  java
  • Coursera课程 Programming Languages, Part B 总结

    碎言碎语

    1. 很多没有写过 Lisp 程序的人都会对 Lisp 中的括号产生偏见(包括曾经第一次看到 Lisp 程序的我),事实上,括号赋予 Lisp 的是严谨的程序结构,Lisp 中的括号是极其严格的,往往既缺一不可,也不能画蛇添足。括号往往意味着表达式的计算,例如 (e)表示 e 表达式的计算,那么一旦 e 是一个确定的值,则会出现问题,(3),在 Racket 中会报错:application: not a procedure;
    2. 这门课最值得一提的就是 week 2 的作业,在 Racket 语言中实现自己的语言 MUPL(Made Up Programming Language),Racket 作为解释器去执行用自己的语言写成的代码。当然话是这么说,其实介绍作业的材料已经解决了从 0 到 1 的困难了,从定义语言的基本语法,到扩展这门语言,再到用这门语言编写函数,都有着详细的指导。
    3. Racket 作为解释器,如果要执行我们的语言,简单情况下,只需要编写一个 eval 函数,里面充满着各种判断语句来解析我们的语言(语法)。另外,我们可以在自己的语言中定义宏的写法,其实这里宏可以就是一个 Racket 函数,这个函数会返回一个 MUPL 表达式,在 eval 函数执行的时候可以很自然的替换,这不是很类似 Racket 语言中的宏吗?检测宏是否运行正常只要看它生成的代码是否正确就行了。
    4. 从类型检查谈起,并讨论了弱类型语言以及强类型语言的区别(如 C 的数组越界),并在某些方面比较了动态类型和静态类型的优缺点。在 Racket 中我们仅仅使用 list 就定义出很多数据结构,通过 car cdr 可以很容易的使用(如最基本的树,也可以实现 set 和 map),但是定义一个新的类型,既可以更快地帮助类型测试捕捉到这个错误,也提高了代码的可读性。
    5. 如果对 Part B 的内容还意犹未尽的话,还有 SICP 可以食用,前三章会看的很轻松(或许)。

    笔记

    1. 惰性求值(Lazy Evaluation)

    1. call-by-value:在调用函数时,表达式先被求值再作为参数。
    2. call-by-need:在需要的时候再计算。

    在大多数编程语言中都是第一种方式,在一些语言中我们可以用一种既巧妙又简单的方式实现第二种方式,即惰性求值(Lazy Evaluation)。

    下面举得例子并不是非常恰当,但是很直观。

    Racket 中的 if 表达式是这样的:

    (if e1 e2 e3)
    

    也就是如果 e1 为真,执行 e2 否则执行 e3。很显然它接受三个参数,那么有一个问题,如果应用的是第一种方式,e1 e2 e3 先被计算再判断 e1 真假的话,一旦 e2 表达式产生了 side effect (例如打印函数),则会导致程序的不理想输出,甚至于有时会发生无限递归。
    为了解决这个问题,e2 和 e3 在判断 e1 之前不能被计算。
    在 Racket 语言中,我们直接在 e2 和 e3 外面分别套一个 lambda表达式 即可达到这个目的。

    (define (bad-if e1 e2  e3)
      (if e1 e2 e3))
    (bad-if (= 1 2) (write 1) (write 3)) ; 13
    
    (define (new-if e1 e2 e3)
      (if e1 (e2) (e3)))
    (new-if (= 1 2) (lambda () (write 1)) (lambda () (write 3))) ; 3
    

    所以,如果我们想要延迟求值,把要延迟求值的表达式放到一个 lambda表达式 中即可,在需要用到时对函数求值即可。

    这样问题是解决了,但是显得异常麻烦,有几个参数就要写几个 lambda表达式,有没有什么简洁的方法呢?

    这个时候就要用到 宏(macro) 了。

    宏的替换发生在任何表达式的计算之前。

    (define-syntax good-if
      (syntax-rules ()
        [(new-if e1 e2 e3)
         (new-if e1 (lambda () e2) (lambda () e3))]))
    
    (define (new-if e1 e2 e3)
      (if e1 (e2) (e3)))
    
    (good-if (= 1 2) (write 1) (write 3)) ; 3
    

    虽然这个例子毫无意义,但是思想还是能体现不少的。

    惰性求值一个常见的应用就是 流(stream) 了。

    具体可参考 SICP

    2. macro

    模拟 for 循环。

    (define-syntax for
      (syntax-rules (to do)
        [(for lo to hi do body)
         (let ([l lo]
               [h hi])
           (letrec ([loop (lambda (it)
                            (cond [(= it h) body]
                                  [(< it h) (begin body (loop (+ it 1)))]))])
             (loop l)))]))
    
    (for 1 to 5 do (write "H"))
    
  • 相关阅读:
    设计模式总结——程序猿的武功秘籍(上)
    php获取分类以下的全部子类方法
    TypeError: Cannot read property &#39;style&#39; of null 错误解决
    Java抽象类
    PHP_SELF、 SCRIPT_NAME、 REQUEST_URI差别
    [WPF]使用Pack URI路径訪问二进制资源
    Android JNI编程(五)——C语言的静态内存分配、动态内存分配、动态创建数组
    Android JNI编程(四)——C语言多级指针、数组取值、从控制台输入数组
    Android JNI编程(三)——C语言指针的初步认识、指针变量、互换两个数、函数返回多个值
    Android JNI编程(二)——C语言的基本数据类型,输出函数,输入函数
  • 原文地址:https://www.cnblogs.com/ftae/p/8150574.html
Copyright © 2011-2022 走看看