• SICP学习笔记(1.3.1)


                                        SICP学习笔记(1.3.1)        
                                                        周银辉

    1,高阶函数

    从小学数数 1 2 3 4 5,所以当有一天老师告诉你,它们称之为“自然数”时,你的脸上便没有任何吃惊的表情,因为在你看来它们是那么的“自然”。同样的,如果你开始便是接触的是函数式编程(而不是大学C语言)的话,那么在这里提到高阶函数,你的表情也应该是非常淡然,而不是迷惑。
    在命令式编程语言中,我们习惯于将数、变量、对象作为函数参数与返回值;同样地,在函数式语言中,我们同样习惯于将 函数 作为 另一函数的参数或返回值(也可以是自身的参数或返回值,如果是递归的话),我们将这种“以函数为参数或返回值的函数”称为“高阶函数”或“高阶过程”。由于在函数式语言中,你所遇到的一切均是函数,(比如数字 3,它是 λfx.f (f (f x)) 这样的函数,在1.3.2讲lambda和丘奇数的时候会说道这个东西),所以,将函数作为参数或返回值则是最正常不过的事情了,它是那么的“自然”。
    更多的,关于高阶函数的定义可以参考这里:http://en.wikipedia.org/wiki/Higher-order_function

    2,Currying 

    这个应该放到1.3.2来说,但很奇怪的是,本节练习题1.33的最后一个小问却需要这个知识点,所以在这里简单提一下:Currying就是将拥有多个参数的函数化简成只有一个参数的形式。
    比如在我们的印象中,假设我们需要这样定义两个数的求和运算: (define (add a b) (+ a b)) , 这需要对add方法传入两个参数,比如 (add 2 3); 如果我们只允许add 带一个参数,那应该怎么办呢? 我们应该采用Currying这个技巧编写出下面的代码:
    (define (add a)
      (lambda (b) (+ a b)))
    此后,调用add方法就只需要传入一个参数了,比如 (((add 2) 3)
    如果你还有所疑惑的话,不妨看看 Lambda calculus 以及 Continuation 的相关知识点。或者看看我接下来会写的“SICP学习笔记1.3.2”

    3,练习1.29

    用辛普森规则求积分,比较简单,基本上是将辛普森规则翻译成Scheme代码就可以了:

    (define (sum term a next b)

      (if (> a b)

          0

          (+ (term a)

             (sum term (next a) next b))))

    (define (cube a) (* a a a))

    (define (F f a b n)

      (define h (/ (- b a) n))

      (define (y k) (f (+ a (* k h))))

      (define (term i)

        (+ (y (- (* 2 i) 2))

           (* 4 (y (- (* 2 i) 1)))

           (y (* 2 i))))

      (define (next i) (+ i 1))

      (/ (* h (sum term 1 next (/ n 2))) 3))

    我用这段代码计算了一下几个值:
    (F cube 0 1 10.0)
    (F cube 0 1 100.0)
    (F cube 0 1 1000.0)
    (F cube 0 1 1000000.0)

    运算结果:
    0.25000000000000006
    0.2500000000000001
    0.2500000000000001
    0.2499999999999972

    4,练习1.30

    先理解过程中各个参数的意思:
    sum,求和运算
    term,一个函数,(term a)就相当于数学中的F(a)
    a,求和运算中自变量的起始点
    b,求和运算中自变量的结束点
    next,步进函数,即描述自变量如何从当前值步进到下一个值

    OK,要完成这个练习,我们不妨先写一个专门针对连续整数的求和过程,也就是说,(terms a)的值为a,相当于数学中的f(x)= x ; 并且 (next n)为 n+1, 它的求和过程的尾递归版本如下:
    (define (sum a b)
      (sum-iter a b 0))

    (define (sum-iter a b c)
      (if (> a b)
          c
          (sum-iter (+ a 1) b (+ a c))))

    关于如何建立该过程,请参考 SICP学习笔记(1.2.1 ~ 1.2.2) 中的“尾递归”节

    那么按照上面的方式“依葫芦画瓢”,便可建立如下通用的求和版本:
    (define (sum term a next b)
      (if (> a b)
          0
          (+ (term a)
             (sum term (next a) next b))))

    (define (sum-iter term a next b cumulater)
      (if (> a b)
          cumulater
          (sum-iter term (next a) next b (+ (term a) cumulater))))

    5,练习1.31

    利用高阶函数求Pi(∏ )

    首先,按照求和的方法“依葫芦画瓢”,可以写出一下“求积”版本:
    (define (product term a next b)
      (product-iter term a next b 1))

    (define (product-iter term a next b cumulater)
      (if (> a b)
          cumulater
          (product-iter term (next a) next b (* (term a) cumulater))))

    第n项的分子:
    (define (den n)
      (cond ((= n 1) 2.0)
            ((even? n) (+ 2.0 n))
            (else (den (- n 1)))))

    第n项的分母:
    (define (num n)
      (cond ((even? n) (num (- n 1)))
            (else (+ n 2.0))))

    第n项,函数F(n):
    (define (F n)
      (/ (den n) (num n)))

    步进函数:
    (define (increase a) (+ a 1))

    然后就可以对pi求值了(我这里计算了前1亿项):
    (* 4.0 (product F 1 increase 100000000))

    计算结果(大概花了一分钟):
    3.1415926692944294

    6,练习1.32

    没什么好说的,直接给的答案了:

    (define (accumulate combiner null-value term a next b)

      (accumulate-iter combiner term a next b null-value))

    (define (accumulate-iter combiner term a next b result)

      (if(> a b)

         result

         (accumulate-iter combiner term (next a) next b (combiner (term a) result))))

    (define (F a) a)

    (define (increase a) (+ a 1))

    ;为验证正确性,下面两个为测试函数
    (accumulate + 0 F 1 increase 5)

    (accumulate * 1 F 1 increase 5)

    7,练习1.33

    “过滤器”在2.2.3中会提到,这里可以简单地想象成一个检测装置:对于给定值a,如果满足给定条件condition,则返回值本身,否则返回空值 null-value,所以

    ;定义过滤器,这里我将其值打印出来了,以便看到过滤过程
    (define (filter condition null-value a)
      (cond ((condition a) (begin (display a) (display "; ") a))
            (else null-value)))

    ;定义filtered-accumulate,其为练习1.32的accumulate增强版
    (define (filtered-accumulate combiner null-value term a next b filter-condition)
      (accumulate-iter combiner term a next b null-value null-value filter-condition))

    ;定义filtered-accumulate的尾递归版本
    (define (accumulate-iter combiner term a next b null-value result filter-condition)
      (if(> a b)
         result
         (accumulate-iter combiner term (next a) next b null-value
                          (combiner
                           (filter filter-condition null-value (term a)) result)
                          filter-condition)))

    ;定义素数检测函数
    (define (prime? n)
      (= n (smallest-divisor n)))

    (define (divides? a b)
      (= (remainder b a) 0))

    (define (square a)
      (* a a))

    (define (find-divisor n test-divisor)
      (cond ((> (square test-divisor) n) n)
            ((divides? test-divisor n) test-divisor)
            (else (find-divisor n (+ test-divisor 1)))))

    (define (smallest-divisor n)
      (find-divisor n 2))

    ;定义小于N并且与N互素的检测函数
    (define (gcd-n? n)
      (lambda(a)
          (and (< a n) (= (gcd a n) 1))))

    ;定义恒等函数 f(x)=x
    (define (F a) a)

    ;定义增长率
    (define (increase a) (+ a 1))

    ;计算1~100间的素数之和
    (filtered-accumulate + 0 F 1 increase 100 prime?)

    ;计算小于100并且与100互素的正整数之乘积
    (filtered-accumulate * 1 F 1 increase 100 (gcd-n? 100))

    注意到上面定义的“小于N并且与N互素的检测函数”,其用到了我们上面所说的“currying”技巧,其仅仅带有一个参数n,如果我们按照常规的方式书写:
    (define (gcd-n? n a)
      (and (< a n) (= (gcd a n) 1)))
    其带了两个参数,我们很快就会发现你无法带入到我们的“过滤器”函数中:
    (define (filter condition null-value a)
      (cond ((condition a) (begin (display a) (display "; ") a))
            (else null-value)))
    因为(condition a)是只带一个参数的。

    注:这是一篇读书笔记,所以其中的内容仅属个人理解而不代表SICP的观点,并随着理解的深入其中的内容可能会被修改




  • 相关阅读:
    10.11-10.16
    10.8-10.10
    9.26-28
    9.29css继承属性
    表单的学习
    排版
    css补充
    今天学的新内容
    新内容
    文本样式的修改
  • 原文地址:https://www.cnblogs.com/zhouyinhui/p/1576637.html
Copyright © 2020-2023  润新知