很早前就想学一门脚本语言来玩玩,看了一段时间的python,可是没有继续好好的学下去,导致现在python基
本不会。暑假的时候看到王垠的博客写了一篇文章,推荐了Scheme语言和SICP这本书,上网搜了下后面这本书,发现评论非常好,评价非常高,可以和TAOCP相比了。就打算看看这本神书,顺便做做后面的习题,网上很多大神已经写过题解,不过都是Scheme语言写的,所以就打算先学学Scheme语言。暑假看了《The Little Scheme》和《teach yourself scheme in fixnum days》的一部分,前面一本书是以Scheme语言为基础,主讲递归的思想(个人觉得讲的非常好,不过后面的章节需要一定的scheme语言基础)
后面这本书就是一本介绍scheme的书,比较薄,不过作为入门书还是可以的(用的scheme版本是R5RS)。
t-y Scheme里面首先也是讲数据类型 有boolean number string real等。在scheme里面最基本的东西就属
括号了,每一个过程都是用括号来表示,所以scheme的程序会有非常多的括号。而且表达式是前缀表达式,形如(+ 3 2)计算3+2的值,字符使用#\后面加上相应的字母来表示的,其他的和C语言差不多,
第三章讲述的是过程,类似于C语言中的函数,下面是一个例子


  1. (define area ;定义过程 名字叫做area
  2. (lambda (length breadth) ;两个参数 分别是length 和breadth
  3. (* length breadth) ;计算并返回
  4. )
  5. )
  6. (area 4 6) ;用area过程求值 结果为24=4*6


