ChinaUnix.net 首页 | 博客 | Linux | 论坛 | 人才 | 培训 | 知识库 | 资料 | 读书 | 手册 | 精华 | 下载 | 沙龙 | 搜索
Linux首页 | Linux论坛 | 论坛精华 | 开源新闻 | 技术文章 | 专题专栏 | 新手指南 | 迁移方案 | 产品方案 | 开源项目 | 开源图书 | 软件下载 | 人才招聘 | Linux博客
  搜索

  产品与方案
·中科红旗全面打造现代化邮政体系
·红旗助力“网上审批服务” 推动电子政务
·红旗正版化开创呼和浩特网吧建设新起点
·红旗Linux助信息产业部邮件服务器“快跑”
·中标普华Linux 为电子政务信息化保驾护航
·中标普华Linux助力基金产业
·中标普华Office率先支持UOF标准
·中标普华邮件系统助力西藏政府信息化建设
·红旗Linux助力国库集中支付系统改革
·红旗助中信卫星 掀起GIS通信应用风暴
·红旗软件助力烟草总局 全面建设“数字烟草”
·红旗助力“信访阳光工程”打造畅通信访渠道
·红帽联合FIS发布下一代实时核心银行平台
·红旗助力金盾 打造全无忧出入境信息系统
·红旗Linux全力打造中国邮政总局名址信息库
·爱尔兰证交所从Unix迁移到红帽企业Linux
·一流的意大利银行选择使用红帽企业Linux
·PLUS Finanzservice选择使用红帽企业Linux
·红帽助力TransACT Communications 公司
·法国零售业巨头Lapeyre采用Redhat Linux
·旅游预订网站选择使用红帽企业Linux
·马哈拉施特拉邦政府的红帽解决之道
·美国联邦政府案例
·红帽为慕尼黑展览会提供现代化集群系统
·Yuba郡用开源软件和红帽产品提高了效率
·红帽企业Linux助印度理工建立高性能计算中心
·采用红帽Linux 将系统维护时间缩短了65%
·从UNIX迁移到Linux使Peñoles公司获益非浅
·Hikal公司用红帽企业Linux开展任务关键的ERP项目
·KDE3.5.4新版本发布
·芝加哥商业交易所从Unix向Linux迁移
·南方基金管理有限公司成功案例 Red Hat Linux
·广东北电通讯设备有限公司成功案例
·挪威国家石油公司从UNIX迁移到红帽Linux,成本减半
·中央电视台CCTV动画部案例 Red Hat Linux

  图书

鸟哥的Linux私房菜基础学..


Linux程序设计.第3版


Linux设备驱动开发详解


  下载
·Endian Firewall
·linux kernel(Linux 内核)
·CentOS
·Fedora Core 6
·Scientific Linux
·Slackware 11.0
·Gentoo Linux
·ubuntu-6.10-i386服务器版本
·ubuntu-6.10-amd64服务器版
·ubuntu-6.10-i386桌面版
·ubuntu-6.10-amd64桌面版
·Engarde Linux
您的位置: Linux时代 > 技术文档 > 程序开发 >

跨越边界: Lisp 之美

日期:2007-03-09 作者:Bruce Tate 来自:linux.chinaunix.net


Lisp 长久以来一直被视为伟大的编程语言之一。其漫长的发展过程(接近五十年)中引发的追随狂潮表明:这是一门非同凡响的语言。在 MIT,Lisp 在所有程序员的课程中占了举足轻重的地位。像 Paul Graham 那样的企业家们将 Lisp 卓越的生产力用作他们事业成功起步的推动力。但令其追随者懊恼万分的是,Lisp 从未成为主流编程语言。作为一名 Java™ 程序员,如果您花一点时间研究 Lisp 这座被人遗忘的黄金之城,就会发现许多能够改进编码方式的技术。

我最近第一次完成了马拉松赛跑,我发现跑步比我预想的更有价值。我跑了 26.2 英里,通过该步骤,我开始认为这是对身体非常有益的简单活动。一些语言给了我类似的感觉,如 Smalltalk 和 Lisp。对 Smalltalk 来说,引发类似感觉的是对象;Smalltalk 中的一切内容都是在处理对象和消息传递。对于 Lisp 来说,这个至为重要的步骤更为简单。这门语言完全由列表组成。但不要被这个简单的假相所欺骗。这门有着 48 年历史的语言具有难以置信的强大功能和灵活性,这是 Java 语言所不能企及的。

