学习Scheme

    技术2022-05-11  70

    今晚想学习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讲授算法设计入门了……


    最新回复(0)