<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
  <channel>
    <title>let focus = 'computing' in where</title>
    <description>“有些人活着，他却已经死了；有些人死了，他却还活着。”
——一直以来，我深深地被这句话所震撼。所以，我的理想事实上也很简单——只要活着就好，仅此而已。</description>
    <link>http://lichray.javaeye.com</link>
    <language>UTF-8</language>
    <copyright>Copyright 2003-2008, JavaEye.com</copyright>
    <docs>http://blogs.law.harvard.edu/tech/rss</docs>
    <generator>JavaEye - 做最棒的软件开发交流社区</generator>
      <item>
        <title>递归下降语法分析详解</title>
        <author>Lich_Ray</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://lichray.javaeye.com">Lich_Ray</a>&nbsp;
          链接：<a href="http://lichray.javaeye.com/blog/155321" style="color:red;">http://lichray.javaeye.com/blog/155321</a>&nbsp;
          发表时间: 2008年01月12日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <div class="quote_title">引用</div><div class="quote_div">本文以 lichray 设计的 S-dict(t) 配置文件解析器为例，简单介绍了词法分析器的原理，详细讲述了递归下降语法分析器手工构造方法。因为该项目本身已经完成，故此本文拥有一个实际可用的例子，是不可多得的入门教程。</div><br /><div class="quote_title">引用</div><div class="quote_div">T1 大人说过，技术的迅速贬值是十分残酷的，比如大部分的手工优化代码，早已被编译器们代劳。这篇文章中要说的递归下降语法分析方法也是严重贬值了的技术之一。不过我认为，在享受着别人构造的自动化工具同时，知道其原理还是很重要的。一个典型的例子就是正则表达式——大家都会用，能保证写对的人也很多，但看了专家们的解答后都自愧不如——原因很简单：你会写正则表达式的编译器吗？<br />不过这篇文章并不是在教你怎样写 yayacc，只是希望你能从中体会到工具的思想并能更好地组织头脑中的 BNF 产生式。当然了，用这种方法手工构造一个代码语法高亮程序也是个不错的想法。</div><br /><br />很多人都是从《C 编程语言》这本书上听说递归下降这种优美的语法分析器手工构造方法的，但这“很多人”中的很多人事实上没有看懂或者是看懂了就是构造不出自己的语法分析器。我三个月前完成了自己设计的 S-dict(t) 解析器，找到一个来理清这里面思路的好机会。<br /><br /><span style="font-size: medium"><strong>S-dict(t) 简介</strong></span><br />这是作者设计的一直配置文件和数据交换格式，语法类似 Scheme，支持多种数据类型甚至包括迭代器。具体的示例文件太长，在附件中可获得。它的解析器相当于只把编程语言的解析过程做到语法分析树构造这一步，天然的例子。<br /><pre name="code" class="java">