过程可以用匿名过程,就是没有define那一行,其他地方都一样
第四章讲述的是各种判断,if、when等


  • (define p 80)

  • (if (> p 70)
  • ‘safe ;如果p > 70
  • ‘unsafe ;else
  • )


  • ;unless 貌似是不会循环的 ,我试验的结果是只运行了一次


  • (unless (> p 90)

  • ; (begin
  • (set! p (+ p 1)) ;
  • (display p)
  • ; )
  • )


  • when和unless类似,另外我们可以用过程来写递归或者循环,这里面的递归貌似都是尾递归的。不过循环还有
    一种表达方式就是do表达式,语法如下
    (do ((var1 val1 update1)…)(test res …) exp …)
    ;说明:类似于C语言的for循环。先将val1赋值给var1,…之后循环开始,在每次循环的开始,先执行表达式
    test,
    ;如果返回布尔值真,则循环终止,并返回结果res,如果表达式test返回#f,则运行表达式exp…,之后依次
    ;用update1…的值来为变量var1…重新赋值
    例子如下:


  • (define do-test ;定义过程

  • (lambda (n) ;一个参数 为n
  • (do ((sum 0 (+ sum i)) (i 1 (+ i 1)) ) ;sum 和i都赋初值,然后每次循环分别用 (+ sum i)和 (+ i 1)来改变
  • ((> i n) sum) ;如果i > n则推出循环 返回sum
  • (display sum) ;如果i <= n 则循环
  • )
  • )
  • )


  • 第五章讲的是变量的作用域,一般定义的是全局变量,在函数里面定义的只能在函数里面用,而且会屏蔽全局
    同名变量,可以用let和let来确定一个变量的作用域,let里面局部变量赋初值的时候不能用let里面定义的
    变量,但是let
    里面定义的局部变量可以用let*前面已经定义的局部变量来赋初值。例子如下


  • (define x 20)

  • (let (
  • (x 2)
  • (y x) ;let:a reference to x in the initialization will refer to the global,not the local x
  • )
  • (+ x y) ;结果是20+2
  • )
  • (let* (
  • (x 1)
  • (z 2)
  • (y x)
  • )
  • (+ x y) ;结果是1+1
  • )


  • 第六章里面讲了多个函数相互调用的递归和letrec,letrec和let,let的功能类似,不过let里面的赋初值
    只能用前面已经出现过的局部变量,但是不能用没有出现过的,letrec却可以用后面才出现的局部变量和局部
    过程。例子如下:


  • (letrec ((local-even? (lambda (n) (if (= n 0) #t (local-odd? (- n 1)))))

  • ;如果用let* 那么上面的local-odd?过程就会是未定义的
  • (local-odd? (lambda (n) (if (= n 0) #f (local-even? (- n 1))))))
  • (list (local-even? 23) (local-odd? 23)))


  • 这一章还讲了一个用来翻转list的过程,不过由于R6RS中已经不能用set-cdr!了,所以只能用R5RS来实现,过
    程如下:


  • (define reverse!

  • (lambda (s)
  • (let loop ((s s) (r ‘())) ;定义s 和r 并赋初值
  • (if (null? s) r
  • (let ((d (cdr s))) ;定义d 并赋初值
  • (set-cdr! s r)
  • (loop d s)
  • )
  • )
  • )
  • )
  • )


  • 还有for-each 和 map两个东西,例子如下


  • (for-each display

  • (list “one” “two” “buckle my shoe”) ;输出onetwobuckle my shoe
  • )
  • (display (map cons ‘(1 2 3 4) ‘(10 20 30 ()))) ;输出((1.10) (2.20) (3.30) (4));注意这里连接的两部分元素必须一样多


  • 第七章讲述了文件的基本操作,默认打开的是控制台,可以用open-input-file filename来打开输入文件,
    open-output-file filename来打开输出文件,最后一定要关闭,不然操作的数据不会对文件进行相应的操作(
    特指写功能),可以用call-with-input-file call-with-output-file来代替,这样的话可以不需要自己关
    闭文件,处理完当前的任务就会自动关闭相应的文件,如果用open-output-file打开一个已经存在的文件的时
    候,不加其他参数的话,是会报错的,如果想打开已经存在的文件可以加参数 #:extis ‘update/‘truncate
    update的话是直接在原文件上从头开始修改,而truncate则是删掉旧文件,重新建一个新文件。例子如下


  • (define i (open-input-file “hello.txt”)) ;打开文件

  • (read-char i) ;读取一个字符
  • (define j (read i)) ;定义j为读取剩下的所有字符的一个字符串
  • (define o (open-output-file “greeting.txt” #:exists ‘update)) ;以update方式打开一个文件
  • (display “hello” o) ;输出hello
  • (write-char #\space o) ;输出空格
  • (display ‘world o) ;输出world
  • (newline o) ;输出空行

  • 如果greeting.txt文件里面的文字原来是”abcdefthijklmn”的话,那么上述操作会使得文件的内容变成”hello
    worldlmn”,但是如果用truncate方式打开的话,那么就会变成”hello world”

    顺便把The Little Scheme的commandment贴在这

    First: When recurring on a list of atoms, lat, ask two questions about it: (null? lat) and else.
    When recurring on a number, n, ask two questions about it: (zero? n) and else.
    When recurring on a list of S-expressions, l, ask three questions about it: (null? l), (atom? (car l)), and else.

    Second: Use cons to build lists.

    Third: When building a list, describe the first typical element, and then cons it onto the natural recursion.

    Fourth: Always change at least one argument while recurring .When recurring on a list of atoms, lat, use(cdr lat). When recurring on a number, n, user (sub1 n). And when recurring on a list of S-expressions, l, use (car l) and (cdr l) if neither (null? l) nor (atom? (car l)) are true.
    It must be changed to be closer to termination. The changing argument must be tested in the termination condition:
    when using cdr, test termination with null? and
    when using sub1, test termination with zero?.

    Fifth: When building a value with + , always use 0 for the value of the terminating line, for adding 0 does not change the value of an addition.
    When building a value with x[multiply] , always use 1 for the value of the terminating line, for multiplying by 1 does not change the value of a multiplication.
    Sixth: Simplify only after the function is correct.

    Seventh: Recur on the subparts that are of the smae nature:
    On the sublists of a list.
    On the subexpressions of an arithmetic expression.

    Eighth: Use help functions to abstract from representations.

    Ninth: Abstract common patterns with a new function.

    Tenth: Build functions to collect more than one value at a time.
    这书断断续续的看了一段时间,打算现在开始边看t-y-scheme边看SICP,然后把SICP的每一章课后习题都做了
    ,也好督促自己好好学习。

    Comments