第一次和 Lisp 打交道时,我还是在校大学生,但这次不是很顺利。因为我拼命地想把 Lisp 编入到熟悉的过程化范例中,而不是在 Lisp 的函数结构下工作。尽管 Lisp 并不是一门严格的函数语言(因为一些特性,它不符合最严格的术语定义),但 Lisp 的许多习语和特性有着很强的函数风格。从那以后,我学会了利用列表和函数式编程。

关于本系列

在 跨越边界系列 文章中,作者 Bruce Tate 提出这样一个观点:如今的 Java 程序员可通过学习其他方法和语言得到很好的其他思路。自从 Java 技术明显成为所有开发项目的最佳选择以来,编程前景已经变化。其他的框架影响着构建 Java 框架的方式,从其他语言学到的概念可以影响您的 Java 编程。您编写的 Python(或 Ruby、Smalltalk……)代码可以改变您处理 Java 编码的方式。

本系列为您介绍与 Java 开发根本不同,但也可以直接应用于 Java 开发的编程概念和技术。在一些示例中,需要对技术进行集成来利用它。在另外一些示例中,您将能够直接应用这些概念。单独的工具不及其他语言和框架能够影响 Java 社区中的开发人员、框架甚至基本方法的思想那么重要。

本期的跨越边界 将重拾这份遗失的财富。我会带您简单地领略一下 Lisp 的基本构造,然后快速的扩展开来。您将学到 Lambda 表达式、递归和宏。这份简单的向导会让您对 Lisp 的高效性和灵活性有所理解。

入门

本文使用 GNU 的 GCL,它针对许多操作系统都有免费下载。但稍作修改,就能使用任何版本的 Common Lisp。请参见 参考资料 获取可用 Lisp 版本的详细说明。

和学习大多数其他语言一样,学习 Lisp 最好的方法就是实践。打开您的解释程序,和我一起编码。Lisp 基本上是一门编译好的语言,通过直接键入命令,就可以轻松地用它进行编程。

列表语言

基本上,Lisp 是一门关于列表的语言。Lisp 中的一切内容(从数据到组成应用程序的代码)都是列表。每个列表都由一些原子 和列表组成。数字就是原子。键入一个数字仅仅会返回该数字作为结果:


清单 1. 简单原子
				
>1
1
>a
Error: The variable A is unbound.

如果键入一个字母,解释程序会报错,如清单 1 所示。字母是变量,所以使用之前必须先为其赋值。如果想要引用一个字母或词语而不是变量,请使用引号将其括起来。在变量前加单引号告诉 Lisp 延迟对后续列表或原子进行求值,如清单 2 所示:


清单 2. 延迟求值和引用
				
>"a"
"a"
>'a
A

请注意 Lisp 把 a 大写为 A。lisp 假设您希望使用 A 作为符号,因为它没有加括号。后面会讨论赋值,但先要让列表来完成这一任务。简单地讲,Lisp 列表是加了括号并使用空格隔开的原子序列。尝试如清单 3 所示键入一个列表。这个列表是无效的,除非在列表前面加上 '。


清单 3. 键入一个简单列表
				
>(1 2 3)
Error: 1 is invalid as a function.
>'(1 2 3)
(1 2 3)

除非在列表前加上 ',否则 Lisp 会像对函数求值那样对每个列表求值。第一个原子是运算符,列表中其余的原子是参数。Lisp 有数目众多的原语函数,正如您预料的那样,其中包括许多数学函数,例如,+、* 和 sqrt(+ 1 2 3) 返回 6(* 1 2 3 4) 返回 12

操纵列表的有两类函数:构造函数选择函数。构造函数构建列表,选择函数分解列表。firstrest 是核心选择函数。first 选择函数返回列表的第一个原子,rest 选择函数返回除第一个原子外的整个列表。清单 4 显示了这两个选择函数:


清单 4. 基本 Lisp 函数
				