main ::= {tree}
tree ::= '(' id leaf ')'
leaf ::= exps | main
exps ::= [{exp}]
exp  ::= id | bool | num | str | arr | low
id   ::  [^#\.\d\(\)\[\]\{\}'"`,;+-][^\s\(\)\[\]\{\}'"`,;]*
bool ::  #t|#f|#T|#F
num  ::  [+-]?[0-9]*\.?[0-9]+([eE][+-]?[0-9]+)?
str  ::  '\'' [{chr}] '\'' | '"' [{chr}] '"'
low  ::  #[^\s\(\)\[\]\{\}'"`,;]*
chr  ::= any-Unicode-character-except-"-or-\-or-control-character | 
		\" | \\ | \0 | \b | \7 | \f | \n | \r | \t | \v | 
		\x two-hex-digits | \u four-hex-digits
number
arr  ::= '[' arrc ']'
arrc ::= [{exp}]
comt ::  ^\s*#.*$
</pre><br />以上它的完整的 E-BNF 产生式，是实现解析器的根据，在下面两章会被用到。<br /><br /><span style="font-size: medium"><strong>词法分析：到底是什么</strong></span><br />《现代编译原理》这本书上对词法分析的介绍尤为生动，生动的结果就是给人一种错觉：词法分析需要完整地解析正则表达式，连接有穷自动机，所以手工实现，难度不亚于给自动机作数学注明。但那只是语言学的理论分析结果。在实际应用中，如果有什么语言的词法分析需要严格地完全连接非确定有穷自动机，那只能说明这个语言设计地很“令人困惑”。<br />词法分析器只不过是这样的一个程序：你给它要分析的程序源代码，它返回一个数组，数组中的每一项都是正确分割了的词法元素。比如对于一段 S-dict(t) 代码：<br />(i (name 'lichray')<br /> (age 13))<br />它应该返回 ['(', 'i', '(', 'name', "'lichray'", ')', '(', 'age', 13, ')', ')']。当然了，这种做法是比较简易的，通用的做法应当是给每个词法元素一个数据类型，然后返回相应的对象/实例/结构。构造它，根据词法元素表照抄即可。<br /><pre name="code" class="java">
## 这些全是正则表达式
id   ::  [^#\.\d\(\)\[\]\{\}'"`,;+-][^\s\(\)\[\]\{\}'"`,;]*
bool ::  #t|#f|#T|#F
num  ::  [+-]?[0-9]*\.?[0-9]+([eE][+-]?[0-9]+)?
str  ::  '\'' [{chr}] '\'' | '"' [{chr}] '"'
low  ::  #[^\s\(\)\[\]\{\}'"`,;]*
main ::  \(
end  ::  \)
arr ::  \[
narr ::  \]
</pre><br />解析字符串的思路大致是这样：对每个词法元素写一个函数，以一个主函数完成词法元素的判断过程，并调用相应的函数解析。每完成一个元素的解析，就返回拆解下来的结果和字符串的剩余部分，将这些数据，结果、剩余字符串和行号返回给主函数继续。<br /><pre name="code" class="javascript">
// 这个函数就可以完成这样的工作
// ssub(string, pattern) 同时返回 pattern 匹配 string 后的结果和余下的字符串
function ssub (s, p) {
	var ss = p.exec(s)[0]
	return [ss, s.slice(ss.length)]
}
// 弹出下一个单词
function word (s) {
	return ssub(s, /^[^\s\(\)\[\]\{\}'"`,;]+/)
}
// 弹出下一个“东西”，仅用于报错
function gew (s) {
	return /^[^\s\(\)\[\]'"]+/.exec(s)[0]
}
// 还真的报错了
function error (m, ln) {
	throw Error (m+" in line "+ln)
}
</pre><br /><pre name="code" class="javascript">
	// 先给出判断语法元素的函数，它们的参数是剩余的字符串
	// 剩余的字符串没有了，整个代码也到头了
	function isEOF (c) { return c == "" }
	function beLine (c) { return c[0] == '\n' }
	function beSpase (c) { return /\s/.test(c[0]) }
	function beId (c) { return /[^#\.\d\(\)\[\]\{\}'"`,;+-]/.test(c[0]) }
	function beBool (c) { return c[0] == '#' }
	function beNum (c) { return /[-+\d\.]/.test(c[0]) }
	function beStr (c) { return c[0] == '\'' || c[0] == '\"' }
	function beMain (c) { return c[0] == '(' }
	function beEnd (c) { return c[0] == ')' }
	function beArr (c) { return c[0] == '[' }
	function beNarr (c) { return c[0] == ']' }
</pre><br />似乎很繁琐。真的吗？可别忘了我整个程序近400行代码可只写了8行注释哦~`<br />注意：事实上，S-dict(t) 解析器并没有使用独立的词法分析器，而是把词法分析和语法分析同时完成了，而且本文并非以讲解词法分析为主。所以下面的代码虽然放在源代码文件中时几乎实际可运行，但没有被实际采用。<br /><pre name="code" class="javascript">
/* 基本函数 */
// 连接项目与列表
function cons (o, l) {
	return [o].concat(l)
}
// 取列表除去第一项后余下的部分
function cdr (l) {
	return l.slice(1)
}
// 消除左侧除换行之外的空白
function strim (s) {
	return s.replace(/^\s+?(?=\n)?/,'')
}

/* 词法分析器 */
// 标准的正则尾递归优化写法。尽管 JavaScript 不支持优化，但不失为一种很好的组织程序的手段
// 使用时调用 slex(string, [], 1)，参数 t 用来暂存分析数组
function slex (s, t, ln) {
	if (isEOF(s)) return t.reverse()
	var tmp = []
	// 用 ln 参数保存行数
	if (beLine(s)) return slex(cdr(s), ln+1)
	if (beSpase(s)) return slex(strim(s), ln)

	else if (beBool(s)) tmp = bool(s, ln)
	else if (beNum(s)) tmp = num(s, ln)
	else if (beId(s)) tmp = id(s, ln)
	else if (beStr(s)) tmp = str(s, ln)
	else if (beMain(s)) tmp = main(s, ln)
	else if (beEnd(s)) tmp = end(s, ln)
	else if (beArr(s)) tmp = arr(s, ln)
	else if (beNarr(s)) tmp = narr(s, ln)
	else error("Unknown Value: "+gew(s), ln)

	return slex(tmp[1], cons(tmp[0], t), tmp[2])
}

/* 所有的词法元素的解开过程 */
// 每个函数返回的第三个值是新的行号
// 此处省略，详见附件源代码 179-222 行
// 源代码中 arr、narr、main、end 几个函数都是语法分析的版本，
// 以 arr 举例说明这一系列函数的共性
function arr (s, ln) {
	return ['[', cdr(s), ln]
}
// 其他的函数只不过是使用了 ssub() 函数通过正则表达式解析了而已。
</pre><br />这里注意一下：为什么没有 low 词法元素的处理函数？在源代码中也可以看到，因为 low 和 bool 共用同一个起始字符，所以解析函数也被写到了一起。最后一章将会解释这样做的意义。<br /><br /><span style="font-size: medium"><strong>语法分析：词法分析立体版</strong></span><br /><br /><strong>语法分析的目的</strong><br />无论是生成抽象语法树（AST）还是算符优先栈还是别的什么数据结构，我们发现，最终在根据分析结果执行代码时，其实都是在做一个树形过程，都需要逻辑上的一个立体的数据结构。语法分析的，就是通过获取平面的词法分析结果，根据语法结构描述（比如 BNF）输出立体的数据结构。在 S-dict(t) 这个例子中，我们选择的数据结构是 JavaScript 对象构成的树。例如代码<br />(i (name 'lichray')<br /> (age 13))<br />的分析结果应该是：<br />{i: {name: 'lichray', age:13}}<br />限制是：一个树枝上如果有叶子或其他树枝，整个树枝的下属必须全部是叶子或树枝而不能是数据；数据只能出现在叶子上。<br /><br /><strong>E-BNF 和 BNF</strong><br />除了知道语法分析的“来龙去脉”，还需要一样描述语法语法结构的形式语言。现在的教科书上所教授的一般是扩展的巴菲斯-劳尔范式(E-BNF)，但是事实，E-BNF 相对于 BNF，存在一个很有意思的问题：那就是自由度过大。除非按照教科书上的方法消除左递归，否则往往很难手工构造。事实上，我上文中给出的 E-BNF 也并非一开始就是那样，<br /><pre name="code" class="java">
main ::= {tree}
tree ::= '(' id leaf ')'
leaf ::= exps | main
</pre><br />这三句很明显可以被非常直接地合并成<br /><pre name="code" class="java">
tree ::= '(' id exps | {tree} ')'
</pre><br />一句。但这样你就不得不把构建分析表的工作交给 yacc 之类的工具了，因为你没有写出全部的语法元素。<br />而 BNF 可以强迫你写出大部分需要递归描述的语法元素，并且可以直接地指定语法的结合方向。S-dict(t) 的 BNF 如下：<br /><pre name="code" class="java">
// e 代表为空，对应的希腊字母
// 当然也可以消除 e，把为空表现在上级产生式中
main ::= tree | tree main
tree ::= '(' id leaf ')'
leaf ::= exps | main
exps ::= e | exp | exp exps
exp  ::= id | bool | num | str | arr | low
arr  ::= '[' arrc ']'
arrc ::= e | exps
</pre><br />会心一笑：我的 E-BNF 为什么写成了那个怪样子，其实就是从 BNF 逐句转来的。<br />知道了语法元素的递归方向，不需要消除递归，只需把每个元素的解析过程表示为函数，把要执行的全局代码插入函数体，然后把 BNF 的逻辑直接而机械得转为函数间的递归调用，语法分析器就写出来了。BNF 中只有选择和递归两种逻辑，下面是对它们的编码示例。<br /><pre name="code" class="javascript">
// 参数 ls 是词法分析结果
function sdict (ls) {
	var index = ["~"]  //对象树节点名的线型访问栈
	var root = {}      //全局的对象树
	var stack = [root] //对象树节点的线型访问栈

	// 对照 BNF，不难发现解开递归的技巧：
		/* 把对起始符和终结符的处理写在函数中，递归部分一直向下推迟，
		   直到 exps 这个产生式被终结时再调用回 main() */
	// main ::= tree | tree main
	function main (ls, ln) {
		if (ls == false)
			if (!canPop(stack))
				return
			else error("Lack of end quotes", ln)
		else if (beMain(ls[0]))
			tree(cdr(ls), ln)
		else if (beEnd(ls[0]))
			if (canPop(stack)) {
				// 语法分析 main 结束就弹出一个数据栈，一条树枝结束了
				stack.pop()
				main(cdr(ls), ln)
			}
			else error("To many end quotes", ln)
		else error("Wrong Syntax: "+ls[0], ln)
	}

	function tree (ls, ln) {
		// 这是一个一步推导产生式的例子
		// tree ::= '(' id leaf ')'
		if (beId(ls[0])) {
			var tmp = id(ls[0])
			// 这些就是插入的全局数据结构更新代码，向栈中增加索引
			add_index(stack, tmp[0])
			leaf(tmp[1], ln)
		}
		else error("Not an Id: "+ls[0])
	}

	/* 详细参见源文件74-143行，把每个函数处理空白的代码去掉，
	   参数s替换为 ls，函数体内替换为 ls[0] 就是独立语法分析的版本。 */
}
</pre><br /><br /><span style="font-size: medium"><strong>合并词法分析和语法分析</strong></span><br /><strong>简单的优化</strong><br />把所有的小函数全部作内联，展开代码到被调用的地方。这样一来，判断起始符号的函数就可以全部消灭（顺便提一句，使用静态多态类型的纯函数式编程语言，如 Haskell、Ocaml、ML，写的语法分析器自动生成器不需要这种优化，因为语言本身就已经帮你做了）。当然了，作为应用“优化”手段的一般代价，就是代码看不懂了。<br /><br /><strong>一趟分析</strong><br />S-dict(t) 的解析器事实上使用了一趟分析的技术，把词法分析和语法分析合并到一起完成，不使用词法分析生成的中间数组，提高解析效率。方法是把语法分析提取 ls[0] （即当前元素）的过程扩写为从 s （剩余的源代码）中弹出下一个词法元素的过程。实例代码就是源代码中的 sdict() 函数，就不列在这儿了。<br /><br /><span style="font-size: medium"><strong>引发的联想</strong></span><br />从上面三章我们不难看出，词法分析、语法分析、一趟分析其实都是一个非常机械的过程，完全可以用工具自动生成代码。词法分析器生成器是最简单的，可以直接生成全部代码，像 flex 做的那样，连同逻辑一起硬编码；也可以像 lex 那样，提供固定的解析代码，只生成非确定有穷自动机的分析表。语法分析要稍复杂一点，问题在于如何判断什么时候该规约（确定下面调用哪一个分析函数）什么时候该移进（更新分析结果，继续向下递归）。这需要判断终结符和产生式之间的关系。最简单的判断方法是 LL(1)，也就是说统一只向下查看一个字符。这也就是我们这个例子中使用的方法。但这种方法较容易产生歧义，需要使用者自己修正解析代码。这就是 S-dict(t) 中 bool 和 low 的解析写成了一个函数的原因。antlr 就是用了 LL(1) 分析，不过它带有连接词法分析和语法分析的能力。yacc 使用的是 LR() 分析，一种带有回溯能力的强大的自动分析方法，克努特天才的创造。也许以后会有机会给大家讲到这个。
          <br/>
          <span style="color:red;">
            <a href="http://lichray.javaeye.com/blog/155321#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Sat, 12 Jan 2008 22:08:31 +0800</pubDate>
        <link>http://lichray.javaeye.com/blog/155321</link>
        <guid>http://lichray.javaeye.com/blog/155321</guid>
      </item>
      <item>
        <title>消息传递：从风格到机制</title>
        <author>Lich_Ray</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://lichray.javaeye.com">Lich_Ray</a>&nbsp;
          链接：<a href="http://lichray.javaeye.com/blog/113907" style="color:red;">http://lichray.javaeye.com/blog/113907</a>&nbsp;
          发表时间: 2007年08月18日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <div class="quote_title">引用</div><div class="quote_div">这是最终确定的 JavaScript 基于消息传递编程风格的文章“OOP 诡异教程（上）”的下篇，它的 Python 改写版本就是 <a href="http://lichray.javaeye.com/topic/88435" target="_blank">尝试用Python实现消息传递编程风格</a>。原文地址：(豆瓣：<a href="http://www.douban.com/group/topic/1669427/" target="_blank">http://www.douban.com/group/topic/1669427/</a> 博客：<a href="http://let-in.blogspot.com/2007/06/oop.html" target="_blank">http://let-in.blogspot.com/2007/06/oop.html</a>)。原来的想法是以风格开头，谈到 JavaScript 的内部机制，但作者 lichray 迟迟没有动键盘，认为不如利用已有的风格做一套机制出来，这样可能更有意义。于是，就有了这个更加“诡异”的下篇，展示了一个更加“诡异”的招数。<br /></div><br /><br /><div class="quote_title">引用</div><div class="quote_div">这篇文章的宗旨是利用我们仅有的“宾谓”语法构造出完整的一套面向对象机制，所以更多代码在更多的时候是不应在实际工作中使用的（也算一种元语言抽象），所以类似效率、代码风格之类的问题反对回帖质疑。</div><br /><br /><strong><span style="font-size: 12pt">四. 扩展的实现</span></strong><br /><a href="http://www.javaeye.com/topic/88435" target="_blank">上篇</a>最后给出了一个“看上去很美”的基于消息传递的编程风格，比如构造一个 People 类的代码类似：<br /><pre name="code" class="javascript">
function People () {
	var money = 0
	function setMoney (dollars) {
		money = dollars
	}
	function pay (dollars) {
		money -= dollars
	}
	return (function (verb) {
		return eval(verb)
	})
}
</pre><br />有了这样的语法我们就可以描述不少句子了。但是存在一个问题：现实中的 Objects 之间是存在关系的——比如，forrest 是个 IQ 为 75 的傻子，傻子是 People 的一种。而我们仅仅是生搬硬套了一种语法而割裂了这种 "is-a" 关系。现在我们的工作，目的之一就是让这样一个“真切”的世界从我们已有的编程风格的地基上拔地而起。<br />到底应该怎样做才能使 Fool 产生的对象都能响应 People 的消息呢？我们要给 Fool 产生的对象（也就是返回的那个匿名函数啦）都添加这样一种能力：如果在 Fool 中响应不了消息，那就反馈给 People 响应。<br /><pre name="code" class="javascript">
function Fool (iq) {
	var IQ = iq || 0
	function init (iq) {
		IQ = iq
	}
	return (function (verb) {
		try {
			return eval(verb)
		} catch (e) {
			return People()(verb)
		}
	})
}
</pre><br />js> forrest = Fool()<br />js> forrest('init')(75)<br />js> forrest('IQ')<br />75<br />js> forrest('money')<br />0<br /><br /><strong><span style="font-size: 12pt">五. 语法扩展和代码生成</span></strong><br />这下代码量增加了很多，强迫潜在的使用者们在创建每个类时都这样写那实在是令人抓狂。本来这篇文章应该不提此类问题的解决，但考虑到有益于读者理解“机制”这个抽象概念，这里给出一个可行的方案——把普通的类代码用 Function() 函数重编译为可用的 JavaScript 函数。也就是说，我们能给出类扩展的代码并指定被扩展的类来获取类似上文的代码：<br /><pre name="code" class="javascript">
Fool = extend('People()', function (iq){
	var IQ = iq || 0
	function init (iq) {
		IQ = iq
	}
})
</pre><br />为了方便字符串操作，我们希望编译后的代码的参数部分（如 People()）都集中出现在一个位置且尽可能便于定位。在函数头添加一句<br /><pre name="code" class="javascript">
var origin = People()
</pre><br />当然是可行的，这样还能使 Fool 内部显式引用到其超类。但这样还不够漂亮。我们修改编译后的样例代码为：<br /><pre name="code" class="javascript">
function () {
	return (function (origin) {
		var IQ = 0
		function init (iq) {
			IQ = iq
		}
		return (function (verb) {
			try {
				return eval(verb)
			} catch (e) {
				return origin(verb)
			}
		})
	})(People())
}
</pre><br />这个利用参数传递变量的小技巧不值得学习，实际效率不高。但在这篇文章中，这样绑定特殊变量的技术是标准方案。<br />那么，我们的语法扩展兼代码生成函数 extend() 的实现为：<br /><pre name="code" class="javascript">
function extend (originc, code) {
	function argsArea (code) {
		// 题外话，正则表达式也有不值得一用的时候
		return code.slice(code.indexOf('(')+1, code.indexOf(')'))
	}
	function bodyCode (code) {
		// 不用 trim() 了，别没事儿找事儿
		return code.slice(code.indexOf('{')+1, code.lastIndexOf('}'))
	}
	function format (body) {
		var objc = bodyCode(function () {
			return (function (verb) {
				try {
					return eval(verb)
				} catch (e) {
					return origin(verb)
				}
			})
		}.toString())
		return 'return (function (origin) {'+body+objc+'})('+originc+')'
	}
	var $ = code.toString()
	return Function(argsArea($), format(bodyCode($)))
}
</pre><br />这样前文提到过的 extend 的实例代码就可以正常运行了，测试代码不再重复。<br /><br /><strong><span style="font-size: 12pt">六. 机制完备化</span></strong><br />这样，我们的基于消息传递编程风格的一套面向对象机制就确定下来了。机制是宪法，是语言的根本大法，有了它，我们就可以通过修改代码生成器，很快地给这套机制进行完备化。<br />想法有很多，例子只举两个。<br />第一个例子：类的定义中应该能直接引用到将产生的对象 self。答案只有一句话：把返回的那个作为对象的匿名函数命名为 self。<br />第二个例子：既然是单继承模式，应当存在一个顶层类 AbsObj，使没有指定继承的类自动继承它。答案是：在 extend 函数体第一行添加代码：<br /><pre name="code" class="javascript">
if (arguments.length == 1) {
	code = originc
	originc = 'AbsObj()'
}
</pre><br />然后手工构造设计 AbsObj 类，为空也无所谓。不过当然了，一般都会给顶层类添加一些全局性质的消息绑定。由于是“底层操作”，基本上都需要修改 extend 函数。做了一个简单的：<br /><pre name="code" class="javascript">
function AbsObj () {
	//检测是否能响应此 verb，要再用一次异常处理
	function canHandle(verb){
		try {
			// 别担心这里的 self 会传递不过去
			self(verb)
		} catch (e) {
			return false
		}
		return true
	}
	function toString() {} // 这个搞起来其实很麻烦~`
	var self = function (verb) {
		return eval(verb)
	}
	return self
}
</pre><br /><br />js> Obj=extend(function(){x=5})<br />js> o=Obj()<br />js> o('canHandle')('x')<br />true<br />js> o('canHandle')('y')<br />false<br /><br />文章写完了，小结一下。消息传递的编程不仅仅是一种代码风格，还可以成长为一种完备的机制。这种完备性远不只是这两篇加起来不到300行的文章所能覆盖的（例如非常彻底的“万物皆对象”，因为只要是能响应消息的函数，连接一下 AbsObj 就是合法对象了；类，函数都可以），大家可以试着玩一玩，顺便体会一下这个计算模型的透明和强大。<br />另外，熟悉函数式编程的朋友可以帮忙思考一下：这样一个基于闭包变换的计算模型实质上是函数式的，再配合动态的函数式的对象级继承（用一个匿名类代换一下）就能在纯 FP 真正下实现 OOP 了。可惜的是每一次更新操作都要重新生成对象，性能代价大了点，yet another question.
          <br/>
          <span style="color:red;">
            <a href="http://lichray.javaeye.com/blog/113907#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Sat, 18 Aug 2007 20:10:17 +0800</pubDate>
        <link>http://lichray.javaeye.com/blog/113907</link>
        <guid>http://lichray.javaeye.com/blog/113907</guid>
      </item>
      <item>
        <title>读《ACM演讲集》有感</title>
        <author>Lich_Ray</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://lichray.javaeye.com">Lich_Ray</a>&nbsp;
          链接：<a href="http://lichray.javaeye.com/blog/110626" style="color:red;">http://lichray.javaeye.com/blog/110626</a>&nbsp;
          发表时间: 2007年08月09日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <div class="quote_title">引用</div><div class="quote_div">上个学期末学校开展“读书月”活动，然后我冒“天下”之大不讳写了这篇文章，现转发在此。</div><br /><br /><span style="font-size: 12pt"><br />最近我在读一本似乎和编程没太大关系的计算机书籍，叫《ACM图灵奖演讲集 - 前20年》（以下简称《ACM演讲集》）。这本书收录了1966-1985年这20年间图灵奖获奖者们的演讲。相对于计算机的普及速度，文章已经很古老了，中文版出版也很久了，但并不像那些纷扰而嘈杂的电脑书刊那样稍纵即逝。普通的电脑书，充斥着看似先进的技术；普通的编程书，充斥着不知为什么而生的代码。而它，就像一位智者，静坐在书店那些已经满是垃圾文字的书架上，看着“畅销书排行榜”上的风云变幻，嘴角一丝不易察觉的坏笑。<br /><br />“看一本好书，就是在和许多伟大的灵魂对话”。如果《ACM演讲集》仅仅保存着一位作者的灵魂，不可能被我如此看重。事实上，它收录了23位获奖者的22篇文章。代码寥寥无几，甚至是作为反面教材的多于实例；除了一两篇纯属学术论文的文章，余者也没有复杂的数学推导。然而就是这样一本似乎只是在空谈的书，让我了解到了计算机科学发展的黄金时代的情况和“上古”的计算机前辈科学家们的不朽的思想与伟大的探索。约翰·巴克斯（BNF范式设计者、Algol语言发明人），唐纳德·E·克努特（《计算机编程艺术》作者、Tex系统设计者），约翰·麦卡锡（人工智能先驱，Lisp语言发明人），看着这些熟悉的名字，阅读着他们充满智慧和神性的演讲稿，研究前辈们思想的结晶，真是乐事。<br /><br />我对当代计算机的发展（尤其是国内）失望久矣：教育者们误人子弟，学生们急功近利，程序员们心浮气躁，整个国内软件业不成体统。步入计算机科学的殿堂后，我头脑中一直盘旋着许多疑惑——计算机科学是怎样发展的？科学家们都是怎样探索的？我应该如何学习？《ACM演讲集》给了我答案。其中每一篇文章都在总结前人努力的基础上打开崭新的视界，向我展示了前辈们创世纪般的工作。事实上，80年代之后，商业的发展严重阻碍了计算机科学的发展以及编程语言的进步，半个世纪前的文章仍然有着重要意义，人们仍然可以窥见大师们惊人的高瞻远瞩。<br /><br />《算法系统的综合（1966）》展示了第一位图灵奖获得者阿兰·伯利斯对当时编程语言及算法的理解，现在的系统少有出其右者；《程序的函数风格及其代数（1978）》展示了巴克斯设计的形式化函数式编程系统（FFPS），其严谨的函数式编程的数学定义令人叹为观止，他的思想至今只有极少数语言能够超越；《人工智能的一般性（1971）》中，麦卡锡对当时已有的Prolog逻辑式编程语言进行了分析并给出了在机械化推理方面的新思路；《关系数据库：生产力的实用基础（1981）》一文总算让大家有种“找到组织了”的感觉，然而一看文章才知道我们从不离手的关系型数据库理论是那样复杂，实现是那样困难，耗尽了一代科学家的心血。<br /><br />然而将这些伟大的努力投入实际应用的人们交还给前辈们什么了呢？商业化的、根本无法与其它产品协作的系统，然而它们独霸市场甚至早就了产业链；用来招揽更多用户的垃圾编程语言，然而它们渗入各层次计算机教育甚至是国家级项目。市场是产生了，教育却落后了，各式各样的流行文化你方唱罢我登场，有什么用？难道这些人不需要科学的进步了吗？看看现在那些公司和个人在汹涌而来的多处理器构架和分布式计算问题前的手足无措状就知道他们的可笑和可悲了。<br /><br />说这些又有什么用？没有用，不说了。我学我的，他搞他的，不相干。<br /><br />最新的《ACM演讲集》应该不久就能引进了吧？多希望能看到我的偶像Alan Kay在2003年获得图灵奖时的演讲啊！顺便提一句，他的Smalltalk语言及其系统自1972年实现以来已经30多年没见过真正的对手了，不过，想在未来10年之类被某个对手超越仍然几乎是不可能的。<br /></span>
          <br/>
          <span style="color:red;">
            <a href="http://lichray.javaeye.com/blog/110626#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Thu, 09 Aug 2007 22:33:10 +0800</pubDate>
        <link>http://lichray.javaeye.com/blog/110626</link>
        <guid>http://lichray.javaeye.com/blog/110626</guid>
      </item>
      <item>
        <title>用 Python 秒掉八皇后问题！</title>
        <author>Lich_Ray</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://lichray.javaeye.com">Lich_Ray</a>&nbsp;
          链接：<a href="http://lichray.javaeye.com/blog/106747" style="color:red;">http://lichray.javaeye.com/blog/106747</a>&nbsp;
          发表时间: 2007年07月29日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <span style="color: darkred">文章中用纯文本制作的图不可使用等宽字体显示。请<a href="http://www.javaeye.com/topic/106747" target="_blank">进入论坛</a>查看本文，文中错误参考回帖，谢谢。</span><br /><div class="quote_title">引用</div><div class="quote_div"><br />在 <a href="http://www.javaeye.com/topic/101055" target="_blank">函数式编程语言曲高和寡？</a> 一文中，我们看到 Haskell 能用两行代码<br /><pre name="code" class="python">
sort [] = []
sort (x:xs) = sort [y | y &lt;- xs, y &lt; x] ++ [x] ++ sort [y | y &lt;- xs, y >= x]
</pre><br />搞定快速排序算法。这是偶然，还是必然？在这篇文章中，lichray 用我们所熟悉的 Python 语言，几行代码搞定很多学编程几年的人都只是一知半解的算法——八皇后问题，展示和上篇文章中的快速排序一样清晰的、令人耳目一新的<strong>函数式算法思想</strong>。<br /></div><br /><br /><strong>预备知识：</strong><br />这一部分对于那些 Python 老手和已经知道八皇后问题定义的程序员来说是多余的。<br />　　1. 八皇后问题（摘自 SICP ed2 中文版 P84 练习2.42）<br />　　“八皇后谜题”问的是怎样将八个皇后摆在国际象棋棋盘上，使得任意一个皇后都不能攻击另一个皇后（也就是说，任意两个皇后都不在同一行、同一列或者同一对角线上）。一个可能的解如图所示。<br />　　┌──┬──┬──┬──┬──┬──┬──┬──┐<br />　　│　　│　　│　　│　　│　　│ Q　│　　│　　│<br />　　├──┼──┼──┼──┼──┼──┼──┼──┤<br />　　│　　│　　│ Q　│　　│　　│　　│　　│　　│<br />　　├──┼──┼──┼──┼──┼──┼──┼──┤<br />　　│ Q　│　　│　　│　　│　　│　　│　　│　　│<br />　　├──┼──┼──┼──┼──┼──┼──┼──┤<br />　　│　　│　　│　　│　　│　　│　　│ Q　│　　│<br />　　├──┼──┼──┼──┼──┼──┼──┼──┤<br />　　│　　│　　│　　│　　│ Q　│　　│　　│　　│<br />　　├──┼──┼──┼──┼──┼──┼──┼──┤<br />　　│　　│　　│　　│　　│　　│　　│　　│ Q　│<br />　　├──┼──┼──┼──┼──┼──┼──┼──┤<br />　　│　　│ Q　│　　│　　│　　│　　│　　│　　│<br />　　├──┼──┼──┼──┼──┼──┼──┼──┤<br />　　│　　│　　│　　│ Q　│　　│　　│　　│　　│<br />　　└──┴──┴──┴──┴──┴──┴──┴──┘<br />　　在这篇文章中，我们要解决的问题比这个范围还要更广一点，即：允许棋盘是 n × m 大小的。也就是说，所谓的 n 皇后问题只是我们给出的程序的 n ＝ m 时的版本。不过别担心，<strong>函数式编程的力量就在于抽象等级的空前提高</strong>，问题越抽象，解决起来越顺手。<br />　　<br />　　2. 列表领悟特性<br />　　现在应该绝大多数动态语言的程序员都对这个特性很了解了，因为常见的 Python, Ruby, ErLang, Haskell 甚至是 JavaScript 都加上了这个特性。这是一种通过给出列表中<strong>每一项的形式</strong>和组成形式的元素要满足的条件自动生成列表的语法糖。下面是 Python 中的两个例子：<br /><pre name="code" class="python">
# 得到一个 2 到 20 中的偶数组成的列表，只要写
>>> [x for x in range(2, 21) if x % 2 == 0]
[2, 4, 6, 8, 10, 12, 14, 16, 18, 20]

# 记住，只需给出一个形式，比方说想求两个列表的“笛卡尔乘积”
>>> [(x , y) for x in range(4) for y in [3,1,7,8]]

</pre><br />　　很明显，仅仅使用列表领悟特性就可以表示一些算法了，比如提到过的快速排序：<br /><pre name="code" class="python">
def sort (ls): 
　　return [] if ls == [] \
　　　else sort([y for y in ls[1:] if y &lt; ls[0]]) + \
　　　[ls[0]] + \
　　　sort([y for y in ls[1:] if y >= ls[0]])
　　# 别忘了 python-2.5 的新特性条件分支表达式哦！可惜太长，不得不强制换行。
</pre><br />　　但要注意一点：<span style="color: white">实现拙劣的多未知数列表领悟（比如 Python）可能会崩了你的程序，文中会谈到这一点。</span><br /><br /><strong>算法描述：</strong><br />　　首先我们要认识到一点，算法所对应的函数的输入和输出分别是什么。我们需要的是一个函数 queens()，它接受两个参数，<strong>自然数</strong> row 和 col，分别表示行数和列数；坐标是 (col, row) 形式的序对，下标 从 0 开始计数。例如，对于一个 3 × 4 的棋盘，<br />　　┌──┬──┬──┬──┐<br />　　│0, 0│1, 0 │　　│　　│<br />　　├──┼──┼──┼──┤<br />　　│　　│ Q　│2, 1│　　│ row = 3<br />　　├──┼──┼──┼──┤<br />　　│　　│　　│2, 2│　　│<br />　　└──┴──┴──┴──┘<br />　　　　col = 4<br />　　我们把输出的结果表示为棋盘格局描述组成的列表。那棋盘格局呢？难道表示为所有棋子的坐标，像 [(5,0), (2,1), (0,2),(6,3), (4,4), (7,5), (1,6), (3,7)] ？不需要吧，看也能看出来，完全可以使得下标 col、row 中有一个是有序排列的。在这里，我们<strong>认为</strong> row 是有序的，对于上面的例子，只须表示为 [5, 2, 0, 6, 4, 7, 1, 3] 即可，row 是一个解的下标。<br />　　那么，整个函数的输出就应该类似这样：[[1, 3, 0, 2], [2, 0, 3, 1]]；这是 queens(4,4) 的输出结果。<br /><br /><strong>归纳法定义：</strong><br />　　什么是归纳法定义？回忆一下经典的求级数例程是怎么写出来的。我们根据数学归纳法得到：<br />　　<em>f</em> (0) = 1<br />　　<em>f</em> (n) = 1 + <em>f</em> (n - 1)<br />　　然后把这些抄成编程语言的形式。对于函数 queens 也是这样，我们要确定这个函数的递归下界和递推表示。<br />　　递归下界很好办，就是 queens(n,0) （此处 n 忽略，因为不影响结果）的输出结果。对于一个没有列数的棋盘，只有一个解，空解 []；同时，输出的<strong>解集</strong>也只有这一个元素，为 [[]]（下面的数学定义使用了集合表示代替列表）。<br />　　<em>f</em> (*,0) = {{}}<br />　　递推表示是什么呢？我们可以在纸上画画图，不难发现，对于一个解，你画出的最后一个位置就是在前面已画出的<strong>少一个棋子的格局</strong>的基础上再<strong>加</strong>一个位置<strong>安全</strong>的棋子。设这个“加”函数为 <em>g</em> (x,y)，“安全”函数为 <em>s</em> (x,y)。那么<br />　　<em>f</em> (n,m) = {<em>g</em> (x,y) | x ∈ [0, n], y ∈ <em>f</em> (n,m-1) <em>s</em> (x,y) = <strong>true</strong>}<br /><br /><strong>形式化的思考：</strong><br />　　有了上面的数学定义，抄成 Python 代码，那就是用脚趾都能搞定呀。<br /><pre name="code" class="python">
def queens (row, col):
　　return [[]] if col == 0 \
　　　else [[ran] + rst \  # 在 Python 中，g(x,y)=[x]+y 
　　　for ran in range(row) \
　　　for rst in queens(row, col - 1) \
　　　if safe(ran, rst)]
</pre><br />　　不难看出，有了列表领悟这个强大的武器，我们就可以放心大胆地利用描述一个元素形式的思路来解决这类列表输出的问题。这就是列表领悟最根本的思想：“形式化的思维方式”。<br />　　现在只剩一个问题了：safe() 函数。先应用一次我们的“图形化的思考”。对一个格局（解，rst）来说，新加入的棋子的 col 值 ran 必须对这个格局中所有已存在的<strong>位置</strong>满足一个测试 check()，这个测试对于一个位置 (x,y)，要求（col 值不等已自动满足） ran ≠ x and |ran - x| ≠ y + 1。<br />　　ran ≠ x 很好理解，就是不为同一列；|ran - x| ≠ y + 1 则意味着左右不在同一斜线上。<br />　　┌──┬──┬──┬──┐<br />　　│　　│ran │　 　│　　│　0<br />　　├──┼──┼──┼──┤<br />　　│x1y │　　│　　│ Q　│　1<br />　　├──┼──┼──┼──┤<br />　　│ Q　│　　│　　│x2y │　2<br />　　├──┼──┼──┼──┤<br />　　│　　│　　│ Q　│　　│　3<br />　　└──┴──┴──┴──┘<br />　　　　0　 　1　 　2　 　3<br />　　如图，算一算图中的两个点 (x1,y) 和 (x2,y)，是不是满足了上面的式子？<br />　　由于这里要同时用到 rst 中点的 col 值和 row 值，这样解决：在前文的<strong>算法描述</strong>中已经指出，因为 row 值被认为是有序的，事实上是一个解的下标，我们 check 一下这个下标，在 check() 的过程中去获取 col 值不就行了？<br /><pre name="code" class="python">
　　def check (pos):
　　　　# 表达式变个形式，少打点字
　　　　return not (ran == rst[pos] or abs(ran - rst[pos]) == pos + 1)
</pre><br />　　值得注意的是，由于这里 check() 用到了逃逸变量 ran 和 rst，check 函数体就必须写在 safe() 函数体内部以使这它们在其<strong>闭包环境</strong>中出现。<br />　　safe() 函数也就很明了了：先生成一个由全部 check(pos), pos ∈ [0, #rst] 结果组成的列表，然后判断一下这个列表中每一项是否都为真。假设我们已得到这样的一种测试一个列表中元素是否全为 True 的函数叫 ands()。<br /><pre name="code" class="python">
def safe (ran, rst):
　　def check (pos):
　　　　return not (ran == rst[pos] or abs(ran - rst[pos]) == pos + 1)
　　return ands([check(pos) for pos in range(len(rst))])
</pre><br />　　前文已经说明，ands() 函数接受一个列表为参数，如果列表中每一项都为 True 则返回 True，否则返回 False。还是直接把数学定义抄一遍：<br /><pre name="code" class="python">
def ands (ls):
　　return True if ls == [] \
　　　else (False if not ls[0] \
　　　else ands(ls[1:]))
</pre><br />　　于是，我们的程序就写完啦！试着跑一下 queens(4,4)，<br /><pre name="code" class="python">
>>> queens(4,4)
[[1, 3, 0, 2], [2, 0, 3, 1]]
</pre><br />　　没问题！再跑一下 queens(8,8)！奇怪，为什么跑了 10 分钟还没出结果？<br /><br /><strong>问题在哪儿：</strong><br />　　<span style="color: white">拙劣的列表领悟实现。Python 在处理列表领悟时，一方面把“形式”部分封装为一个函数，然后找出所有未知数的列表，组装成一个大的矩阵，然后对矩阵中的每一项应用该函数。问题出在哪儿？先生成了所有项，占用了过多的空间。</span>如果在 Haskell 里面，这样做是无所谓的，因为惰性的列表会生成一点，计算一点，抛弃一点，输出一点；<span style="color: white">但这对于严格（采用应用序求值）的命令式语言 Python 来说，这种实现仅仅是玩具级的。</span><br />　　其实，列表领悟不仅仅是一个语法糖，在严格的语言实现它需要一定技巧：首先分析未知数对于的列表的计算所消耗时间（数据流分析 call 的次数），把计算耗时最多的列表应用群体操作，对结果的每一项应用产生下一个列表的群体操作，依此类推，<strong>只要产生一个包含所有未知数的序对就应用包装好的函数</strong>。空泛的讲这些用处不大，下面我们手工把这个该死的程序救活。<br /><br /><strong>通用列表操作：</strong><br />　　关键在于处理掉这一句：<br /><pre name="code" class="python">
	[[ran] + rst \
　　　for ran in range(row) \
　　　for rst in queens(row, col - 1) \
　　　if safe(ran, rst)]
</pre><br />　　首先对最耗时的列表 queens(row, col - 1) 应用群体操作 flatmap(newcol, queens(row, col - 1))。这里需要注意的是，光用 map() 是不够的，因为下面会根据另一个列表 rang(row) 中的每一项产生一个新列表，导致<strong>多出</strong>一个列表层。所以我们需要用 flatmap() 函数，它在普通的 map() 操作之后会用 append() 把产生的列表联结起来（所以叫“展平”的 map），把多出的一层列表消去：<br /><pre name="code" class="python">
def flatmap (opt, ls):
　　# 小技巧：累积器在连接列表时可以不用 lambda x,y: x+y，直接用 list.__add__，
　　# 重点是类型出错时有报警
　　return reduce(list.__add__, map(opt, ls))
</pre><br />　　接下来，根据列表和列表生成列表（在通用列表操作的世界中，就是 list, list, and list），注意原来的形式 [ran] + rst 是怎么被函数封装掉的：<br /><pre name="code" class="python">
def newcol (rst):
　　# 顺手解决 filter 只能传递一个参数的限制，用 lambda 代掉
　　return filter(lambda ls: safe(ls[0], rst), map(lambda x: [x] + rst, range(row)))
</pre><br />　　其它什么内容都不用改了（当然了，如果你有心情，还可以把另一个列表领悟替换掉，<br /><pre name="code" class="python">
def safe (ran, rst):
　　return every(check, range(len(rst)))
# every() 函数相当于打过兴奋剂的 ands
def every (test, ls):
　　return True if ls == [] \
　　　else (False if not test(ls[0]) \
　　　else every(test, ls[1:]))
</pre><br />　　不过单层的列表领悟没有性能问题）。现在可以完美地输出八皇后问题的 92 个解了（这里就不列了，太长了）。<br /><br /><strong>反思：</strong><br /><ul><li> 函数式编程的思想描述算法绝对强，无论是列表领悟还是通用列表操作都十分清晰，仿佛能感受到数据在表达式间游动；</li><li> 和平起见，就不把我那个传说中的只有一行代码的版本拿来和几十行的命令式风格的代码比较了，但大家应该心领神会；</li><li> 有时候，把问题想的简单一些，思维更“形而上学”一些，反而能更快的得到解决问题的思路，顺着思路再改进也不迟；</li><li> Python 的表达式版的分支语句很漂亮，但被该死的换行限制给玷污了；<span style="color: white">另外列表领悟实现得够烂，眼下只能是玩玩儿。</span></li></ul><br /><br />最后留个小问题：我们在<strong>算法描述</strong>一节中提到了一种解的表示方法，把一个格局中所有棋子的坐标表示为序对，然后返回解的列表。请给出一个小函数，把我们现在的实现版本输出的解转换为这种形式。数据的本质是信息。信息的完全是首要的，数据的表示只是个次要问题。
          <br/>
          <span style="color:red;">
            <a href="http://lichray.javaeye.com/blog/106747#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Sun, 29 Jul 2007 20:33:16 +0800</pubDate>
        <link>http://lichray.javaeye.com/blog/106747</link>
        <guid>http://lichray.javaeye.com/blog/106747</guid>
      </item>
      <item>
        <title>functional.js 介绍及源码分析</title>
        <author>Lich_Ray</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://lichray.javaeye.com">Lich_Ray</a>&nbsp;
          链接：<a href="http://lichray.javaeye.com/blog/105854" style="color:red;">http://lichray.javaeye.com/blog/105854</a>&nbsp;
          发表时间: 2007年07月26日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <div class="quote_title">引用</div><div class="quote_div">本文对刚刚在网络上现身的 JavaScript 函数式编程库 <a href="http://osteele.com/javascripts/functional/" target="_blank">functional.js</a> 进行详尽的解读（地址已更正，感谢 hax）。<br /></div><br />functional.js 是模仿 Haskell 语言标准库 Prelude 制作的函数式编程库，主要实现了：<br /><ul><li> 扩展的克里化函数</li><li> 运算符函数化</li><li> 紧缩的匿名函数语法</li><li> 无须指定参数的匿名函数语法</li><li> 函数向导语法</li><li> 基本的通用列表操作</li><li> 部分扩展基于对象化</li></ul><br />其中，扩展语法由字符串表示。未能实现的特性有：<br /><ul><li> 尾递归优化</li><li> 模式匹配（包括参数匹配、列表匹配、情况分析）</li><li> 惰性运算（包括无穷列表）</li><li> 列表领悟</li><li> 扩展绑定、同时绑定</li><li> 其它列表操作（以及对于列表操作的基于对象化）</li></ul><br />下面我们一边分析<a href="http://osteele.com/javascripts/functional/functional.js" target="_blank">源代码</a>，一边讲解库的用法。<br /><br />一、库安装和概览<br />functional.js 库的所有用户级操作分为3个部分：<br /><ol><li> 全局操作，绑定在全局对象 Functional 上，主要是高阶函数操作和列表操作，所谓库安装即把这些内容可选的、安全地复制到全局环境</li><li> 函数扩展，实现特殊高阶函数特性的工具（特供内部）</li><li> 语法扩展，绑定在 String.prototype 上，负责将字符串表示的 lambda 语法翻译为相应的高阶函数（特供内部）</li></ol><br />下面是安装函数 Functional.install 的源代码（中文注释为笔者所加，下同）：<br /><pre name="code" class="javascript">
Functional.install = function(except) { // except 参数是一个对象，不加载这些操作 
    var source = Functional,
        target = window; // 复制操作到全局环境 window，仅限于浏览器环境
    for (var name in source)
        name == 'install' // 当然，不能把 install 复制到 source
        || name.charAt(0) == '_' // 命名开头为 _，私有属性
        || except && name in except
        || {}[name] // work around Prototype
        || (target[name] = source[name]);
}
</pre><br />一般只要执行 Functional.install() 一句即可。<br /><br />二、高阶函数操作<br />1. Functional.compose ([Function]) // 匿名的参数类型指的是 arguments 的类型，下同<br />　　接受一列参数个数被认为相等的（允许 Currying 算子）函数为参数，返回一个函数，它接受一定的参数，能够对它们累积倒序 apply 那列函数。<br />　　示例：compose('1+', '2*')(2) => 5<br />　　<br />2. Functional.sequence ([Function])<br />　　累积 apply 的顺序为参数顺序，为 compose 的反序。<br />　　示例：sequence('1+', '2*')(2) => 6<br /><br />以上两个操作亦可见于 Function.prototype，用法：'1+'.lambda().sequence('2*')(2) ==> 6<br /><br />3. Function.prototype.flip ()<br />　　返回一个函数，是原函数对象 this 参数接受顺序颠倒后的版本，不应属于函数扩展类。<br />　　示例：flip('a/b')(1, 2) => 2<br />　　<br />4. Function.prototype.saturate ([]) {<br />　　返回一个函数，是原函数对象 this 忽略自己接受的参数，仅接受指定参数的版本。<br />　　形式：f.saturate(args...)(args2...) == f(args...)<br />　　<br />5. Function.prototype.aritize (n::Number) // 有名的参数类型由 :: 指定，下同<br />　　返回一个函数，是原函数对象 this 忽略自己接受的参数列表中下标为 n 的参数的版本。<br />　　<br />6. Function.S (f, g::Function) <br />　　以单个大写字母命名的是函数的原子操作，应被收入 Functional 对象，这个很奇怪。<br />　　形式：S(f, g)(args...) == f(g(args...), args...)<br />　　<br />三、通用列表操作<br />1. 绑定在 Functional 对象上的部分完全照抄 Haskell Prelude 以及 Clean 的命名，它们是：<br /><ol><li> map(f, [x1, x2...]) = [f(x, 0), f(x2, 1), ...]</li><li> foldl, reduce(f, init, [x0, x1, x2]) == f(f(f(init, x0), x1), x2)</li><li> filer, select('%2', [1,2,3,4]) -> [1, 3]</li><li> foldr(f, init, [x0, x1, x2]) == fn(x0, f(x1, f(x2, init)))</li><li> some(f, [x1, x2, x3, ...]) == f(x1) || f(x2) || f(x3)...</li><li> every(f, [x1, x2, x3, ...]) == f(x1) && f(x2) && f(x3)...</li></ol><br />以上操作的介绍网上到处都是，不再赘述；但有一点不同，即它们除了接受正常参数之外，还在最后接受一个可选参数 object::Object，它被用于指定操作执行的对象/环境。<br />另外，这些操作全部基于命令式风格实现，对于没有尾递归优化的 JavaScript 来说，效率有保障。<br /><br />四、群体谓词操作<br />1. Functional.and ([Function])<br />　　接受一列函数为参数，返回一个函数，它接受一个参数，对该参数 apply 那列函数，如结果全为 true，返回 true；否则返回 false。<br />　　形式：and(f1, f2...)(args...) == f1(args...) && f2(args...)...<br />　　示例：and('>1', '>2')(2) => false<br />　　<br />2. Functional.or ([Function])<br />　　接受一列函数为参数，返回一个函数，它接受一个参数，对该参数 apply 那列函数，如结果全为 false，返回 false；否则返回 true。<br />　　形式：or(f1, f2...)(args...) == f1(args...) || f2(args...)...<br />　　示例：or('>1', '>2')(2) => true<br />　　<br />3. Functional.not = function(fn::Function)<br />　　返回一个函数，是参数返回的布尔值的函数（谓词，下同） fn 取否的版本。<br />　　形式：f.not()(args...) == !f(args...)<br />　　<br />4. Functional.equal ([Function])<br />　　接受一列函数为参数，返回一个函数，它接受一个参数，对该参数 apply 那列函数，如结果全部 == ，返回 true；否则返回 false。<br />　　形式：equal(f1, f2...)(args...) == f1(args...) == f2(args...)...<br />　　示例：equal()() => true // 特殊情况<br /><br />五、函数扩展<br />　　这一章仅仅是介绍内部实现。<br />1. Function.prototype.bind (object::Object,[])<br />　　返回一个函数，作为 this 函数对象的副本，使其将在 object 环境下执行，并额外携带参数。<br />　　形式：f.bind(obj, args...)(args2...) == f.apply(obj, [args..., args2...])<br />　　<br />2. Function.prototype.curry ([])<br />这是实现克里化特性的关键函数，思想来自<a href="http://www.coryhudson.com/blog/2007/03/10/javascript-currying-redux/" target="_blank">网络</a>。<br /><pre name="code" class="javascript">
Function.prototype.curry = function(/*args...*/) {
    var fn = this;
    var args = [].slice.call(arguments, 0);
    return function() {
        return fn.apply(this, args.concat([].slice.call(arguments, 0)));
    };
}
</pre><br />　　返回那个传说中的可在参数不足时分步调用的函数——Currying 算子。<br />　　形式：f.curry(args1...)(args2...) == f(args1..., args2...)<br /><br />其它的 curry 类函数有：<br /><ul><li> rcurry，对从右边开始缺少参数的函数作克里化</li><li> ncurry，不接受全部参数就不 apply 参数的版本</li><li> rncurry，前者的反序版本</li><li> uncurry，作者一再强调，这不是 curry 的反转版本。它会拆分出第一个已得参数，形式为：f.uncurry(a, b...) == f(a)(b...)</li></ul><br /><br />3. Function.prototype.partial ([])<br />　　在此函数定义之前，有定义 _ = Function._ = {} 。结合它们可以允许你像在 Haskell 中那样在参数列表中用 _ 忽略参数。但现在空谈是没用的，要结合第七章的语法扩展。<br />　　<br />4. Function.prototype.guard (guard:>Function, otherwise)<br />　　类似的，是一个允许在函数定义中使用向导功能的工具，尚缺少语法扩展支持。<br />　　形式：f.guard(g, h)(args...) == f(args...), when g(args...) is true<br />　　　　f.guard(g ,h)(args...) == h(args...), when g(args...) is false<br /><br />六、工具函数<br />1. Functional.invoke (methodName::String, [])<br />　　示例：invoke('toString')(123) => "123"<br />　　<br />2. Functional.pluck (name::String)<br />　　示例：pluck('length')("abc") => 3<br />　　<br />3. Functional.until (pred:>Function, fn:>Function) // 用 :> 表示将参数强制转换类型<br /><pre name="code" class="javascript">
Functional.until = function(pred, fn) {
　　// 使用时参数会被强制转为 Functional 的函数，参数可为字符串
    fn = Function.toFunction(fn); 
    pred = Function.toFunction(pred); 
　　// 返回一个接受一个参数的函数，
    return function(value) {
　　　　// 它不断对此参数 apply 函数 pred，
        while (!pred.call(null, value))
　　　　　　// 并用 fn(value) 的值更新 value，
            value = fn.call(null, value);
        return value; // 直到测试结果为 true。
    }
}
</pre><br />　　类似 Haskell 的 until，是一种函数式的循环，用命令式风格实现。<br />　　<br />4. Functional.zip ([])<br />　　特别注意，此 zip 并非 Haskell 中的 zip，它接受可变参数列表而不是列表的列表。<br />　　形式：zip(a, b...) == [[a0, b0], [a1, b1], ...]<br /><br />以上的章节中绑定在 Functional 上函数都可作为 Function 的对象方法直接使用，我们看这一行：<br /><pre name="code" class="javascript">
Functional.__initalFunctionState = 
Functional._startRecordingMethodChanges(Function.prototype);
</pre><br />前文对它们作出了定义，这里忽略。用法：name(arg, args...) == arg.name(args...)。<br /><br />七、语法扩展<br />1. String.prototype.lambda ()<br />　　把字符串表示的字符串翻译为函数扩展可接受的函数，进一步转为 JavaScript 函数。<br /><pre name="code" class="javascript">
String.prototype.lambda = function() {
    var params = []; // 存储字符串形式的参数的列表
    var expr = this;
　　// ECMAsplit 是作者为兼容 IE6.0 所写的 split 版本
    var sections = expr.ECMAsplit(/\s*->\s*/m); // 使字符串被 '->' 分割
　　/* 注意，分割的结果支持超过任意个 '->'，下面会发现，
　　　　-> 9 或者
　　　　x -> y -> x+y 这样的代码也会被正确理解。
　　*/
    if (sections.length > 1) {
　　　　// 这就是所谓的“正确理解”了
        while (sections.length) {
            expr = sections.pop();
　　　　　　// 然后把参数打碎，再重组为 JS 可识别的参数语法
　　　　　　/* 也就是说，x y -> x*y+2 和
　　　　　　　　x,y -> x*y+2 都可被接受。
　　　　　　*/
            params = sections.pop().split(/\s*,\s*|\s+/m);
　　　　　　// 装配成代码，顺便支持尾递归语法
            sections.length && sections.push('(function('+params+'){return ('+expr+')})'); 
        }
    } else if (expr.match(/\b_\b/)) {
        params = '_'; // 忽略参数的前奏，下文判断
    } else {
　　　　// 这里处理运算符表达式参数缺失的情况，相当于运算符函数化
　　　　// 分为前缺失和后缺失两种情况，
        var leftSection = expr.match(/^\s*(?:[+*\/%&|\^\.=&lt;>]|!=)/m);
        var rightSection = expr.match(/[+\-*\/%&|\^\.=&lt;>!]\s*$/m);
　　　　/* 注意，前缺失类似 *2，后缺失类似 2*，复杂表达式同样支持
　　　　　　此外，前后都缺失也可以，比如 * 甚至是 *3*
　　　　*/
        if (leftSection || rightSection) {
　　　　　　// 翻译缺失代码的技术：用 $1、$2 代换参数
            if (leftSection) {
                params.push('$1');
                expr = '$1' + expr;
            }
            if (rightSection) {
                params.push('$2');
                expr = expr + '$2';
            }
        } else {
　　　　　　// 这个地方就有点意思了；它使得函数支持参数指定缺失
　　　　　　/* 比如 x*y 就已经是一个函数了，相当于 x y->x*y
　　　　　　　　作者还特别防止了一个 bug，即对象属性访问语法中，
　　　　　　　　属性部分不被认为是未指定的参数。例如
　　　　　　　　obj.pro + 4 这个函数，只有 obj 一个参数
　　　　　　　　而且，this 和 arguments 不会被认为是未知数。
　　　　　　*/
            var vars = this.replace(/(?:\b[A-Z]|\.[a-zA-Z_$])[a-zA-Z_$\d]*|[a-zA-Z_$][a-zA-Z_$\d]*:|this|arguments|'(?:[^'\\]|\\.)*'|"(?:[^"\\]|\\.)*"/g, '')
			.match(/([a-z_$][a-z_$\d]*)/gi) || []; 
            for (var i = 0, v; v = vars[i++]; )
                params.indexOf(v) >= 0 || params.push(v);
        }
    }
    return new Function(params, 'return (' + expr + ')'); // 把代码装配成函数对象
}
</pre><br /><br />八、过滤器生成器<br />　　仅供特别好学的同志们参考。<br />1. Function.prototype.prefilterObject (filter::Function)<br />　　形式：fn.prefilterObject(filter).apply(object, args...) == fn.apply(filter(object), args...)<br />　　<br />2. Function.prototype.prefilterAt (index::Number, filter::Function)<br />　　形式：fn.prefilterAt(i, filter)(a1, a2, ..., a_{n}) == fn(a1, a2, ..., filter(a_{i}), ..., a_{n})<br />　　<br />3. Function.prototype.prefilterSlice (filter::Function, start, end::Number)<br />　　形式：fn.prefilterSlice(i0, i1, filter)(a1, a2, ..., a_{n}) == fn(a1, a2, ..., filter(args_{i0}, ..., args_{i1}), ..., a_{n})<br /><br />九、其它用户级函数<br />1. Functional.id = Functional.I = function(x) {return x};<br /><br />2. Functional.constfn = Functional.K = function(x) {return function() {return x}};<br /><br />3. .toFunction ()<br />　　在 String.prototype，Function.prototype，Function（需要参数 fn::Function） 上都有绑定，把对象转换为一个合适的 Functional 函数。但你不需要把代码写这样，map('*2'.toFunction(),alist)，因为全局用户级函数都会对应为函数的参数自动执行 toFunction()，只要 map('*2',alist) 就行了。另外，String.prototype 上还有 JavaScript-Like 的 call、apply 方法。<br /><br />十、结语<br />　　functional.js 很强，<a href="http://osteele.com/archives/2007/07/functional-javascript " target="_blank">很有用</a>，很牛X；但同时也很年轻（7.20 发布），很多可以实现的功能还不完善，不说列表领悟什么的吧，至少应该把 Haskell Prelude 库在通用列表操作方面的函数的移植工作完成。我们期待 <a href="http://osteele.com/" target="_blank">Oliver Steele</a> 的表现。
          <br/>
          <span style="color:red;">
            <a href="http://lichray.javaeye.com/blog/105854#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Thu, 26 Jul 2007 22:17:45 +0800</pubDate>
        <link>http://lichray.javaeye.com/blog/105854</link>
        <guid>http://lichray.javaeye.com/blog/105854</guid>
      </item>
      <item>
        <title>函数式编程语言曲高和寡？</title>
        <author>Lich_Ray</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://lichray.javaeye.com">Lich_Ray</a>&nbsp;
          链接：<a href="http://lichray.javaeye.com/blog/101055" style="color:red;">http://lichray.javaeye.com/blog/101055</a>&nbsp;
          发表时间: 2007年07月14日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <div class="quote_title">引用</div><div class="quote_div"><br />看到作者 lichray 忙于研究数理逻辑，其父发出了由衷的感叹：你学的东西没人用啊。“谁说没人用？自己看不懂罢了。Haskell 的语法是‘写意’了点，但其中的思想清澈见底。”<br /></div><br /><div class="quote_title">引用</div><div class="quote_div"><br />本文以一个函数式风格的快速排序算法为例，把它从 Haskell 代码改写为 大家所熟知的 JavaScript 代码，试图说明 FP 绝对是表达思想的最强工具。不要被那些 FP 语言们的语法所迷惑。终有一天，你会发现，原来这才是编程啊。<br /></div><br /><br />下面是一个 Haskell 写的快速排序算法，一共四行（想想学校里教的一般是多少行~）：<br /><pre name="code" class="java">
qsort [] = []
qsort (x:xs) = qsort lt ++ [x] ++ qsort st
	where lt = [y | y &lt;- xs, y &lt; x]
		st = [y | y &lt;- xs, y >= x]
</pre><br />想理解它是再简单不过的事：首先确定递归下界——排序空列表的结果为空列表本身。<br />qsort [] = []<br />然后把快速排序算法的目的描述一下：<br />把一个选出的元素（这里是第一个元素 x）作为“中间元素”，同时把剩余元素组成的列表记作 xs，将所有小于中间元素的元素组成的列表排序 qsort lt 连接上 ++ 这个元素“组成的列表”[x] 再连接上 ++ 所有大于等于这个元素的其它元素排序后的列表 qsort st；这里用到的 where 变量 it 指的是 = 所有小于它的元素组成的列表 [y | y &lt;- xs, y &lt; x]，st 指的是 = 所有大于等于这个元素的其它元素的列表 [y | y &lt;- xs, y >= x]。<br /><br />下面把它转换为较低级的 Scheme 代码。先分析一下这里用到的 Haskell 语言的特性：<br /><ol><li>第一句，指定函数取值点。相对于数学中描述函数在某点取值的语法。对于普通的语言，可以用 if 语句代替。</li><li>参数 (x:xs)，是参数领悟特性。指的是，此参数是一个由元素 x 开头，剩余部分为 xs 组成的列表。对于普通的语言，可以用在函数内部绑定参数的分解结果的方法代替。</li><li>列表连接运算符 ++，用 append 函数代替。</li><li>where 语句，后置变量说明。用前置 let 绑定代替。</li><li>类似 [y | y &lt;- xs, y &lt; x] 的列表领悟。对于单元素的领悟，用 filter 函数过滤，过滤规则表示为 lambda 函数，它对于 y &lt; x 返回 true。</li></ol><br />下面是转换后的 Scheme 版本的程序：<br /><pre name="code" class="java">
;; 使用 SICP 中描述的 filter 函数，就不抄在这儿了
(define (qsort ls)
	(if (null? ls) '()
		(let
			((x (car ls))
			(xs (cdr ls)))
			(let 
				((lt (filter (lambda (y) (&lt; y x)) xs))
				(st (filter (lambda (y) (>= y x)) xs)))
				(append (qsort lt) (list x) (qsort st))))))
</pre><br /><br />下面把它转换为我们需要的 JavaScript 代码。先分析一下这里用到的 Scheme 语言的能力：<br /><ol><li>取列表首项函数 car，用 <em>Array</em>[0] 代替。</li><li>取列表剩余项函数 cdr，用 <em>Array</em>.slice(1) 代替。</li><li>判断列表是否为空函数 null?，用 ls == false 代替（JS 的特殊之处）。</li><li>变量绑定语法 let，仅用声明变量语法 var 代替（千万不能不用）。// 在 JavaScript 1.6 中加入了 let 关键字，爽一点；还有列表领悟，无语了！</li><li>过滤器函数 filter。为快一点起见，自己用命令式风格写一个绑定在 Array.prototype 上。</li><li>匿名函数 lambda，不就是匿名 function 嘛！</li><li>列表连接函数 append，用 <em>Array</em>.concat() 代替。</li></ol><br />OK。下面是要用到的、对 JavaScript 1.5 作出的扩展：<br /><pre name="code" class="javascript">
// 把要用到的表达式抽象出来
Array.prototype.head = function () {
    return this[0];
}

Array.prototype.tail = function () {
    return this.slice(1);
}

Array.prototype.filter = function (proc) {
    var tmpArr = [];
    for (var i = 0; i &lt; this.length; i++)
	if (proc(this[i]) == true)
	    tmpArr.push(this[i]);
    return tmpArr;
}
</pre><br />这样就可以写出转换后的 JavaScript 代码了：<br /><pre name="code" class="javascript">
Array.prototype.qsort = function () {
	if (this == false) return []
	var x, xs, lt, st
	x = this.head()
	xs = this.tail()
	lt = xs.filter(function (y) {return y &lt; x})
	st = xs.filter(function (y) {return y >= x})
	return lt.qsort().concat([x], st.qsort())
}
</pre><br />最后试一下：<br />js> [4,7,9,1,3,5].qsort()<br />1,3,4,5,7,9<br />是不是有一种“终于找到组织了”的感觉呢？<br /><br />题外话：<br />对比一下用命令式方法写成的 JavaScript 版的快速排序：<br /><pre name="code" class="javascript">
function qsort (arr, l, u) {
    l = l || 0;
    u = ((u != 0) && (u == undefined)) ? arr.length : u;
    if (l >= u) return;
    var m = l;
    for (var i = l+1; i &lt;= u; i++)
	if (arr[i] &lt; arr[l])
	    arr.swap(++m, i);
    arr.swap(l, m);
    qsort(arr, l, m-1);
    qsort(arr, m+1, u);
}
</pre><br />代码好像少一点，但我没看懂——虽然是我自己写的，3 个月前写的——要是有 bug 我就直接 faint 了...<br />JavaScript 真是一个站在函数式编程语言与命令式编程语言之间的奇特生物——包容任何思想，这也是我钟爱 JavaScript 的一个重要原因。<br /><br />函数式编程语言曲高和寡，谁说的？掌握其思想是重要的，也是容易的；语言是其次，只是表达思想的工具罢了。
          <br/>
          <span style="color:red;">
            <a href="http://lichray.javaeye.com/blog/101055#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Sat, 14 Jul 2007 19:52:44 +0800</pubDate>
        <link>http://lichray.javaeye.com/blog/101055</link>
        <guid>http://lichray.javaeye.com/blog/101055</guid>
      </item>
      <item>
        <title>FP 做数学统计题</title>
        <author>Lich_Ray</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://lichray.javaeye.com">Lich_Ray</a>&nbsp;
          链接：<a href="http://lichray.javaeye.com/blog/93509" style="color:red;">http://lichray.javaeye.com/blog/93509</a>&nbsp;
          发表时间: 2007年06月23日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          今天布置的家庭作业还真是烦人！做完《数学之友》上“统计案例”一章所有习题。我的妈呀，算什么卡方、线性回归，公式繁琐地要命，按计算器都能把人按死。还是让电脑帮我做吧。<br />不用什么 GNUplot、Mathematica，俺用 Haskell，自己动手，丰衣足食！<br /><br />一. 独立性检验<br />卡方就是一个公式：<br /><pre name="code" class="java">
kaf a b c d = 
	((sqr ((a * d) - (b * c))) * (a + b + c + d)) / 
		((a + b) * (c + d) * (a + c) * (d + b))

-- sqr 函数是用来求一个数的平方的，再实现一下：
sqr n = n * n
</pre><br />这样就搞定一种题了！以后算卡方不要太简单，把要检测独立性的四个数据按顺序作为 kaf 函数的参数输入 Hugs 就搞定了！<br />Main> kaf 184 61 91 9<br />11.097807845216 :: Double<br /><br />二. 线性回归分析<br />其实就是求一个 y = a + bx 中的 a，b。把 xn 值、yn 值各表示为一个列表，像这样来使用：<br />Main> lrb [1..8] [5.54, 7.52, 10.02, 11.73, 15.69, 16.12, 16.98, 21.06]<br />2.12142857142857 :: Double<br />Main> lra [1..8] [5.54, 7.52, 10.02, 11.73, 15.69, 16.12, 16.98, 21.06]<br />3.53607142857143 :: Double<br />那么，就可以根据公式把它们抄出来：<br /><pre name="code" class="java">
lrb xl yl = 
	-- 这里要注意一下，数学中的 sigma 是一种记法而非函数
	(sigma
	 (\(x, y) -> (x - xp) * (y - yp)) (zip xl yl)) /
		sigma (\x -> sqr (x - xp)) xl 
	where 
		xp = avg xl
		yp = avg yl

lra xl yl = avg yl - (lrb xl yl) * avg xl
</pre><br />其中，avg 函数用来求一个列表的平均值（这里也就是所有 x 取值的平均值啦），它需要知道列表长度：<br /><pre name="code" class="java">
avg ls = sum ls / len ls

len [] = 0
len (x:xl) = (len xl) + 1
</pre><br />最后一个 sigma 函数的实现也很简单，就是累加嘛，只是写地有些不好看罢了：<br /><pre name="code" class="java">
sigma f (x:xl) = 
	if xl == []
	then f x
	else f x + sigma f xl
</pre><br />重申一遍，这里的 sigma 被表示为函数而非数学形式。那么，猜猜下面的 sigma 的数学形式是什么？<br />Main> sigma (\(x, y) -> x * y) [(1,4), (5,3), (3,8), (2,4)]<br />51 :: Integer<br /><br />三. 线性相关系数<br />线性相关系数 r 的求法也只是个函数，再抄一遍：<br /><pre name="code" class="java">
lrr xl yl = 
		(sigma 
	 (\(x, y) -> (x - xp) * (y - yp)) (zip xl yl)) /
		sqrt ((sigma (\x -> sqr (x - xp)) xl) * 
		 (sigma (\y -> sqr (y - yp)) yl))
	where 
		xp = avg xl
		yp = avg yl
</pre><br />以后这样用就行了：<br />Main> lrr [1..8] [5.54, 7.52, 10.02, 11.73, 15.69, 16.12, 16.98, 21.06]<br />0.987345979074916 :: Double<br />哇！这组数据的线性相关性接近 1，好高啊！<br /><br />四. 结语<br />现在发现 Haskell 还真是实用，这个周末数学作业不愁了，打魔兽去喽！<br />最后给楼下的看客留一个与 FP 没什么关系的小题目：<br />对于一组线性相关数据，往往要求它的所有产生数据—— a、b、r，还有线性回归函数，顺便还要用这个函数再求一下对未来的预期。我如果当真输入 3 遍数据然后手工计算预期岂不是很傻？！想想看怎么让我只输入一遍数据就得到所有的产生数据（设计一下数据结构而已）还有线性回归函数（其实只有这个才有那么一点点技术含量）。
          <br/>
          <span style="color:red;">
            <a href="http://lichray.javaeye.com/blog/93509#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Sat, 23 Jun 2007 19:28:43 +0800</pubDate>
        <link>http://lichray.javaeye.com/blog/93509</link>
        <guid>http://lichray.javaeye.com/blog/93509</guid>
      </item>
      <item>
        <title>函数式语言：我的性能没问题</title>
        <author>Lich_Ray</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://lichray.javaeye.com">Lich_Ray</a>&nbsp;
          链接：<a href="http://lichray.javaeye.com/blog/91076" style="color:red;">http://lichray.javaeye.com/blog/91076</a>&nbsp;
          发表时间: 2007年06月16日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <div class="quote_title">引用</div><div class="quote_div">lichray 将用几天的时间写完本文系列文章的全部，<span style="color: red">剩下的文章将会发布在新建的 <a href="http://wfp.group.javaeye.com/" target="_blank">函数式编程の道 </a> 圈子上</span>。这些文章将并非是从编译原理的角度来探讨函数式编程语言的文章。本文只会浅尝辄止地覆盖函数式编程语言的编译、解释优化手段，并试图让大家相信：使用函数式编程语言/风格，获得的只是表达能力上的大幅提升，而程序性能几乎不会下降。<br />PS: 文章中部分内容可以在《现代编译原理》这本书中找到。<br /></div><br /><br /><strong><span style="font-size: 12pt">一. 内存分配优化</span></strong><br /><br /><strong>1. 高阶函数</strong><br />* 针对不纯的函数式编程语言：<br />不纯的函数式编程语言其实也就是比普通的命令式语言多了这个高阶函数罢了，但性能会造成下降：因为函数必须能具有普通变量的可操作性，比如作参数、返回值，函数必须作为数据的一种、被分配在堆上而不是栈上。<br />在处理在堆上分配函数方面，函数的创建、查找对于现在的算法来说已不是问题，关键是处理重复的函数——数据重复一般是无所谓，因为创建速度太快，而函数就不同了；如果在一个循环中创建函数，那岂不是要了函数式编程语言的命？<br /><pre name="code" class="java">
for (var i = 0; i &lt; 10; i++) {
	var f = function (a) {
		return a * a
	}
	print(f(i))
}
</pre><br />对于“相同的函数”，有必要只保留一个。<br />如果两个函数是“相同”的，需要满足下面两个定义中的一个：<br /><ul><li>二者从程序的源代码文本的同一处获取其函数体</li><li>在使用 eval() 函数从产生程序时，二者从其参数的同一处获取其函数体</li></ul><br />这样简单的定义已经能够处理大部分情况了（比如上面那个例程）。<br /><br />* 针对纯函数式编程语言：<br />纯函数式编程语言的要求更加严格，决不允许出现赋值，但优化起来可以更“放心”。例如，如果编译/解释该语言需要用到中间代码，下面的优化就可以办到：<br /><ul><li>进入执行上下文，重命名内部绑定的变量，用中间代码翻译所有函数体，若存在中间代码相同的函数，认为它们是同一个函数</li><li>在使用 eval() 函数从产生程序时……（略）</li></ul><br />这样一来，重复说明函数体相同（或只有内部绑定的变量命名不同）的函数将被优化——因为变量的绑定不会改变，不用担心调用两个函数造成的“结果”不同。<br /><br /><strong>2. 词法作用域</strong><br />对于支持词法作用域（套嵌函数）的语言来说，因为函数拥有有执行上下文，函数还必须“记住”自己是在哪儿被调用的，在那儿还声明了什么变量，因此需要一个“环境”，雪上加霜。<br />一些书上会这样描述一个词法作用域查找变量绑定位置的过程：<br /><ol><li>查找本级作用域是否包含该变量的绑定</li><li>如果 Result(1) 为假，转到 Step4</li><li>返回本级作用域中该变量的绑定</li><li>如果本级作用域是全局作用域（顶层），报错</li><li>对上级（调用环境）作用域应用 Step1</li></ol><br />言下之意，函数都是被保存在一个链表中的指针所指向的，然后就像访问链表那样查找本级、上级……然后算法复杂度为 O(n)。但你觉得这可能吗？看看下面的 Scheme 代码：<br /><pre name="code" class="java">
(define (fact n)
	(if (= n 0)
		1
		(* n (fact (- n 1)))))
</pre><br />完了，要创建深度为 n 的链表啦，回忆一下 C 里面的 malloc() 的效率，一般 profile 出来的罪魁祸首就是它，不觉得毛骨悚然吗？<br />没有一个真正的函数式编程语言实现是在链表上分配函数的，函数被分配在哈希表中的可能性其实也不大（因为缺乏层次搜索能力），一般都是在经过特殊优化的二叉树上分配。真正的变量搜索算法也不可能那么傻，一般只是把原有的符号表“立体化”。此外，通过先进的活跃性分析技术，更有可能被重复使用的函数所需的逃逸变量将更容易被找到。<br /><br />(To be continued)
          <br/>
          <span style="color:red;">
            <a href="http://lichray.javaeye.com/blog/91076#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Sat, 16 Jun 2007 21:32:57 +0800</pubDate>
        <link>http://lichray.javaeye.com/blog/91076</link>
        <guid>http://lichray.javaeye.com/blog/91076</guid>
      </item>
      <item>
        <title>古老，但很神奇</title>
        <author>Lich_Ray</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://lichray.javaeye.com">Lich_Ray</a>&nbsp;
          链接：<a href="http://lichray.javaeye.com/blog/90813" style="color:red;">http://lichray.javaeye.com/blog/90813</a>&nbsp;
          发表时间: 2007年06月15日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <span style="color: red"><span style="font-size: 12pt">！我的圈子<a href="http://wfp.group.javaeye.com" target="_blank">函数式编程の道</a>已经建立，对 FP 感兴趣的人可以加入，共同探讨这一古老的新事物！</span></span><br /><div class="quote_title">引用</div><div class="quote_div">写在前面的话：Scheme、函数式编程(FP) 已入门者无须阅读。<br />这是我很久以前在自己写的一篇文章，现在又拿出来，是为了在 JavaEye 上造势，让访客们看一看 Scheme & 函数式编程世界的精彩（如果还不知道什么的 FP 的话）——当然，我绝对没有要贬低其它语言（除了 Java）的意思，事实上我最喜欢的语言之一就是文中提到的 C。好了，不罗嗦了，让我们一起来推开 Scheme 那道门吧。造势之后，我会写文章，结束关于函数式编程语言效率问题的讨论，值得期待哦~~<br />PS: 文中的 C、Java 代码可能不可以运行，它们只是用来说明问题的。</div><br /><br /><br /><strong>剧本：</strong>《古老，但很神奇》<br /><strong>人物：</strong>XX大学计算机系的社友 Go4——8呆，F1，老农和小四。<br /><strong>时间：</strong>“晚汇报”时间…<br />（老农在计算机系混的时间不短了，可惜技术一直没长进——连打电脑游戏都“不上档次”（小四语）。这不，昨天打 CS 又被 F1 欺负了，现在正郁闷着呢。）<br /><br />老农（上网无聊中）：这年头，电脑好的人就是吃香啊~~（旁白）俺编程也差，游戏也差，废啦~~有了，上 CSDN.net，找点文章进修一下。<br />F1：老农，怎么样，CS 技术不行啊！好好练吧！<br />小四（推推眼镜）：老农伯伯！算了吧，我看你还是把编程学学好吧，哈哈，你那本《数据结构》好像还是新的吧？<br />（老农翻着 CSDN 上最新的 Blog 文章，忽然眼睛一亮。）<br />小四：嘿，发现什么了？<br />老农（连忙把 Firefox 最小化）：没什么，又不是黄网激动什么？！<br />老农（旁白）：不错，就用这篇文章 K.O.他们。<br />老农（满脸堆笑）：嘿，你们仨过来，我在网上发现一道数据结构方面的面试题目，想不想试试？<br />（正在上铺捧着 SIC P发呆的8呆忽然从发呆状态切换到亢奋状态，人啊~~）<br />8呆：少废话，快说！<br />老农（奸笑中）：设计一个函数 visit_tail，要求通过一次遍历找到链表中倒数第n个节点，然后从它开始用函数 func 例遍后面的所有链表元素，链表可能相当大，可使用辅助空间，但是辅助空间的数目必须固定，不能和n有关。还有，为了简化问题，可以不给出链表的其它操作函数，比方说内存分配。<br />8呆（再次切换回发呆状态）：无聊…这也配叫题目…<br />老农（怒）：那你做啊！<br />（8呆在他的 SICP 上写了点什么，然后很潇洒地离开了寝室）<br />老农（专向另两个人）：既然他不参加，那我们三个比吧。<br />小四、F1（信心实足）：那现在开始计时吧。<br />…………<br />（老农把刚才背下来的代码改了改，花了3分钟抄在终端里）<br />老农（觉得时机移到）：我好了，你们呢？<br />小四（大喊）：解决解决！<br />F1：恩，我也好了，只是还没做单元测试。<br />（老农、小四（旁白）：我说你是编程还是写软件啊~~）<br />（3个人凑在老农的电脑上开始比拼）<br />老农：你们看，我是用 C 写的，已经测试过的。思想是，用两根指针，第一根先出发，相距 n 步后第二根出发。然后同时步进，直到第一根指针达到末尾，然后用 func() 函数对第二个指针开始的子链表进行例遍。<br />（小四和 F1 仔细一看，大笑不止。老农的代码如下（注释是笔者所加，其实是F1和小四看到各句时的反应）：）<br /><pre name="code" class="java">
typedef struct{ //哟，终于不用struct Node了，
    int data;
    Node * next;
} Node;
Node * visit_tail(iNode * head,int n,void (* func)(int cur)){ 
    //K&R 时代的函数指针，见到老佛爷啦！！Node *pfirst; 
    Node *psecond; //吓，终于用到匈牙利命名法了（老农：#!?%）
    pfirst=head;
    for(counter=0;counter&lt;n;counter++){
        if(pfirst->next)
            pfirst=pfirst->next;
        else
            return NULL; //什么破编程风格，真实版初学者啊
    }

    psecond=head;
    while(pfirst!=NULL) {
        pfirst=pfirst->next;
        psecond=psecond->next;
    }
    for(int i=0;i &lt; n; i++)
        (* func)(psecond.data);
        psecond=psecond->next;
    }
} //靠，一个函数解决所有问题，高耦合啊！
</pre><br />老农（再怒）：小四少罗嗦！你的呢！拿出来啊？！<br />小四：在这儿，好好学学吧！<br />（大家一看，小四也用了 C，但…好漂亮啊。代码如下（注释是小四的解释）：）<br /><pre name="code" class="java">
typedef T int
    //没有 C++ 一样“泛型”！

typedef struct {
    T data;
    Node * next;
} Node;

typedef struct {
    Node * pre;
    Node * curr; //当前结点与 List 绑定，不但可以分步例遍，还能保证正确初始化
} List;

void init_curr (List * l) {
    l->curr = l->pre; //什么叫“编程风格”，知道不？！
}

void visit (List *l, void (* func)(T data)) {
    while (l-curr) { //少用 !=NULL，这可是《The C Programing Language》上说的！
        (* func)(l-curr->data);
        l-curr = l->curr->next;
    }
} //使用内联结点的公用访问函数，降低耦合

void index (List * l, int n) {
    if (n) return NULL; //结点下标从1开始
    init_curr(l);
    if (n > 0) {
        for (int i = 0; i &lt; n; i++) {
            curr = l->curr->next;
        }
    } //小四（自我陶醉）：漂亮啊
    if (n &lt; 0) {
        tmp = l->curr;
        for (int i = 0; i &lt; n; i++) {
            tmp = tmp->next;
        }
        while (tmp) {
            l->curr = l->curr->next;
            tmp = tmp->next;
        }
    }
    return l->curr; //方便用户，增强鲁棒性能
}

void visit_tail (List * l, int n, void (* func)(T data)) {
    index(l, -n);
    visit(l, func);
} //多清爽
</pre><br />老农（口吐白沫）：我的挽回面子计划就这么，完了…<br />小四（偷笑）：你还是面对现实吧…<br />F1：小四你先别得意，我的你们还没拜读过呢！<br />（大家跑到 F1 电脑前一看，先是被长度吓了一跳，然后，无语。）<br /><br /><pre name="code" class="java">
package datastruct.fifi.com.baidu.hi; //加入我的数据结构Java包

//当然只有包内成员才能创建Node对象
protected class Node {
    private Object data; //多态性，让你们的 typedef 去死吧！
    private Node next;

    //构造函数
    Node (Object data) {
        this.data = data;
    }

    //获取下一个元素
    public Node next () {
        return this.next;
    }

    //变换下一个元素，用户不能调用
    protected void setNext (Node o) {
        this.next = o;
    }

    //取数据
    public Object getData () {
        return this.data;
    }

    //换数据
    public void setData (Object data) {
        this.data = data;
    }

    //根据Effective Java的最高指导，要重写toString()方法
    public String toString () {
        return this.data.toString();
    }
}

//自定义异常类，数据结构下标越界
public class IndexOutOfRangeException extend IndexOutOfBoundsException {
    public IndexOutOfRangeException (int lower, int upper, int index) {
        super("Lower: " + lower +", Upper: " + upper + ", index: " + index);
    }
}

//用于被继承的访问类
public class Visitor {
    public operation (Object o) { //访问操作
        System.out.println(o);
    }
}

public class List {
    private Node preFix;
    private Node current;
    private int size;

    List () {
        current = preFix = new Node();
        size = 0;
    }

    //定位函数
    private void index (int i) throws IndexOutOfRangeException {
        if (i > size || i &lt; -size-1) {
            thows new IndexOutOfRangeException(0, size+1, i);
        }
        this.init();
        if (i >= 0) {
            for (int j=0; j &lt; i; j++) {
                current = current.next();
            }
        }
        if (i &lt; 0) {
            i = - i - 1;
            for (int j=0; j &lt; i; j++) {
                current = current.next();
            }
        }
    }

    //基本访问函数,需要一个继承自Visitor类的访问工具
    public visit (Visitor v) {
        while (current != null) { //Java拒绝看不懂的 while(curr) ！
            v.operation (current.data());
            current = current.next();
        }
    }

    //访问后n个元素
    public visitTail (Visitor v, int n) {
        this.index(-n);
        this.visit(v);
    }
}
</pre><br />小四：靠！过分了！Java真是让程序员远离算法的祸首啊！爱好算法的人千万离它远些！<br />老农：反正我的“扬眉吐气”计划是泡汤了，你们想怎么讨论就怎么讨论吧。。。<br />小四：>_&lt; 。。。<br />F1：we 得意。<br /><br />（3人正在争执着，忽然门“吱”的一声（什么破门）开了，8呆走了进来。）<br />8呆：吵什么呢，比完了没？我是第一吧？<br />F1、小四、老农：什么啊，你不是自顾自走了啦？！<br />8呆（诧异）：我走之前已经写好啦。<br />（8呆拿来他的 SICP，递给三人。只见上面写了两行 Scheme 代码：）<br /><pre name="code" class="java">
(define (list-visit-tail func n l)
    (map func (list-tail l (- (length l) n))))
</pre><br /><br />（编辑公曰：上面这段 Scheme 代码滴意思斯酱紫滴：先定义一个名为 list-visit-tail 的过程，然后用内置宏 list-tail 取 list 的后 n 位组成 list 返回，再用操作 map 进行例遍。过程 map 是列表基础操作，实现也就3行；它不改变列表本身，而是返回操作后的列表。不改变状态是函数式编程的精髓。）<br />（完）<br /><br /><strong>结语：</strong><br />	这个题目其实对于任何函数式编程语言来说都是一两句话。Scheme、Haskell它们都来自于世界上第二古老的语言 Lisp，但它们的思想博大精深——基于 lambda 演算理论的函数式编程，古老，但很神奇。<br /><br /><strong>参考资料：</strong><br />	老农说的“这篇文章”：<a href="http://blog.csdn.net/yuetiantian/archive/2006/10/19/1341086.aspx" target="_blank">一次遍历找链表倒数第n个节点</a><br />		<br />	8呆的书SICP：<a href="http://mitpress.mit.edu/sicp/" target="_blank">Structure and Interpretation of Computer Programs</a><br />		<br />	命令式语言的劣势：<a href="http://www.nirvanastudio.org/java/why-java-and-almost-every-other-programming-language-sucks.html" target="_blank">为何 Java（以及很多其他编程语言）令人不爽</a><br />		<br />	函数式语言的优势：<a href="http://www.nirvanastudio.org/wp-content/uploads/2006/08/why%20functional%20programming%20matters.html" target="_blank">为什么函数式编程至关重要？</a><br />		<br />	Scheme 入门推荐书：<a href="http://www.ccs.neu.edu/home/dorai/t-y-scheme/t-y-scheme-Z-H-1.html" target="_blank">Teach Yourself Scheme in Fixnum Days</a><br />		<br />	如果你还没学过编程的话：<a href="http://www.htdp.org/2003-09-26/Book/curriculum-Z-H-1.html" target="_blank">How to Design Programs</a><br />	<br />	如果你对函数式编程语言的效率有疑虑：<br />函数式语言：我的性能没问题 <a href="http://www.javaeye.com/topic/91076" target="_blank">（一）</a><br /><br />	其它有关 Scheme 的东东详见：我的 Scheme 学习笔记 (<a href="http://let-in.blogspot.com/2006/09/scheme.html" target="_blank">1</a>  <a href="http://let-in.blogspot.com/2006/09/scheme_27.html" target="_blank">2</a>  <a href="http://let-in.blogspot.com/2006/09/scheme_28.html" target="_blank">3</a>  <a href="http://let-in.blogspot.com/2006/09/scheme_29.html" target="_blank">4</a>  <a href="http://let-in.blogspot.com/2006/10/scheme.html" target="_blank">5</a>)<br />		（官网可能暂时关闭）
          <br/>
          <span style="color:red;">
            <a href="http://lichray.javaeye.com/blog/90813#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Fri, 15 Jun 2007 19:31:13 +0800</pubDate>
        <link>http://lichray.javaeye.com/blog/90813</link>
        <guid>http://lichray.javaeye.com/blog/90813</guid>
      </item>
      <item>
        <title>无类语言的OOP（JavaScript描述）</title>
        <author>Lich_Ray</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://lichray.javaeye.com">Lich_Ray</a>&nbsp;
          链接：<a href="http://lichray.javaeye.com/blog/89554" style="color:red;">http://lichray.javaeye.com/blog/89554</a>&nbsp;
          发表时间: 2007年06月12日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          本文以 JavaScript 语言为例，介绍了无类面向对象语言中实现各种面向对象概念的方法。值得注意的是，下面所说的并非“奇技淫巧”，其中的大部分都是计算机科学家们在设计无类语言时就已经确立了的模式，少部分是我借鉴其它语言的经验已经对前辈们思想的理解给出了完备化技术。<br />阅读本文至少需要对 JavaScript 语言“特别”的对象机制以及函数的运行上下文有所了解。如果您还对 JavaScript 对象知之甚少，可以查看附件中我翻译的 ECMA262 v3 中 4.2.1 Object 这一节；如果对 Lambda 演算不了解，建议去看 SICP :)。<br /><br />一. 基础：<br />建立类。只需声明一个函数作为类的构造函数即可。<br /><pre name="code" class="javascript">
function Light (light) {
	//填充对象属性
	this.light = light ? light : 0
	this.state = false
	
	//对象方法。
	//放心，JavaScript 没傻到给每个对象都真去分配一个函数的地步
	this.turnOn = function () {
		this.state = true
	}
}
</pre><br />创建实例。通过下面的代码创建一个新的电灯：<br /><pre name="code" class="javascript">
new Light(100) instanceof Light
js> true
</pre><br />这个新的电灯现在是匿名的，接下来可以在任何表达式中使用它。当然，最常用的做法是把一个名字绑定上这个对象。<br />访问实例属性。<br /><pre name="code" class="javascript">
//访问属性
new Light(100).light
js> 100
anOnLight = new Light()
//调整属性
anOnLight.state = true
</pre><br />匿名类。顾名思义，这个类没有名字（精确的说是构造函数没有名字）。就像这样：<br /><pre name="code" class="javascript">
aLight = new (function (light){
	this.light = light ? light : 0
	this.state = false
)(90)
</pre><br />类属性；类函数。顾名思义，一个类自身绑定的属性、函数，被所有类的实例可见，但不可直接使用。<br /><pre name="code" class="javascript">
//类属性
Light.SIZE = 5
//类函数
Light.newInstence = function (arg) {
	//这么简单的 Factory 模式
	//this 指向函数运行所在名字空间的上级
	return new this(arg)
}
</pre><br />想利用实例使用类的属性用下面的办法。函数调用类似：<br /><pre name="code" class="javascript">
anOnLight.constructor.SIZE
js> 5
</pre><br />类方法。真正意义上的“方法”<br /><pre name="code" class="javascript">
Light.prototype.turnOff = function () {
	this.state = false
}
anOnLight.turnOff()
anOnLight.state
js> false
</pre><br /><br />二. 进阶<br />单继承。一个类扩展另一个类的所有能力。<br /><pre name="code" class="javascript">
function PhilipLight (price) {
	this.price = price
}
//事实上是建立了一个匿名的 Light 实例，然后将其能力反映给 PhilipLight
//飞利浦灯泡的亮度默认为100。这种继承模式很有意思。
PhilipLight.prototype = new Light(100)
myLight = new PhilipLight(12)
myLight.price
js> 12
//类方法照用。对象方法也照用。
myLight.turnOn()
myLight.state
js> true
</pre><br />可以把单继承作为一个 Object 类的能力保留下来，如果不强求默认值的话：<br /><pre name="code" class="javascript">
//把那些垃圾的库抛在脑后，让它们见识见识什么叫优雅。
Object.prototype.extend = function (aClass) {
	this.prototype = new aClass
}
PhilipLight.extend(Light) //No problem
</pre><br />多继承。我可以很明白的说，JavaScript 办不到。因为想在单继承链上实现多继承是不可能的。不过，这并不是说 JavaScript 面向对象机制不能达到多继承那样的表现力：装饰模式、Mixin 这些更强大的机制都是能办到的。<br />Mixin。漂亮地实现 Mixin 的前提是访问拦截器（getter 和 setter）。<a href="http://developer.mozilla.org/cn/docs/New_in_JavaScript_1.6" target="_blank">JavaScript 1.6</a> 之前没有这种东西，需要修改编程习惯——这不是我们想要的。<a href="http://developer.mozilla.org/en/docs/New_in_JavaScript_1.7" target="_blank">JavaScript 1.7</a> 中加入的只是对特定消息的访问拦截器（现已在出现在 <a href="http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Guide:Creating_New_Objects:Defining_Getters_and_Setters" target="_blank">1.5</a> C 实现中）支持所以我们只能稍微改变一下编程风格。先说明一下如何对某个对象应用其它类的函数。<br />泛型。JavaScript 1.5 中，我们可以用函数对象的 call() 方法或 apply() 方法对该对象应用来自其它类的函数：<br /><pre name="code" class="javascript">
//Light 也是一种商品
function Product (price) {
	this.price = price
	//买 num 件商品需要的钱
}
Product.prototype.buySetOf = function (num) {
	return this.price * num
}
//那么对于同样有 price 属性的飞利浦灯泡，我们可以这样计算买10个灯泡要多少钱：
Product.prototype.buySetOf.call(myLight, 10)
js> 120
//apply 的第二个参数是被 call 的参数列表
Product.prototype.buySetOf.apply(myLight, [10])
js> 120
</pre><br />类的半自动混合。<br /><pre name="code" class="javascript">
Object.prototype.mixin = function (aClass) {
	//这里用到的技术下文中讲解
	this.prototype.app = function (func, args) {
		//func 是消息字符串
		if (this[func] != undefined)
			return (this[func].apply(this, args))
		return (aClass.prototype[func].apply(this, args))
	}
}
PhilipLight.mixin(Product)
myLight = new PhilipLight(12)
myLight.app('buySetOf', [10])
js> 120
</pre><br />对象的半自动混合。对象当成另一个对象使用，类似的方法：<br /><pre name="code" class="javascript">
Object.prototype.able = function (anObject) {
	this.app = function (func, args) {
		//func 是消息字符串
		if (this[func] != undefined)
			return (this[func].apply(this, args))
		return (anObject[func].apply(this, args))
	}
}
//这个用法弱智了点，但确实能说明问题
myLight.able(new Product)
myLight.app('buySetOf', [10])
js> 120
</pre><br /><br />三. 补完<br />这一章讲解 4P 的实现。<br />包(package)没什么好说的，通读一遍 Prototype.js，看看作者是如何使用 JavaScript 对象描述程序结构的，就什么都知道了。这可比什么 interface 强多了。<br />公有(public)权限。Pass.<br />受保护的(protected)权限。如果你使用了 JavaScript 对象来描述程序结构，那么，其中每个类中的函数会自然获得 protected 权限——因为，使用它们都需要包名或者 with 语句。<br />私有(private)权限。不像 Python 等等语言，它们事实上是不存在的私有权限；JavaScript 使用 Lambda 演算中的逃逸变量原理实现私有权限。换个例子：<br /><pre name="code" class="javascript">
function Desk (height) {
	//对于一个符合标准的实现，这里的 var 关键字可以省略
	var height = height ? height : 0
	var weight = 0
	//下面的东西对于 Java 程序员来说很熟悉 :)
	this.getHeight = function () {
		return height
	}
	this.setHeight = function (num) {
		height = num
	}
}
deak = new Desk(34)
deak.getHeight()
34
deak.setHeight(45)
deak.getHeight()
45
desk.height
ReferenceError line 1：desk.height is not defined
</pre><br />此时的 height 就是逃逸变量，从 Desk 函数中以作为对象上绑定的函数的环境上绑定的变量“逃”了出来（这句话有些拗口，不过的确如此）。对于直接由构造函数参数引入的变量，也可以作为私有属性。类似的，还可以有私有函数——直接将函数定义写入构造函数即可。<br /><br />四. 小结<br />以 Self、JavaScript 为代表的无类语言在用函数式风格解释面向对象思想方面作出了巨大进步，无论是灵活性还是强大程度都不是那些关键字一大堆的语言可与之相媲美的。如果我有空，可能还会来介绍一点 E 语言方面的思想，那才是真正无敌的无类语言啊。
          <br/>
          <span style="color:red;">
            <a href="http://lichray.javaeye.com/blog/89554#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Tue, 12 Jun 2007 22:21:51 +0800</pubDate>
        <link>http://lichray.javaeye.com/blog/89554</link>
        <guid>http://lichray.javaeye.com/blog/89554</guid>
      </item>
      <item>
        <title>胡侃：面向对象思想的进化</title>
        <author>Lich_Ray</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://lichray.javaeye.com">Lich_Ray</a>&nbsp;
          链接：<a href="http://lichray.javaeye.com/blog/89241" style="color:red;">http://lichray.javaeye.com/blog/89241</a>&nbsp;
          发表时间: 2007年06月11日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <div class="quote_title">引用</div><div class="quote_div">本文作者还是那个无名小辈 lichray。他在考查了一些语言和历史之后，觉得有必要谈一谈自己对面向对象思想的一些诡异的想法。文中会提到许多编程语言，不过当然了，重点在于思想，文章不是用来推销语言的。</div><br /><br /><span style="color: indigo">面向对象编程思想的提出已经不是几年而是几十年了，考查其思想的变化，一方面是对现有语言的一些评判，另一方面，也算是对前辈计算机科学家的缅怀。 ——题记</span><br /><br />Kristen Nygaard在1962年发明的 Simula 语言现在被认同为世界上第一种明确实现面向对象编程中某些“必要”元素（比如 class）的语言。Simula 是从 Algol 发展来的，可以说，是一种增加了 class 这个数据类型的 Algol，并将参数传递的默认模式从“按名调用”换成了“按引用调用”，还提出了根据类型确定初始化过程的方法。<br />从 Simula 的时代开始，科学家们在解决软件复杂度方面的思路开始“异常开阔”——当然也有资深的老派牛人们不这么认为。比方说 Peter Norvig，写了<a href="http://norvig.com/design-patterns/" target="_blank">Design Patterns in Dynamic Programming</a>一书来反驳。他认为设计模式早已体现在以 Lisp 为首的一批语言中了，根本不需要什么面向对象。但我还是跟从偶像 Alan Kay 的观点。某些时候，把一种特定的编程风格做进语言里也是必要的。<br />言归正传。Simula 之后的语言开始试图把 class 作为编程的基础，并提出了一系列出色的、基于类的编程风格，最终确定了“万物皆对象”这一面向对象理论的“终极思想”。于是，Alan Kay 的 Smalltalk 在 70年代诞生了。它是世界上第一个真正把面向对象作为程序组织基础手段的编程语言。它首次明确实现了“消息”和“继承”这两个重要概念，对于“封装”和“多态”也给出了里程碑式的解决方案。在 Smalltalk 程序中，一切程序元素（除了词法元素）都是对象，一切操作都是消息。而且，Smalltalk 的实现本身就是用 Smalltalk 写成的，这也就意味着，对于一个 Smalltalk 程序来说，它的底层也是以面向对象为基础的。直到现在，也只有 Self 语言有资格在面向对象的程度方面与 Smalltalk 一较高下。<br />Smalltalk 在成为里程碑的同时，也成为了分水岭。Smalltalk 之后的面向对象语言的发展方向分成了两条路：一是继续把 Smalltalk 中封装的概念扩大化，为程序员添置更多的约束，以支持所谓的“大规模开发”的需要；另一方面，一些人主张继续理解面向对象的原理，为这一思想添加数学上解释，而不仅仅是“拿来主义”，使程序员更不易出错或“分神”。<br />正像大家所猜测的那样，前者指的是以 C++ 为代表的、坚持以命令式语言风格去实现面向对象的“有类”语言，后者是以 Self 为代表的、准备以函数式语言风格去理解面向对象的“无类”语言。<br />有类语言中，C++ 是曾经的“蔚然大宗”。它最初的名字是 C with class，很显然，一开始只是想对 C 作出像 Simula 之于 Algol 那样的修改。但是，C++ 诞生的时候（1983年），也是 Smalltalk 80 产生的时代，也是 Ada 语言正在开发的时代。在享受了 Smalltalk 提出的类方法、对象方法、私有方法等等语言设施之后，C++ 提出了3个为后来的有类语言广泛采用的关键字：private，public，protected。不避讳的话，可以简称3P。这种区分相对于 Smalltalk 来说略少了一点，但事实证明，在实际的抽象中是够用的。另外，C++ 还从 Algol68 （这只是一个失败的旧事物罢了）那儿学来了运算符重载，还有 Ada 中的泛型和程序包。堆砌了太多特性的 C++ 显得有些不堪重负，而且失败的教育（总是拿它当 C 的升级版讲解！）、失败的实现（尤以 Visual C++ 为甚）也是它后来在开源界不那么受欢迎的重要原因。C++ 之后的有类语言开始了消减多余特性，理清思路的努力。<br />紧接着 C++ 之后出现的语言主要有 Objective Pascal 和 Objective-C。后者是 Mac 公司的拳头开发语言，前者是什么？就是 Turbo Pacal 中的 Pascal 语言。二者试着给古老的语言们添加新的元素。前者只是很客气的添加了 new 和 unit，特性不足；后者的转型是成功的，但说句题外话：想象一下 C 的基础上直接加上 Smalltalk 的消息传递语法，这就是 Objective-C 的语法设计。<br />Java，原名 Oak。这个名字现在已经处于“无敌”状态，去年已经在语言排行榜上 K 掉 C 当上了老大。Java 没有什么新东西，正如上文所说，只是一个消除了多余特性后的产物。成功的原因，一是时代的召唤，而是官方实现的基于虚拟机的跨平台特性。虚拟机早已不是什么新事物了，不过有大公司出来宣传确实是新鲜事。也许是受了微软的影响，大多数普通程序员更愿意那种喜欢被某个大公司“罩着”的感觉，于是纷纷用起了 Java。历史就是这样，听不到你的嗟呀。<br />Java 火起来了，各大计算机方面的媒体上充斥着 Java、OOP 等等的字眼，仿佛面向对象只剩下 Java 一种模式。各种语言纷纷改头换面，以适应“新时代”的要求：Perl 加上了4P（package 也算一个）；Objective Pascal 摇身一变成了 Boject Pascal，再改名 Delphi；Basic 名字前加上了 Visual，后来后面又加了 .Net；C++ 把 ++ 改成了 # 号；就连一向对面向对象嗤之以鼻的 PHP 都被迫升到了 4.0、5.0；我的“母语”Action Script”版本也升到了2.0、3.0。<br />在有些人眼中，这是一场“意义重大”的变革；但在我眼中，这是一场灾难。<br />因为，在某个角落，无类语言们正在为寻找面向对象的数学理论基础而努力。为什么要寻找数学理论基础？这个问题早在70吗=年代就已经不是个问题了，而现在居然还有人不明所以。有类语言和无类语言的区别，就像是命令式与函数式之间的区别，就像是图灵机和 Lambda 演算之间的区别，就是一字一次的编程和面向整体操作的区别，就是 Bug 多和少之间的区别，就是单线程与高效并发之间的区别，就是必将灭亡的旧事物和必将走向辉煌的新事物之间的区别……这么多，还不够吗？<br />无类语言从已有的 lambda 演算理论中寻找适合解释面向对象思想的部分。1986 年研究完成的 Self 语言首先抛弃了 class 关键字，从对类和对象这两个基本问题上做文章。“万物皆对象”，类就是对象，但用它可以产生对象；怎么产生？通过复制已有对象产生。这种手段称为“基于原型的面向对象程序设计”。Self 之于无类语言，相对于 C++ 之于有类语言。它是无类语言中的蔚然大宗。但可惜的是，Sun 公司的 Self 实现几乎没有进步；Smalltalk 可能因为受够了 Java 之流自称继承了自己，认为 Self 是它唯一的“知心朋友”，发起了一个叫做 Morphic 新体系，算是对经典的延续。<br />Self 之后，1993年出现了 Lua。Lua 是无类语言的一个特别“函数式”的版本，在继承了 Scheme 所有思想之后，消灭了专有 List 这一数据结构，全以哈希表代之，并出色地解释了许多程序表示上的一些不太明朗的问题，还区分了消息发送和普通函数调用。Lua 不论从那个角度（科学或者商业）来看都是成功的，是无类语言中的佼佼者。<br />Lua 之后出现了 JavaScript，原名 LiveScript（1995），国际标准收录名为 ECMAScript。JavaScript 与 Java 诞生于同一时代，生不逢时的同时又生而逢时。说它生不逢时，是因为在 Java 的盛名之下，不负众多程序员的“众望”，被他们指责为“假面向对象”；说它生而逢时，是因为，它总算没在一浪高过一浪的 class + 4P 的嚷嚷中倒下，成为我们最为熟知无类语言。它实在是太优秀然而又太谦虚了：它秉承了 Lisp 家族的一贯传统，能够用数据表示程序本身（JSO）；它又有足够的函数式编程特性，但它谦虚地称之为 function；它用复制对象再用 new 关键字 apply 构造函数的方式漂亮地解释了类和对象的关系；相对于 Lua 取消了消息发送和函数调用之间的界限；在没有任何 4P 关键字的情况下还能用逃逸变量理论实现 4P 的所有特性（不要被 dojo 之类的库蒙蔽了双眼）。几乎是一个完美的 Self 的继任者，但还是那句话，真正会 JavaScript 的人不多啊。<br />至此，无类语言的发展似乎已经很令人满意了，理论似乎已经成熟，只剩下一个问题：函数本身是一个什么样的对象？E 语言（1987，提出很早，实现太晚）回答了这个问题。答案很简单。函数是对象的一种，对象和方法都是 lambda 算子，如果你愿意，又可以把函数的函数体再表示为对象的一个方法！看起来这个答案不那么起眼，但它确实让人们领教了 lambda 演算在抽象层次上的嚣张。同时，E 语言还大胆地加入了对象继承的概念——一个对象可以像类继承一样继承其它对象的属性——因为这仅仅是 lambda 算子的扩展罢了！<br />但是，无类语言的进步不会就此结束，还有更多、跟有力量的无类语言产生，比如 Scala，一个给无类语言添加了 ML 中的静态多态类型判定和惰性运算的变态……也许真的有一天，面向对象，甚至是面向策略，真的能和函数式编程达到和谐。<br />历史就是这样一个聋子，听不到你的嗟呀。不过，但愿后人会从这些嗟呀中借鉴到很多。<br /><br />注：还有两个很牛语言没提到：Ruby 和 Python。其中前者学 Smalltalk 学地精神分裂，去掉了 Smalltalk 的强大 IDE 之后居然一炮走红；Python 是个不知道该站在那一边的语言，既没有 3P 又不知道逃逸变量或 block 是什么，惨兮兮的。
          <br/>
          <span style="color:red;">
            <a href="http://lichray.javaeye.com/blog/89241#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Mon, 11 Jun 2007 20:53:12 +0800</pubDate>
        <link>http://lichray.javaeye.com/blog/89241</link>
        <guid>http://lichray.javaeye.com/blog/89241</guid>
      </item>
      <item>
        <title>尝试用Python实现消息传递编程风格</title>
        <author>Lich_Ray</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://lichray.javaeye.com">Lich_Ray</a>&nbsp;
          链接：<a href="http://lichray.javaeye.com/blog/88435" style="color:red;">http://lichray.javaeye.com/blog/88435</a>&nbsp;
          发表时间: 2007年06月09日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <span style="font-size: 9pt"><div class="quote_title">引用</div><div class="quote_div">本文站在一个难以名状的角度上研究了 Python 语言中消息发送的编程风格。原文是使用 JavaScript 描述的。文章作者 lichray 只把文章的上篇改成了 Python，因为下篇对于 Python 来说是没有意义的。lichray 是个 ECMAScript 的狂热追随者，mozilla.org 邮件列表里的无名潜水员。<br />文章中使用了 Python 解释器，行开头有 ">>>" 表示那是输入，输入下一行没有这个标记的表示解释器回馈消息。省略了多余的回馈。<br />PS: 文章用处不大。</div></span><br /><br /><span style="font-size: 9pt"><strong>一. 对象和消息</strong><br />考虑一下我们平常怎么说话的。我们叫某某人做某事，用下面的句式：<br />forest run!<br />其中"!"是语气的标志，对于编程语言来说是没有意义的，全部换成"."：<br />forrest run.<br />不知道如果我告诉大家上面这句话就是 Smalltalk 语言中一个合法语句大家会怎么想。好了，不谈这个。这样我们就得到了一种语法，"宾"谓结构：<br /><pre name="code" class="python">
ObjectVerb::
	Object Verb.
</pre><br />如果让它支持多个 Verb，比如<br />forrest run, jump, stop.<br />可以扩展成这样：<br /><pre name="code" class="python">
ObjectVerb::
	Object VerbList.
VerbList::
	Verb
	Verb , VerbList
</pre><br />很明显，对于 Python 来说，上面的 BNF 不可能和任何一个产生式匹配。问题出在哪儿？我们要帮 Python 指定，谁是 Object，谁是 Verb。鉴于 Object 只有一个，Verb 有多个，我们可以用括号来区分它们，然后把最后那个句号去掉：<br /><pre name="code" class="python">
ObjectVerb::
	Object ( VerbList )
</pre><br />这样上面的那句话就变成了下面的形式：<br />forrest (run, jump, stop)<br />很像函数调用，是吧？不过还有一个问题，现在这些 Verb(s) 对于 Python 来说是“裸词”（Perl 语），我们可以避开再去定义这些标识符，用字符串代替；最后再说明一下 Object 是什么：<br />forrest ('run', 'jump', 'stop')<br />那么现在我们第一个“模仿”自然语言的程序版本出现了，加上下面针对 Python 的文法：<br /><pre name="code" class="python">
Object::
	Identifier
Verb::
	StringLiteral
</pre><br /><br /><strong>二. 实现消息传递</strong><br />有了文法，一切都好办。看得出来，我们下面的工作是定义能创建一个新 Object 的函数，函数中有一些动作，产生的新 Object 是一个能处理这些消息的函数。创建 Forrest Gump 的函数还可以创建 Tom，Mike 等等；他们都是 People：<br /><pre name="code" class="python">
def People ():
	def run ():
		print("I'm running!")
	
	def jump ():
		print("I'm jumping!")
	
	def stop ():
		print("I can't stop!")
		
	def _dispatch_ (verb):
		if verb == 'run': run()
		elif verb == 'jump': jump()
		elif verb == 'stop': stop()

	return _dispatch_
</pre><br /><br />当然，我们可以用 lambda 和 eval() 替换显式的 _dispatch_ 函数。需要注意是，使用 eval() 要先保存上层执行环境：<br /><pre name="code" class="python">
def People ():
	def run ():
		print("I'm running!")
	
	def jump ():
		print("I'm jumping!")
	
	def stop ():
		print("I can't stop!")
		
	local = locals()
	return (lambda verb: eval(verb, globals() ,local)())
</pre><br />	<br />Ok。现在我们来试一