今晚想学习Scheme。

总算把Udi Manber 大师的《Introduction to Algorithms: A Creative Approach》的数学归纳法看完了,说来丢脸……,加起来看5个小时※如果归纳法在组合算法中真有如此神奇的话,那么Scheme所代表的函数式语言,把程序定义为都是一群只需要通过Evaluator就可以得到结果的list,那么算法就真的简单了。可惜,所谓递归不如非递归程序的观点在我的头脑中根深蒂固,唉,所谓的函数调用从来只是为了简化程序做模块而已。

言归正传。

Scheme的历史就省略了,反正它只认得一种数据结构:list。

关于数据结构:Scheme中的数据类型比较简单,写什么就是什么,字符变量都以#/形式做前缀。此外还有bool,number,character,string,vector,dotted-pair,,list,,procedure,form数据类型。Scheme默认的data是lexical常量,为了指定一个symbol变量的引用,需要使用' 做前缀或者使用带谓词的表(quote symbol-x)。

关于控制结构:Scheme提供类似C的if-(else),cond,case,when,unless的分支选择结构(没有循环结构?)。注意使用if时,else分支是隐含在if分支之后的,一般进行入口检查使用when就好了。

关于程序块:Scheme中的程序块称为form,也是一种可以evaluate的“数据”。过程procedure也是可以evaluate的,不同于pascal中无返回值的procedure。procedure使用lambda form来定义的。实际上,

The values bound to lexical variables can be procedures:

(let ((cons (lambda (x y) (+ x y))))
   (cons 1 2))
 =>  3

(用一个lambda form来给cons symbol赋值:(※#◎~~)

关于程序结构与变量作用域:定义一个symbol变量使用谓词define定义global变量,使用谓词let定义local变量,可以嵌套定义(value-bind)。特别的是local变量,谓词let的对参数symbol的绑定作用域在let form程序快中,每次let只查询一次。

The local variable initializations -- x to 1; y to 2; z to 3 -- are not considered part of the let body. Therefore, a reference to x in the initialization will refer to the global, not the local x:

(let ((x 1)
       (y x))
   (+ x y))
 =>  21

This is because x is bound to 1, and y is bound to the global x, which is 20.

The x in y's initialization refers to the x just above. The example is entirely equivalent to -- and is in fact intended to be a convenient abbreviation for -- the following program with nested lets:

(let ((x 1))
   (let ((y x))
     (+ x y)))
 =>  2

Scheme本身是一个evaluator(评估器)(类似堆栈计算机?),递归程序设计是它的特长。变量的定义和作用域在scheme中的一个例子如下:

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

The lexical variables introduced by a letrec are visible not only in the letrec-body but also within all the initializations. letrec is thus tailor-made for defining recursive and mutually recursive local procedures.

(即letrec相当于static local,let while record )

但letrec的用途远不在此,而是用于循环(注意到一般的控制结构谓词中没有while):

recursive procedure defined using letrec can describe loops. Let's say we want to display a countdown from 10:

(letrec ((countdown (lambda (i)
                       (if (= i 0) 'liftoff
                           (begin
                             (display i)
                             (newline)
                             (countdown (- i 1)))))))
   (countdown 10))

This outputs on the console the numbers 10 down to 1, and returns the result liftoff.

更方便的做法是命名这样一个循环,令人费解的是,此时let完全是一个macro(宏),而且在宏中的变量似乎能够递归替换,具有了letrec的功效。

Scheme allows a variant of let called named let to write this kind of loop more compactly:

(let countdown ((i 10))
   (if (= i 0) 'liftoff
       (begin
         (display i)
         (newline)
         (countdown (- i 1)))))

Note the presence of a variable identifying the loop immediately after the let. This program is equivalent to the one written with letrec. You may consider the named let to be a macro (chap 8) expanding to the letrec form.

--------------------------------------------------下文中提到一个概念“(tail-recursive 尾递归”------------to be continued...

 

Udi Manber,国际算法大师,在线信息搜索引擎的先驱,现Amazon.com的副总裁兼首席算法师(CAO),“A Creative Approach”提出一种用数学归纳法来设计递归的组合算法程序的新方法。说到挖掘recurse 在程序设计中的潜力,其实,MIT早就用scheme讲授算法设计入门了……