> (first '(lions tigers bears))
LIONS

> (rest '(lions tigers bears))
(TIGERS BEARS)

这两个选择函数都获取整个列表,返回列表的主要片断。稍后,您将了解递归如何利用这些选择函数。

如果希望构建列表而不是将其分开,就需要构造函数。与在 Java 语言中一样,构造函数构建新元素:在 Java 语言中为对象,在 Lisp 中即为列表。conslistappend 是构造函数示例。核心构造函数 cons 带有两个参数:一个原子和一个列表。cons 将该原子作为第一个元素添加到该列表。如果对 nil 调用 cons,Lisp 将 nil 作为空列表对待,并构建一个含一个元素的列表。append 连接两个列表。list 包含一个由所有参数组成的列表。清单 5 显示了这些构造函数的实际应用:


清单 5. 使用构造函数
				
> (cons 'lions '(tigers bears))
(LIONS TIGERS BEARS)

> (list 'lions 'tigers 'bears)
(LIONS TIGERS BEARS)

> (append '(lions) '(tigers bears))
(LIONS TIGERS BEARS)

consfirstrest 一起用时可以构建任何列表。listappend 运算符只是为了方便,但经常会用到它们。事实上,可以使用 consfirstrest 来构建任何列表,或返回任何列表片段。例如,要获取列表的第二或第三个元素,应该获取 rest 中的 first,或 rest 中的 rest 中的 first,如清单 6 所示。或者,若要构建包含两个或三个元素的列表,可以将 consfirstrest 一起使用,来模拟 listappend


清单 6. 构建第二个元素、第三个元素,然后模拟 list 和 append
				
>(first (rest '(1 2 3)))
2

>(first (rest (rest '(1 2 3))))
3

>(cons '1 (cons '2 nil))
(1 2)

>(cons '1 (cons '2 (cons '3 nil)))
(1 2 3)

>(cons (first '(1)) '(2 3))
(1 2 3)

这些示例也许无法引起您的兴趣,但在如此简单的原语之上构建一门简洁优美的语言,其中的原理让一些程序员激动不已。这些由列表构建的简单指令构成了递归、高阶函数,甚至是闭包和 continuation 之类高级抽象的基础。因此下面将研究高级抽象。

构建函数

可以猜到,Lisp 函数声明为列表。清单 7 构建了一个返回列表第二个元素的函数,展示了函数声明的形式:


清单 7. 构建第二个函数
				
(defun my_second (lst)
  (first (rest lst))
)

defun 是用于定义自定义函数的函数。第一个参数是函数名,第二个参数是参数列表,第三个参数是希望执行的代码。可以看出,所有 Lisp 代码都表述为列表。借助这项灵活和强大的功能,就可以像操纵其他任何数据一样操纵应用程序。稍后将看到一些示例使代码和数据之间的区别变得模糊。

Lisp 也处理条件结构,如 if 语句。格式为 (if condition_statement then_statement else_statement)。清单 8 是一个简单的 my_max 函数,用于计算两个输入变量中的最大值:


清单 8. 计算两个整数中的最大值
				
(defun my_max (x y)
  (if (> x y) x y)
)

MY_MAX
(my_max 2 5)

5
(my_max 6 1)

6

下面回顾一下到目前为止看到的内容:

  • Lisp 使用列表和原子来表示数据和程序。
  • 对列表求值时将第一个元素看作列表函数,将其他元素看作函数参数。
  • Lisp 条件语句将 true/false 表达式和代码一起使用。

递归

Lisp 提供用于迭代的编码结构,但递归是更受欢迎的列表遍历方式。使用 firstrest 组合实现递归效果很好。清单 9 中的 total 函数显示了其运行原理:


清单 9. 使用递归计算列表的总和
				
>(defun total (x)
  (if (null x)
    0
    (+ (first x) (total (rest x)))
  )
)

TOTAL
>(total '(1 5 1))

7

清单 9 中的 total 函数将列表当作单个的参数。第一个 if 语句在列表为空的情况下中断递归,返回零值。否则,该函数将第一个元素添加到列表其余部分的总和。现在应该明白如此构建 firstrest 的原因。first 能够去除列表的第一个元素,rest 简化了将尾部递归 (清单 9 中的递归类型)应用于列表其余部分的过程。

由于性能的原因,Java 语言中的递归是有限的。Lisp 提供一项称作尾部递归优化 的性能优化技术。Lisp 编译器或解释器能够将特定形式的递归翻译为迭代,从而允许以一种更为简单明快的方式来使用递归数据结构(如树结构)。

高阶函数

如果模糊了数据和代码之间的区别,Lisp 会更有意思。在本系列的前两篇文章中,介绍了 JavaScript 中的高阶函数 和 Ruby 中的闭包。这两项功能都将函数作为参数进行传递。在 Lisp 中,由于函数和列表没有任何区别,高阶函数也就非常简单。

高阶函数的最常见用法或许是 lambda 表达式,这是闭包的 Lisp 版。lambda 函数是用于将高阶函数传入 Lisp 函数的函数定义。例如,清单 10 中的 lambda 表达式计算了两个整数的和:


清单 10. Lambda 表达式
				
>(setf total '(lambda (a b) (+ a b)))
(LAMBDA (A B) (+ A B))

>total
(LAMBDA (A B) (+ A B))

>(apply total '(101 102))
203

如果使用过高阶函数或闭包,那么可能更容易理解清单 10 中的代码。第一行代码定义了一个 lambda 表达式并将其和 total 符号绑定到一起。第二行代码仅显示了这个和 total 绑定到一起的 lambda 表达式。最终,最后一个表达式对包含 (101 102) 的列表应用这个 lambda 表达式。

高阶函数提供比面向对象概念更高层次的抽象。可以用它们来更简洁清晰地表达想法。编程的至高境界就是在不牺牲可读性或性能的前提下,用更少的代码提供更强大更灵活的功能。高阶函数能实现所有这些要求。

Lisp 还有两种类型的高阶函数。其中功能最强大的可能是。宏为后面的执行定义 Lisp 对象。可以将宏看作代码模板。请参考清单 11 中的示例:


清单 11. 宏
				
>(defmacro times_two (x) (* 2 x))
TIMES_TWO

>(setf a 4)
4

>(times_two a)
8

这个示例应该分为两个阶段进行阅读。第一次赋值定义了宏 times_two。在第二个阶段(称为宏扩展)中,在对 a 求值之前,将 a 扩展为 (* 2 a)。该模板中这项延迟求值方式使宏的功能非常强大。Lisp 语言本身的许多功能都是基于宏的。

结束语

从年份上讲,Lisp 也许很陈旧,甚至语法也很陈旧。但如果稍作研究,就会发现该语言有着难以置信的强大功能,它的高阶抽象一如既往地有效,并且生产力很高。许多更为现代的语言从 Lisp 中得到借鉴,但是其中大多数语言的功能无法与 Lisp 媲美。如果 Lisp 拥有 Java 或 .NET 的一部分市场,并且大学中具备 lisp 知识的人也占有一定的比例,我们可能就会立即用它进行编码。

原文链接:http://www.ibm.com/developerworks/cn/java/j-cb02067.html

本文被浏览



 相关新闻

Java 2007:新年展望2007-03-08 16:12:28
3步骤移植Solaris到Linux Java程序属此范畴2006-12-05 09:07:34
开源企业:拜拜Mono 你好Java2006-12-01 14:03:45
Sun CEO聊Java开源:水涨船高2006-11-21 11:41:21
业界观察:Sun开源Java是否迟到了?2006-11-16 09:24:10
Sun扩大Java开源范围 众多关键年内公开2006-08-16 09:38:36
开源呼吁捐献Java代码 避免成为二等公民2005-07-06 09:15:31
JavaOne 大会今天开展,NoKia抢尽风头2005-06-27 10:48:55
BEA推出基于Java的高速芯片 对抗IBM的进攻2005-06-27 09:14:07
Sun推Java软件分享项目 仍不是真正开源2005-06-22 10:05:01


 相关评论
关于我们 | 联系方式 | 广告合作 | 诚聘英才 | 网站地图 | 免费注册

Copyright © 2001-2006 ChinaUnix.net All Rights Reserved

感谢所有关心和支持过ChinaUnix的朋友们

京ICP证041476号