zoukankan      html  css  js  c++  java
  • SCIP读书笔记(1)

    这书也算是必修吧,尤其是我这种非科班人员,还是应该抽时间尽量学习一下。大致翻过一遍,习题非常多,尽力吧。
    ##构造过程抽象
    * 为了表述认知,每种语言都提供了三种机制:基本元素;组合方式;抽象方法。
    * 前缀表示法,haskell中有学过,scheme中基本都是前缀表达,这使得表达式的可读性略微降低。
    * 声明变量:`(#define name value)`,和C中的宏声明一致,声明函数:`(#define (func ( para1,para2...) ())`;
    * 一般性求职规则是**树形累积**,其他的被称为**特殊形式**(如`define`语句)。
    * Scheme中多项式求值遵从**代换模型**,即逐步将过程调用代换为对应的过程和变量。先展开后规约的成为**正则序求值**,否则被称为**应用序求值**。在Scala中,分别对应*Call By Name* 和 *Call By Value*。和Scala一样,Lisp也是默认遵从后者。
    * 条件表达式:`cond`关键字,对应现代语言中的`switch`,`if`关键字,只能在非此即彼的情况下使用(换言之,没有`else if`)。
    * 逻辑运算符:`and`, `or` 和 `not`.
    ###练习部分:
    1.3:
    ```lisp
    (define (sumMax2 x y z)
             (if (= x (larger x y))
                 (+ x (larger y z))
                 (+ y (larger x z))))
    (define (larger a b)
                      (if (> a b) a b))
    
    (sumMax2 1 2 3)
    ```
    
    1.5 由于第二个参数是一个endless loop,如果对其做value展开,就会死掉,换句话说:如果死机就是应用序,否则是正则序。
    
    * 牛顿迭代法求平方根
    ```lisp
    (define (sqrt x)
      (define (isGoodEnough guess)
        (< (abs (- (* guess guess) x)) 0.001))
      (define (improve guess)
        (average guess (/ x guess)))
      (define (sqrtIter guess)
        (if (isGoodEnough guess)
            guess
            (improve guess)))
      (sqrtIter 1.0))
    
    (define (average x y)
      (/ (+ x y) 2.0))
    
    (define (abs x)
      ((if (> x 0) + -) x))
    ```
    1.6 由于lisp是应用序优先的,因此
    ```lisp
    new-if ( good-enough? guess x) 
                guess  
                (sqrt-iter (improve guess x) x)
    ```
    
    的计算过程是先计算 `(good-enough? guess x)`,而下一步是计算`(sqrt-iter (improve guess x))`,换句话说,递归被无条件执行了,因此程序会陷入死循环。
    
    * 过程的形式参数被称为“约束变量”,与之对应的成为“自由变量”,所谓**约束**,过程约束了形式参数。如果像上文一样把一些过程定义在某个过程内部,这个过程的形参就在所有的过程中共享,不必再次声明,这被称为“词法作用域”。有点类似类内部的私有函数。
    * 尾递归与普通递归的区别:简单来说,尾递归可以被优化成迭代,对于lisp这种没有循环语句的语言,掌握尾递归是一项必备技能。非尾递归可能造成堆栈溢出,尾递归无此后患(现代C/C++的编译器也会自动优化尾递归)。
    
    1.9 `a+b`展开为:` ++(--a+b)`,`+`运算需要再次展开,直到a=0,因此是递归计算过程;而后者展开为`(--a)+(++b)`,不需要伸展长度,因此是尾递归|迭代。
    
    1.10 (A 1 10)=A(0 (A 1 9))=A(0 (A 0 A(1 8)))..A(...A(1 1))=$2^{10}$
    
    (A 2 4)=A(1 (A 2 3))=A(1 (A 1 A(2 2)))...A(...A(2 1))=$2^{16}$
    
    (A 3 3)=A(2 A(3 2))=A(2 A(2 A(3 1)))...=$2^{16}$
    
    根据上面的展开规律:
    
    (f n)=2n;
    
    (g n)=$2^n$
    
    (h n)=$2^{2^n}=4^n$
    
    * 树形展开,即递归的展开是一棵树,这个是比较常见的情况。书中举了著名的坑爹例子”斐波那契“,这个例子是有名的不适合使用递归计算的递归式。
    $$f(n)=egin{equation}
        egin{cases}
            0, &n = 0\
            1, &n=1\
            f(n-1)+f(n-2), &n > 1\
        end{cases}
        end{equation}
    $$
    [$LaTeX$公式的命令都快不记得了,去查了半天ORZ]
    
    如果想要转为迭代,用常规命令语言的思路:
    ```c
    if(n==0)return 0;
    if(n==1)return 1;
    int pre=0;
    int result=1;
    int temp;
    while(--n>0)
    {
        temp=result;
        result=result+pre;
        pre=temp;
    }
    return result;
    ```
    显然,需要两个中间变量,循环结束的条件是次数。使用尾递归实现,因为不需要中间变量交换数据,所以所需变量少一个。
    
    ```lisp
    (define (f n)
        (f-iter 0 1 n)
    
    (define (f-iter  pre result count)
        (if (= count 0) 
            b
            (f-iter result (+pre result) (- count 1)))
    )
    ```
    你看,其实并不难,只要你会写循环…当然,尾递归要简洁很多。如果能够跳出命令式思维,直接想到尾递归写法,这函数式编程才能算入了门(好难= =)。
    
    * 换零钱的例子。其实这是一个组合算法,总的组合次数=一定用了某个部件的次数+一定没用某个部件的次数。按着这个规律进行规约,最终退化到边界情况,问题解决。使用这种典型的分治式思维,写出的代码非常简洁。著名的快速排序也是一句话:快排的结果应该是把比某元素小的快排结果放在其前面,把比该元素大的结果放在后面,这个haskell只要两行,常被用来做广告,哈哈。
    
    ```haskell
    qsort [] = []
    qsort (x:xs) = qsort (filter (< x) xs) ++ [x] ++ qsort (filter (>= x) xs)
    ```
    
    Lisp版:
    ```lisp
    define (quicksort x)
      (cond
       ((null? x) ‘())
       (else (append
       (quicksort (filter (lambda (y) (< y (car x))) (cdr x)))
       (list (car x))
       (quicksort (filter (lambda (y) (>= y (car x))) (cdr x)))))))
    ```
    注:这个是从网上找的,我的lisp水平和这篇笔记的进度一致。
    
    1.11 
    $$ f(n)=egin{equation}
            egin{cases}
            n,&n<3\
            f(n-1)+f(n-2)+f(n-3),&n ge 3\
            end{cases}
        end{equation}
    $$
    
    ```lisp
    (define (f n)
      (if (<= n 3)
          n
      (f-iter 1 2 3 n)))
    
    (define (f-iter ppre pre result count)
      (if (<= count 3)
          result
          (f-iter pre
                  result
                  (+ (* 3 ppre) (* 2 pre) result) 
                  (- count 1))))
    ```
    1.12 帕斯卡三角形,把它当成一个下三角,从二维的角度来讲:
    $$f(x,y)=egin{equation}
    egin{cases}
     1,& y=1 	ext{ or } x=y\
    f(x-1,y-1)+f(x-1,y),&x>y  	ext{ and } y!=1\
    end{cases}
    end{equation}
    $$
    
    ```lisp
    (define (f x y)
      (cond ((< x y) null)
            ((= y 1) 1)
            ((= x y) 1)
            (else (+ (f (- x 1) (- y 1))
                     (f (- x 1) y)))))
    ```
    1.13 纯数学题,这里就略过吧ORZ,我印象中上学的时候似乎证明过。
    
    下面一部分是讲复杂度的,CLRS这里讲的很明白,略过。
    
    * 求幂的题目。这题在面试题中倒是很常见,一个技巧
    $$a^n=a^{n/2}*a^{n/2}$$
    所以如果n是偶数,需要$log{n}$次,是奇数就是$log{n}+1$次。对应的代码段:
    
    ```lisp
    (define (exp a n)
      (define (even? n)
        (= (remainder n 2) 0))
      (cond (= (n 0) 1)
            ((even? n) (square (exp (/ n 2))))
            (else (* a (exp a n-1)))))
    ```
    显然,这里递归运算,下面描述对应的迭代,主要利用了公式:
    $(a^{n/2})^2=(a^2)^{n/2}$,反向分解的思路:
    $5^7=5^3*5^3*5=5^2*5*5^2*5*5$,显然,n是偶数,除以2,递归;n是奇数,减1,递归
    
    ```lisp
    (define (exp a n)
      (define (square a) (* a a))
      (define (even? n)
        (= (remainder n 2) 0))
      (define (exp-iter a n result)
        (cond ((= n 0) result)
              ((even? n) (exp-iter (square a) (/ n 2) result))
              (else (exp-iter a (- n 1) (* a result)))))
      (exp-iter a n 1))
    ```
    
    1.17 好吧,这种题很适合做面试题,定义了乘法,倍乘和减半,实际上把上面的代码中相关的代换掉就可以了。其中`even?`需要用一些技巧,不过也不困难。
    
    先到这里,这本书读的很慢…习题太多,每周抽一天时间学习吧。
    
  • 相关阅读:
    Python中数据在内存中存储汇总
    python每日一题:爬取一些代理网站获取动态ip
    python每日一题:关于反爬虫措施
    python每日一题:爬虫一些需要登录账号密码的网站
    python每日一题:爬虫某网站图书信息
    【MyBatis】MyBatis CRUD
    【MyBatis】自定义 MyBatis
    【JavaWeb】i18n 国际化
    【JavaWeb】AJAX 请求
    【JavaWeb】JSON 文件
  • 原文地址:https://www.cnblogs.com/livewithnorest/p/3439953.html
Copyright © 2011-2022 走看看