| 论坛注册| 加入收藏 | 设为首页| RSS
Google
您当前的位置:首页 > Linux频道 > Linux开发区 > 软件开发

Scheme 语言介绍

时间:2005-11-30 18:49:33  来源:  作者:
        (define HowMuchMoneyDoWeHave
          (lambda (someFriends)

           (define count
              (lambda (aFriend) (cadr aFriend)))
           (trace count)
           ; "HowMuchMoneyDoWeHave"过程的过程体
           (if (null? someFriends) ; 终止条件
               0
               ; 把它们加到一起
               (+ (count (car someFriends)) ; 这个人
                  (HowMuchMoneyDoWeHave ((cdr someFriends))) ; 其他人的
            ) ))
         ;HowMuchMoneyDoWeHave
         (trace HowMuchMoneyDoWeHave)
          ; #t

下面的跟踪展示在程序执行期间递归是如何展开的。跟踪功能不是标准的 Scheme 特征,但多数方言都提供了这种设施。

      (HowMuchMoneyDoWeHave
         '((HappyMole 3) (LittleBear 1) (LittleTiger 0)))

将打印:

        ; 递归“进入”表
            Computing (#
                       ((happymole 3) (littlebear 1) (littletiger 0)))
                 Computing (#
                            ((littlebear 1) (littletiger 0)))
                    Computing (#
                               ((littletiger 0)))
                       Computing (#
                                  ())
                  ; 终止条件满足了,
                  ; “回绕”这个递归 (做我们要的总计)
                  (# ()) --> 0
               Computing (# (littletiger 0))
                 (# (littletiger 0)) --> 0
                         (#
                                 ((littletiger 0))) --> 0
                 Computing (# (littlebear 1))
                   (# (littlebear 1)) --> 1
                 (#
                      ((littlebear 1) (littletiger 0))) --> 1
                   Computing (# (happymole 3))
                  (# (happymole 3)) --> 3
                                (#
                     ((happymole 3) (littlebear 1) (littletiger 0)))
                      --> 4
            ; 4

运气不好,沙发价值 10 个金币,他们仍不能买一个。如同预想的那样你不能太依赖 tiger。他对金钱没有什么想法。

额外特征

本节总结对 Scheme 的简要介绍。尽管多数 Scheme 的主要特征已经被覆盖了,没有空间做更多的风格讨论了。我们故意把我们的处置限制在据信是 Scheme 的本质的和可移植的那些方面。Rees 和 Clinger (1986)给出这门语言的完整定义。有些额外的标准特征,特别是在数值和输入/输出操作领域中。概念,比如引擎[Dybvig (1987),Hayes 和 Friedman (1984)],宏和语法扩展[Dybvig (1987)]没有提及;主要是因为仍然缺乏这些概念的标准实现,尽管很多方言已经提供了一些这种设施。请注意本书不意图充当参考手册的替代品。因此我们强烈建议使用你工作用的方言的参考手册,接着继续你的探索。

编程是需要实践的一种技巧,特别是对于“良好”风格的开发。读其他人写的程序是特别重要的,然而写你自己的程序和从你的错误中学习是生死攸关的。

Scheme 好象特别适合于这种业务,因为它带给用户的不只是编码解决某些问题,还有寻找特别优雅的公式表达。Friedman 评论一个良好的计算机语言为:“应当提供一个环境,程序可以在其中为手头的问题创造性生成计算模型或范例。程序员构造范例的能力不应当受到限制。我把易于构造范例称为语言的“易范例性”(paradigmicity)。有低易范例性的语言是讨厌的。...我们如何建立有高易范例性的语言呢? 我们介入一些基本概念,组合这些概念的一些方式,并且这么做的过程中我们利用了解决问题的多年经验。在现存的语言中没有那个比 Scheme 更易范例性了。它的基本概念是过程、续体、引擎、条件和赋值语句。所有东西都会合在复合和递归下,它预备了语法和语义扩展二者。... 使用这些基本概念的实验和实践的越多,新范例出现的越快。” [Friedman in: Dybvig (1987), preface]。

对风格的一些建议

Scheme 是一个高度交互式的语言,它带有鼓励试探性风格的问题解决的特征。这种属性使学习过程变得容易。新的想法可以立即实验,它担当使困难的概念非神秘化的任务要比任何“静态”的描述形式要好。表结构用来表示数据和程序二者。因为它们还强调了有层次的嵌套和递归,这鼓励了“块状”的应用程序和创造分层设计。

有一些基本规则用来写有良好结构的 Scheme 程序,多数这些规则类似于在典型的 Lisp 教科书中给出的指导方针。Abelson 等人(1985) 的书和在本书中讨论的工具箱程序可以充当有代表性的例子。

过程定义应当简要,并且它们应当面向一个单一的、定义良好的目的。任务应当尽可能的分解到一些子任务中,如果它们延伸超过了一页,把它们委派给其他过程。过程还应当围绕概念来组织,概念依次反映在正确的数据结构中。这个要求演化自分层设计的方法论,强烈建议使用它。创建、查询、选择、显示和更改过程的概念给出对通常需要那种过程的指导。

程序应当是可读的和易于理解的。这个原理对标识符命名和程序构造方式有明显暗示。标识符应当是描述性的,并且它们应当提供与所表示的概念有关的助记符。还希望你遵从分类标识符的特定格式惯例。例如,我们使用大写字母来开始工具箱过程的名字,而小写字母用作系统定义的过程的名字。Scheme 使用特殊的后缀: "?" 用于谓词(就是返回 #t 或 #f 的函数)和 "!" 用于有“副作用”的所有过程(就是说,改变对非局部变量的绑定)。因为 Scheme 是一个动态类型的语言,参数的命名应当反映它们的值。使用 aNumber 或 anAList 替代 n 或 l。尽管你可能开始时憎恨键入长名字、并可能感觉你的程序过于冗长了,额外的努力以后会得到报答的。

在你的程序中使用清晰的结构将要求某些决断力,而你将最终发展出一种你自己的风格。完成这个目标的唯一方法是阅读和评判其他人的代码,并采纳你喜欢的到你写的程序中。因为这种决定通常涉及感性,除了维持一致性之外,不能给出一般的规则。不要惧怕实验。如果你觉得增进了的理解能使你做出更好的工作,抛弃老的程序并再次开始。

递归和表结构在编程符号应用中同迭代和数组在数值应用中一样常见。尽管你需要做有意识的努力来克服所有 Pascal 或 Fortran 条件反射,你应该使用它们发挥它们的全部优势。递归是定义和处理重复和有规律结构的“自然的”和优雅的方式,并且这种结构很易于表示为表。

set! 过程可能产生“非局部”的副作用,所以它有害于一个程序的可理解性。这导致一些人完全反对它的使用并鼓吹没有任何副作用的“纯”函数式编程。在处理过程要按照复杂的状态变化来描述的情况下,这种风格经常是非常不实际的,所以作者不赞同这种推理。改变值绑定的能力是非常有用的工具,它的作用不能总是很方便的用任何其他方式完成。你应当尽可能的细心的保持这种绑定的作用域为局部,并适当的加以文档。在很多情况下,let 表达式、嵌套过程调用和递归能导致更清晰的结构。

深度嵌套的 cars 和 cdrs 经常是难于理解的,因此应当避免。处理这种深度递归结构的更加可取的方法是使用多层解释,这样 car 和 cdr 的长链就变得不需要了。例如,(GetXCoordinate (GetFirstPoint (GetBottomEdge (aTriangle)))) 比 (caar (caddr aTriangle)))更可取。使用适当的选择子(selector)函数也会使你的程序更加灵活并更易于维护。对象封装的实质性利益也应当探究,因为使用这种象征(metaphor)有助于使你的程序更加健壮和安全。

象很多其他强力特征一样,续体可能是危险的工具。尽管它们在某种程度上比“go to”语句相关的想法更加“安全”,它们的正确使用在相当大程度上要求程序员方面的自我约束。对“why”、“what”、“where”和“how”加以适当的文档也是基本的,更甚于更简单的结构。因此续体应当保守的使用,用来实现其他方式不能实现的目标。即使你不应当以它们的“原始”形式使用它们,但是应当把它们看作组合适当的高层结构的建造块(比如,非局部 escape, 协同例程 ...)。

Scheme 的交互式本质允许你在过程写完之后立即测试它们。你应当完全利用这种可能性并运行一组选择的测试个例,有效的和无效的二者都要有。

在编程风格上的多数一般指导方针也适用于 Scheme 程序。Kernighan & Plauger (1974) 和 Ledgard (1974) 给出了一个良好的总结。

程序应当适当的缩进和注释,尽管使用适当的标识符和有可能迅速的测试过程减少了注释的数量但它们仍是需要的。过程的定义通常应当开始于一个对它功能的简要解释,如果从它的名字看来不是很明显的。在复杂的应用中还应当有对类型、结构和这个标识符用途的描述。除了这些考虑之外,应当使用注释来突出一段代码重要的部分。它们应当经常是解释性的(就是说,在做“什么”)而不是纯描述性(就是说,是“如何”完成的)。缩进应当是一致的并应当强调程序的结构。因为嵌套的过程的清单有着一种不幸的倾向,它会溢出页面的右侧,在任何 scheme 缩进之下,都经常需要行使某些调整,在一致性和美观性之间作出权衡。

所有信息都应当尽可能的模块化和局部化。全局变量应当非常保守的使用! Scheme 支持局部变量和局部过程,你应当使用这些设施来把信息装载到正确的模块中。例如,任何只在另一个过程内部调用的过程都应当嵌套到那个过程中。作为它的逻辑上的结论,这导致了面向对象结构,尽管有些事例中过程是简单的,在其中使用与面向对象有关的额外的“脚手架”是不正当的。

因为 Scheme 用作讨论很多重要的编程象征的一个工具,本书后面的章节将提供给出近一步提示和察看这些建议应用的充分机会。

总结和看法

Lisp 是围绕符号操纵的想法建造的。原子和表是它的基本结构建造块。每个对象要么是一个表要么是一个原子。表可以递归的定义,所以可以有表的表的表 ...,形成任意复杂的树结构,表和原子可以被求值为要么数据要么是所谓的 lambda 表达式。事实上,Lisp 解释器将求值它遇到的所有东西,除非被显式的引用了。

Lisp 是一个应用式和基于表达式的语言,这意味着解释的主体单元是圆括号表达式。“纯” Lisp 禁止“副作用”。这个限制经常被证明是过分限制、并且实际语言在很大程度上屏弃了它。所以 Lisp 通常不能被当作函数式语言。需要介入赋值的正确决断必须处理好模块化的问题,这超出了本文的范围。Abelson 等人(1985) 的书的第三章是详细分析的好来源。Lisp 表达式可以引用其他表达式,要么直接的要么通过把某种函数应用到参数。每个对象,包括函数,可以动态的构造和操纵;所以我们可以有返回函数的函数,改变函数定义的函数,等等。存储管理是自动进行的。对对象没有类型限制,但程序员可以通过选择适当的标适符来细心的介入(数据)结构。尽管为变量声明关联上数据类型通常被认可为编译型编程语言的重要特征,它在解释性环境没有多大用处。它的主要用处在于使编译器可以优化存储和执行,并对程序做一致性检查。解释器动态的建立数据结构,所有优化并不重要。自动的一致性检查可能使需要的,但是因为程序员在程序失败的时候有权访问所有绑定的来源和值,错误预防通常不象编译环境中那么关键。它可以被高级的错误检查和更正特征所取代。当然,效验和优化一个已经稳定了的程序仍使非常需要的。

来顶一下
近回首页
返回首页
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表
相关文章
    无相关信息
栏目更新
栏目热门