<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet href="/xsl/rss.xsl" type="text/xsl" media="screen"?>
<!--åå®¢åå«æ¥å¿ï¼æ¯äºèç½ä¸ä¸ç§ä¸ªäººä¹¦ååäººéäº¤æµçå·¥å·ãéè¿åå®¢è®°å½ä¸å·¥ä½ãå­¦ä¹ ãçæ´»åå¨±ä¹çç¹æ»´ï¼çè³è§ç¹åè¯è®ºï¼ä»èå¨ç½ä¸å»ºç«ä¸ä¸ªå®å¨å±äºèªå·±çä¸ªäººå¤©å°ãå»ºç«åå®¢ï¼æå©äºä»äººå¨äºèç½ä¸æ´å¥½å°è®¤è¯æ¨ï¼ä¹æå©äºæ¨æ´å¥½çåå«äººäº¤æµãåå®¢ä¸çæ¯ä¸ä¸ªå¼æ¾åå±äº«çä¸çãæçåå®¢ç±æçå¬å¸å¼åï¼ç®åæ¯åè´¹æå¡ã--> 
<rss version="2.0" 
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	>

	<channel>
		<title>WEB设计和开发</title>
		<link>http://hxjblog.blog.sohu.com/</link>
		<description><![CDATA[WEB设计和开发]]></description>
		<pubDate>Mon, 6 Oct 2008 11:32:17 +0800</pubDate>
		<generator>搜狐博客</generator>
		<image>
			<title>http://blog.sohu.com</title>
			<url>http://js.pp.sohu.com/ppp/blog/images/common/logo_150_60.gif</url>
			<link>http://blog.sohu.com/</link>
			<width>100</width>
			<height>43</height>
			<description>搜狐博客</description>
		</image>
		<item>
			<title>函数式编程</title>
			<link>http://hxjblog.blog.sohu.com/101319420.html</link>
			<comments>http://hxjblog.blog.sohu.com/101319420.html#comment</comments>
			<dc:creator>WEB设计和开发</dc:creator>
			<pubDate>Mon, 6 Oct 2008 11:32:17 +0800</pubDate>
			<category>经典文章</category>
			<guid>http://hxjblog.blog.sohu.com/101319420.html</guid>
			<description><![CDATA[<div>今天看到一个文章:</div>
<div>
<p>原文链接：<a href="http://www.defmacro.org/ramblings/fp.html"><u><font color="#0000ff">Functional Programming For The Rest of Us</font></u></a><br />原文作者：Vyacheslav Akhmechet<br />翻译：lihaitao (电邮: lihaitao在gmail.com)<br />校对：刘凯清 
</p><p>程序员拖沓成性，每天到了办公室后，泡咖啡，检查邮箱，阅读 RSS feed，到技术站点查阅最新的文章，在编程论坛的相关版面浏览公共讨论，并一次次地刷新以免漏掉一条信息。然后是午饭，回来后盯了IDE没几分钟，就再次检查邮箱，倒咖啡。最后在不知不觉中，结束了一天。 
</p><p>不平凡的事是每隔一段时间会跳出一些很有挑战性的文章。如果没错，这些天你至少发现了一篇这类文章&mdash;&mdash;很难快速通读它们，于是就将之束之高阁，直到突然你发现自己已经有了一个长长的链接列表和一个装满了PDF文件的目录，然后你梦想着到一个人迹罕至的森林里的小木屋苦读一年以期赶上，要是每天清晨你沿着那里的林中小溪散步时会有人带来食物和带走垃圾就更好了。 
</p><p>虽然我对你的列表一无所知，但我的列表却是一大堆关于函数式编程的文章。而这些基本上是最难阅读的了。它们用枯燥的学院派语言写成，即使&ldquo;在华尔街行业浸淫十年的专家（veterans）&rdquo;也不能理解函数式编程（也写作FP）都在探讨些什么。如果你去问花旗集团（Citi Group）或德意志银行（Deutsche Bank）的项目经理[<a href="http://xfguo.spaces.live.com/mmm2006-10-27_23.09/#footer"><u><font color="#0000ff">1</font></u></a>]，为什么选择了 JMS 而不 Erlang，他们可能回答不能在产业级的应用中使用学院派语言。问题是，一些最为复杂的，有着最严格需求的系统却是用函数式编程元素写成。有些说法不能让人信服。 
</p><p>的确，关于函数式编程的文章和论文难于理解，但他们本来不必这么晦涩。这一知识隔阂的形成完全是历史原因。函数式编程的概念本身并不困难。这篇文章可以作为&ldquo;简易的函数式编程导引&rdquo;。是一座从我们命令式（imperative）的思维模式到函数式编程的桥梁。去取杯咖啡回来继续读下去吧。可能你的同事很快就会开始取笑你对函数式编程发表的观点了。 
</p><p>那么什么是函数式编程呢？它怎么产生？它可以被掌握吗（Is it edible)？如果它真如其倡导者所言，为什么没有在行业中得到更广泛的使用？为什么好像只有那些拿着博士学位的人才使用它？最要紧的是，为什么它就 TMD 这么难学？这些 closure, continuation, currying，惰性求值和无副作用等等究竟是些什么东西？没有大学参与的项目怎么使用它？为什么它看上去这么诡异于和我们命令式思想友好，圣洁和亲近的一切的一切？我们将于不久扫清这些疑问。首先让我来解释形成实际生活和学界文章之间巨大隔阂的缘起，简单得像一次公园的散步。<a></a> 
</p><h3>信步游园</h3>
<p>启动时间机器，我们散步在两千多年以前的一个被遗忘了太久的春季明媚的日子，那是公元前380年。雅典城墙外的橄榄树树荫里，柏拉图和一个英俊的奴隶小男孩朝着学院走去。&ldquo;天气真好&rdquo;，&ldquo;饮食不错&rdquo;，然后话题开始转向哲思。 
</p><blockquote>
<p>&ldquo;瞧那两个学生，&rdquo;为了使问题更容易理解，柏拉图仔细地挑选着用词，&ldquo;你认为谁更高呢？&rdquo;<br />小男孩看着那两个人站着的水漕说，&ldquo;他们差不多一样高&rdquo;。<br />柏拉图说：&ldquo;你的差不多一样是什么意思？&rdquo;。&ldquo;我在这里看他们是一样高的，不过我肯定如果走近些就会看出他们高度的差别。&rdquo;<br />柏拉图笑了，他正把这个孩子带到正确的方向。&ldquo;那么你是说，我们这个世界没有完全的等同了？&rdquo;<br />小男孩想了一会儿回答，&ldquo;对，我不这样认为，任何事物总有一些区别，即使我们看不到它。&rdquo;<br />这句话非常到位！&ldquo;那么如果这世上没有完全的相等，你又是如何理解&lsquo;完全&rsquo;相等这个概念的呢？&rdquo;<br />小男孩迷惑得说：&ldquo;我不知道。&rdquo;最初尝试着理解数学的本源（nature）时也会产生这种疑惑。</p></blockquote>
<p>柏拉图暗示这个世上的万物都只是一个对完美的近似。他还认识到我们即使没有接触到完美但依然可以理解这一概念。所以他得出结论，完美的数学形式只能存在于另一个世界，我们通过和那个世界的某种联系在一定程度上知晓他们。很明显我们不能看到完美的圆，但我们可以理解什么是完美的圆并用数学公式将它表达出来。那么，什么是数学？为什么宇宙可以用数学定理描述？数学可以描述宇宙中的所有现象吗？[<a href="http://xfguo.spaces.live.com/mmm2006-10-27_23.09/#footer"><u><font color="#0000ff">2</font></u></a>] 
</p><p>数学哲学是一个很复杂的课题。像大多数哲学学科一样它更倾向于提出问题而不是给出解答。这些意见中很多都循回绕转于一个事实，即数学实际上是一个谜语：我们设置了一系列基本的不冲突的原理和一些可以施加于这些原理的操作规则，然后我们就能堆砌这些规则以形成更复杂的规则。数学家把这种方法叫做&ldquo;形式系统&rdquo;或&ldquo;演算&rdquo;。如果愿意，我们可以很快写出一个关于 Tetris（译者注：一种通常被称为俄罗斯方块的游戏）的形式系统。实际上，工作中的 Tetris 实现就是一个形式系统，只是被指定使用了个不常见的表现形式。 
</p><p>人马座的那个生物文明也许不能理解我们的 Tetris 和圆的范式，因为可能他们唯一的感知输入是气味香橙的橘子。他们也许永远不会发现 Tetris 范式，但很可能会有一个圆的范式。我们也可能将无法阅读它，因为我们的嗅觉没有那么复杂，可是一旦我们理解(pass)了那一范式的表示形式（通过这种传感器和标准解码技术来理解这种语言），其底层的概念就可被任何智能文明所理解。 
</p><p>有趣的是如果从来没有智能文明存在，Tetris 和圆的范式仍然严密合理，只是没有人注定将会发现他们。如果产生了一种智能文明，他就会发现一些形式系统来帮助描述宇宙的规律。但他还是不大可能发现 Tetris 因为宇宙中再没有和它相似的事物。在现实世界中这类无用的形式系统或迷题的例子数不胜数，Tetris 只是其中的一个典型。我们甚至不能确定自然数是否是对客观世界的完整近似，至少我们可以简单的想像一个很大的数它不能用宇宙中任何东西描述，因为它以近乎无穷。 
</p><h3>历史一瞥[<a href="http://xfguo.spaces.live.com/mmm2006-10-27_23.09/#footer"><u><font color="#0000ff">3</font></u></a>]</h3>
<p>再次启动时间机器，这一次的旅行近了很多，我们回到 1930 年代。大萧条正在蹂躏着那个或新或就的时代。空前的经济下挫影响着几乎所有阶层的家庭生活，只有少数人还能够保持着饥谨危机前的安逸。一些人就如此幸运地位列其中，我们关心的是普林斯顿大学的数学家们。 
</p><p>采用了歌特式风格设计建造的新办公室给普林斯顿罩上天堂般的幸福光环，来自世界各地的逻辑学家被邀请到普林斯顿建设一个新的学部。虽然彼时的美国民众已难能弄到一餐的面包，普林斯顿的条件则是可以在高高的穹顶下，精致雕凿的木质墙饰边上整日的品茶讨论或款款慢步于楼外的林荫之中。 
</p><p>阿隆左&middot;丘奇就是一个在这种近于奢侈的环境中生活着的数学家。他在普林斯顿获得本科学位后被邀留在研究生院继续攻读。阿隆左认为那里的建筑实属浮华，所以他很少一边喝茶一边与人讨论数学，他也不喜欢到林中散步。阿隆左是一个孤独者：因为只有一个人时他才能以最高的效率工作。虽然如此，他仍与一些普林斯顿人保持的定期的联系，其中包括阿兰&middot;图灵，约翰&middot;冯&middot;诺依曼，和 kurt Grodel。 
</p><p>这四个人都对形式系统很感兴趣，而不太留意现实世界，以便致力于解决抽象的数学难题。他们的难题有些共同之处：都是探索关于计算的问题。如果我们有了无限计算能力的机器，哪些问题可以被解决？我们可以使他们自动地得以解决吗？是否还是有些问题无法解决，为什么？不同设计的各种机器是否具有相同的计算能力？ 
</p><p>通过和其它人的合作，阿隆左&middot;丘奇提出了一个被称为 lambda 演算的形式系统。这个系统本质上是一种虚拟的机器的编程语言，他的基础是一些以函数为参数和返回值的函数。函数用希腊字母 lambda 标识，这个形式系统因此得名[<a href="http://xfguo.spaces.live.com/mmm2006-10-27_23.09/#footer"><u><font color="#0000ff">4</font></u></a>]。利用这一形式系统，阿隆左就可以对上述诸多问题推理并给出结论性的答案。 
</p><p>独立于阿隆左，阿兰&middot;图灵也在进行着相似的工作，他提出了一个不同的形式系统（现在被称为图灵机），并使用这一系统独立得给出了和阿隆左相似的结论。后来被证明图灵机和 lambda 演算能力等同。 
</p><p>我们的故事本可以到此结束，我会就此歇笔，而你也将浏览到下一个页面，如果第二次世界大战没有在那时打响。整个世界笼罩在战争的火光和硝烟之中，美国陆军和海军前所未有的大量使用炮弹，为了改进炮弹的精确度，部队组织了大批的科学家持续地计算微分方程以解出弹道发射轨迹。渐渐意识到这个任务用人力手工完成太耗精力后，人们开始着手开发各种设备来攻克这个难关。第一个解出了弹道轨迹的机器是 IBM 制造的 Mark I &mdash;&mdash; 它重达5吨，有75万个组件，每秒可以完成三次操作。 
</p><p>竞争当然没有就此结束，1949年，EDVAC（Electronic Discrete Variable Automatic Computer，爱达瓦克）被推出并获得了极大的成功。这是对冯&middot;诺依曼架构的第一个实践实例，实际上也是图灵机的第一个现实实现。那一年好运与阿隆左&middot;丘奇无缘。 
</p><p>直到1950年代将尽，一位 MIT 的教授John McCarthy（也是普林斯顿毕业生）对阿隆左&middot;丘奇的工作产生了兴趣。1958年，他公开了表处理语言 Lisp。Lisp 是对阿隆左&middot;丘奇的 lambda 演算的实现但同时它工作在冯&middot;诺依曼计算机上！很多计算机科学家认识到了 Lisp 的表达能力。1973年，MIT人工智能实验室的一组程序员开发了被称为Lisp机器的硬件－阿隆左 lambda 演算的硬件实现！ 
</p><p><strong>函数式编程</strong> 
</p><p>函数式编程是对阿隆左&middot;丘奇理论的实践应用。但也并非全部 lambda 演算都被应用到了实践中，因为 lambda 演算不是被设计为在物理局限下工作的。因此，象面向对象的编程一样，函数式编程是一系列理念，而不是严格的教条。现在有很多种函数式编程语言，他们中的大多数以不同方式完成不同任务。在本文中我将就最广泛使用的源自函数式编程的思想作一解释，并将用Java语言举例。(的确，你可以用Java写出函数式的程序如果你有显著的受虐倾向）。在下面的小节中，我将会把Java作为一种函数式语言，并对其稍加修改使它成为一种可用的函数式语言。现在开始吧。 
</p><p>lambda 演算被设计用来探询关于计算的问题，所以函数式编程主要处理计算，并惊人地用函数来完成这一过程。函数是函数式编程的基本单位，函数几乎被用于一切，包括最简单的计算，甚至变量都由计算取代。在函数式编程中，变量只是表达式的别名（这样我们就不必把所有东西打在一行里）。变量是不能更改的，所有变量只能被赋值一次。用 Java 的术语来说，这意味着所有单一变量都被声明为 final（或 C++ 的 const）。在函数式编程中没有非 final 的变量。 
</p><blockquote>
<p>final int i = 5;<br />final int j = i + 3;</p></blockquote>
<p>因为函数式编程中所有变量都是 final 的，所以可以提出这样两个有趣的表述：没有必要总是写出关键字 final，没有必要把变量再称为变量。那么现在我们对Java作出两个修改：在我们的函数式 Java 中所有变量默认都是 final的，我们将变量（variable）称为符号（symbol）。 
</p><p>就此你也许会质疑，用我们新创造的语言还能写出有些复杂度的程序吗？如果每个符号都是不可变更(non-mutalbe)的，那么就无法改变任何状态！其实事实并非完全如此。在阿隆左研究其 lambda 演算时，他并不想将某个状态维护一段时间以期未来对其进行修改。他关注的是对数据的操作（也通常被称为&rdquo;演算体 caculating stuff&rdquo;）。既然已被证明lambda演算与图灵机等价，它可以完成所有命令式编程语言能够完成的任务。那么，我们怎么才能做到呢？ 
</p><p>答案是函数式程序能保存状态，只是它并非通过变量而是使用函数来保存状态。状态保存在函数的参数中，保存在堆栈上。如果你要保存某个状态一段时间并时不时地对其进行一些修改，可以写个递归函数。举个例子，我们写个函数来翻转 Java 的字符串。记住，我们声明的每个变量默认都是 final 的。[<a href="http://xfguo.spaces.live.com/mmm2006-10-27_23.09/#footer"><u><font color="#0000ff">5</font></u></a>] 
</p><blockquote>
<p>String reverse(String arg) {<br />if(arg.length == 0) {<br />return arg;<br />}<br />else {<br />return reverse(arg.substring(1, arg.length)) + arg.substring(0,1);<br />}}</p></blockquote>
<p>这个函数很慢因为它不断地调用自己[6]，它还也是个嗜内存魔因为要持续分配对象。不过它的确是在用函数式风格。你可能会问，怎么有人会这样写程序？好的，我这就慢慢讲来： 
</p><h3>函数式编程的优点</h3>
<p>你可能会认为我根本无法对上面那个畸形的函数给出个合理的解释。我开始学习函数式编程时就是这么认为的。不过我是错了。有很好的理由使用这种风格，当然其中一些属主观因素。例如，函数式程序被认为更容易阅读。因为每个街区的孩子都知道，是否容易理解在旁观者的眼中，所以我将略去这些主观方面的理由。幸运的是，还有很多的客观理由。 
</p><p><strong>单元测试</strong> 
</p><p>因为函数式编程的每一个符号都是 final 的，没有函数产生过副作用。因为从未在某个地方修改过值，也没有函数修改过在其作用域之外的量并被其他函数使用（如类成员或全局变量）。这意味着函数求值的结果只是其返回值，而惟一影响其返回值的就是函数的参数。 
</p><p>这是单元测试者的梦中仙境(wet dream)。对被测试程序中的每个函数，你只需在意其参数，而不必考虑函数调用顺序，不用谨慎地设置外部状态。所有要做的就是传递代表了边际情况的参数。如果程序中的每个函数都通过了单元测试，你就对这个软件的质量有了相当的自信。而命令式编程就不能这样乐观了，在 Java 或 C++ 中只检查函数的返回值还不够&mdash;&mdash;我们还必须验证这个函数可能修改了的外部状态。 
</p><p><strong>调试</strong> 
</p><p>如果一个函数式程序不如你期望地运行，调试也是轻而易举。因为函数式程序的 bug 不依赖于执行前与其无关的代码路径，你遇到的问题就总是可以再现。在命令式程序中，bug 时隐时现，因为在那里函数的功能依赖与其他函数的副作用，你可能会在和 bug 的产生无关的方向探寻很久，毫无收获。函数式程序就不是这样&mdash;&mdash;如果一个函数的结果是错误的，那么无论之前你还执行过什么，这个函数总是返回相同的错误结果。 
</p><p>一旦你将那个问题再现出来，寻其根源将毫不费力，甚至会让你开心。中断那个程序的执行然后检查堆栈，和命令式编程一样，栈里每一次函数调用的参数都呈现在你眼前。但是在命令式程序中只有这些参数还不够，函数还依赖于成员变量，全局变量和类的状态（这反过来也依赖着这许多情况）。函数式程序里函数只依赖于它的参数，而那些信息就在你注视的目光下！还有，在命令式程序里，只检查一个函数的返回值不能够让你确信这个函数已经正常工作了，你还要去查看那个函数作用域外数十个对象的状态来确认。对函数式程序，你要做的所有事就是查看其返回值！ 
</p><p>沿着堆栈检查函数的参数和返回值，只要发现一个不尽合理的结果就进入那个函数然后一步步跟踪下去，重复这一个过程，直到它让你发现了 bug 的生成点。 
</p><p><strong>并行</strong> 
</p><p>函数式程序无需任何修改即可并行执行。不用担心死锁和临界区，因为你从未用锁！函数式程序里没有任何数据被同一线程修改两次，更不用说两个不同的线程了。这意味着可以不假思索地简单增加线程而不会引发折磨着并行应用程序的传统问题。 
</p><p>事实既然如此，为什么并不是所有人都在需要高度并行作业的应用中采用函数式程序？嗯，他们正在这样做。爱立信公司设计了一种叫作 Erlang 的函数式语言并将它使用在需要极高抗错性和可扩展性的电信交换机上。还有很多人也发现了 Erlang 的优势并开始使用它。我们谈论的是电信通信控制系统，这与设计华尔街的典型系统相比对可靠性和可升级性要求高了得多。实际上，Erlang 系统并不可靠和易扩展，Java 才是。Erlang 系统只是坚如磐石。 
</p><p>关于并行的故事还没有就此停止，即使你的程序本身就是单线程的，那么函数式程序的编译器仍然可以优化它使其运行于多个CPU上。请看下面这段代码： 
</p><blockquote>
<p>String s1 = somewhatLongOperation1();<br />String s2 = somewhatLongOperation2();<br />String s3 = concatenate(s1, s2);</p></blockquote>
<p>在函数编程语言中，编译器会分析代码，辨认出潜在耗时的创建字符串s1和s2的函数，然后并行地运行它们。这在命令式语言中是不可能的，因为在那里，每个函数都有可能修改了函数作用域以外的状态并且其后续的函数又会依赖这些修改。在函数式语言里，自动分析函数并找出适合并行执行的候选函数简单的像自动进行的函数内联化！在这个意义上，函数式风格的程序是&ldquo;不会过时的技术(future proof)&rdquo;(即使不喜欢用行业术语，但这回要破例一次)。硬件厂商已经无法让CPU运行得更快了，于是他们增加了处理器核心的速度并因并行而获得了四倍的速度提升。当然他们也顺便忘记提及我们的多花的钱只是用在了解决平行问题的软件上了。一小部分的命令式软件和 100% 的函数式软件都可以直接并行运行于这些机器上。 
</p><p><strong>代码热部署</strong> 
</p><p>过去要在 Windows上安装更新，重启计算机是难免的，而且还不只一次，即使是安装了一个新版的媒体播放器。Windows XP 大大改进了这一状态，但仍不理想（我今天工作时运行了Windows Update，现在一个烦人的图标总是显示在托盘里除非我重启一次机器）。Unix系统一直以来以更好的模式运行，安装更新时只需停止系统相关的组件，而不是整个操作系统。即使如此，对一个大规模的服务器应用这还是不能令人满意的。电信系统必须100%的时间运行，因为如果在系统更新时紧急拨号失效，就可能造成生命的损失。华尔街的公司也没有理由必须在周末停止服务以安装更新。 
</p><p>理想的情况是完全不停止系统任何组件来更新相关的代码。在命令式的世界里这是不可能的。考虑运行时上载一个Java类并重载一个新的定义，那么所有这个类的实例都将不可用，因为它们被保存的状态丢失了。我们可以着手写些繁琐的版本控制代码来解决这个问题，然后将这个类的所有实例序列化，再销毁这些实例，继而用这个类新的定义来重新创建这些实例，然后载入先前被序列化的数据并希望载入代码可以恰到地将这些数据移植到新的实例。在此之上，每次更新都要重新手动编写这些用来移植的代码，而且要相当谨慎地防止破坏对象间的相互关系。理论简单，但实践可不容易。 
</p><p>对函数式的程序，所有的状态即传递给函数的参数都被保存在了堆栈上，这使的热部署轻而易举！实际上，所有我们需要做的就是对工作中的代码和新版本的代码做一个差异比较，然后部署新代码。其他的工作将由一个语言工具自动完成！如果你认为这是个科幻故事，请再思考一下。多年来 Erlang工程师一直更新着他们的运转着的系统，而无需中断它。 
</p><p><strong>机器辅助的推理和优化</strong> 
</p><p>函数式语言的一个有趣的属性就是他们可以用数学方式推理。因为一种函数式语言只是一个形式系统的实现，所有在纸上完成的运算都可以应用于用这种语言书写的程序。编译器可以用数学理论将转换一段代码转换为等价的但却更高效的代码[<a href="http://xfguo.spaces.live.com/mmm2006-10-27_23.09/#footer"><u><font color="#0000ff">7</font></u></a>]。多年来关系数据库一直在进行着这类优化。没有理由不能把这一技术应用到常规软件上。 
</p><p>另外，还能使用这些技术来证明部分程序的正确，甚至可能创建工具来分析代码并为单元测试自动生成边界用例！对稳固的系统这种功能没有价值，但如果你要设计心房脉冲产生器 (pace maker)或空中交通控制系统，这种工具就不可或缺。如果你编写的应用程序不是产业的核心任务，这类工具也是你强于竞争对手的杀手锏。 
</p><p>高阶函数 
</p><p>我记得自己在了解了上面列出的种种优点后曾想：&ldquo;那都是非常好的特性，可是如果我不得不用天生就不健全的语言编程，把一切变量声明为<br />final 产生的代码将是垃圾一堆。&rdquo; 这其实是误解。在如Java 这般的命令式语言环境里，将所有变量声明为 final 没有用，但是在函数式语言里不是这样。函数式语言提供了不同的抽象工具它会使你忘记你曾经习惯于修改变量。高阶函数就是这样一种工具。 
</p><p>函数式语言中的函数不同于 Java 或 C 中的函数，而是一个超集&mdash;&mdash;它有着 Java 函数拥有的所有功能，但还有更多。创建函数的方式和 C 中相似: 
</p><blockquote>
<p>int add(int i, int j) {<br />return i + j;<br />}</p></blockquote>
<p>这意味着有些东西和同样的 C 代码有区别。现在扩展我们的 Java 编译器使其支持这种记法。当我们输入上述代码后编译器会把它转换成下面的Java代码（别忘了，所有东西都是 final 的）： 
</p><blockquote>
<p>class add_function_t {<br />int add(int i, int j) {<br />return i + j;<br />}<br />} 
</p><p>add_function_t add = new add_function_t();</p></blockquote>
<p>这里的符号 add 并不是一个函数。这是一个有一个成员函数的很小的类。我们现在可以把 add 作为函数参数放入我们的代码中。还可以把它赋给另一个符号。我们在运行时创建的 add_function_t 的实例如果不再被使用就将会被垃圾回收掉。这些使得函数成为第一级的对象无异于整数或字符串。（作为参数）操作函数的函数被称为高阶函数。别让这个术语吓着你，这和 Java 的 class 操作其它 class（把它们作为参数）没有什么区别。我们本可以把它们称为&ldquo;高阶类&rdquo;但没有人注意到这个，因为 Java 背后没有一个强大的学术社区。 
</p><p>那么怎样，何时应该使用高阶函数呢？我很高兴你这样问。如果你不曾考虑类的层次，就可能写出了一整团堆砌的代码块。当你发现其中一些行的代码重复出现，就把他们提取成函数（幸运的是这些依然可以在学校里学到）。如果你发现在那个函数里一些逻辑动作根据情况有变，就把他提取成高阶函数。糊涂了？下面是一个来自我工作的实例：假如我的一些 Java 代码接受一条信息，用多种方式处理它然后转发到其他服务器。 
</p><blockquote>
<p>class MessageHandler {<br />void handleMessage(Message msg) {<br />// &hellip;<br />msg.setClientCode(&rdquo;ABCD_123&Prime;);<br />// &hellip; 
</p><p>sendMessage(msg);<br />} 
</p><p>// &hellip; 
</p><p>}</p></blockquote>
<p>现在假设要更改这个系统，现在我们要把信息转发到两个服务器而不是一个。除了客户端的代码一切都像刚才一样&mdash;&mdash;第二个服务器希望这是另一种格式。怎么处理这种情况？我们可以检查信息的目的地并相应修改客户端代码的格式，如下： 
</p><blockquote>
<p>class MessageHandler {<br />void handleMessage(Message msg) {<br />// &hellip;<br />if(msg.getDestination().equals(&rdquo;server1&Prime;) {<br />msg.setClientCode(&rdquo;ABCD_123&Prime;);<br />} else {<br />msg.setClientCode(&rdquo;123_ABC&rdquo;);<br />}<br />// &hellip; 
</p><p>sendMessage(msg);<br />} 
</p><p>// &hellip; 
</p><p>}</p></blockquote>
<p>然而这不是可扩展的方法，如果加入了更多的服务器，这个函数将线性增长，更新它会成为我的梦魇。面向对象的方法是把MessageHandler作为基类，在导出类中专业化客户代码操作： 
</p><blockquote>
<p>abstract class MessageHandler {<br />void handleMessage(Message msg) {<br />// &hellip;<br />msg.setClientCode(getClientCode());<br />// &hellip; 
</p><p>sendMessage(msg);<br />} 
</p><p>abstract String getClientCode(); 
</p><p>// &hellip; 
</p><p>} 
</p><p>class MessageHandlerOne extends MessageHandler {<br />String getClientCode() {<br />return &ldquo;ABCD_123&Prime;;<br />} 
</p><p>} 
</p><p>class MessageHandlerTwo extends MessageHandler {<br />String getClientCode() {<br />return &ldquo;123_ABCD&rdquo;;<br />} 
</p><p>}</p></blockquote>
<p>现在就可以对每一个服务器实例化一个适合的类。添加服务器的操作变得容易维护了。但对于这么一个简单的修改仍然要添加大量的代码。为了支持不同的客户代码我们创建了两个新的类型！现在我们用高阶函数完成同样的功能： 
</p><blockquote>
<p>class MessageHandler {<br />void handleMessage(Message msg, Function getClientCode) {<br />// &hellip;<br />Message msg1 = msg.setClientCode(getClientCode());<br />// &hellip; 
</p><p>sendMessage(msg1);<br />} 
</p><p>// &hellip; 
</p><p>} 
</p><p>String getClientCodeOne() {<br />return &ldquo;ABCD_123&Prime;; 
</p><p>} 
</p><p>String getClientCodeTwo() {<br />return &ldquo;123_ABCD&rdquo;; 
</p><p>} 
</p><p>MessageHandler handler = new MessageHandler();<br />handler.handleMessage(someMsg, getClientCodeOne);</p></blockquote>
<p>没有创建新的类型和新的class层次，只是传入合适的函数作为参数，完成了面向对象方式同样的功能，同时还有一些额外的优点。没有使自己囿于类的层次之中：可以在运行时传入函数并在任何时候以更高的粒度更少的代码修改他们。编译器高效地为我们生成了面向对象的&ldquo;粘合&rdquo;代码！除此之外，我们还获得了所有函数式编程的其他好处。当然函数式语言提供的抽象不只这些，高阶函数只是一个开始： 
</p><p>currying 
</p><p>我认识的大多数人都读过&ldquo;四人帮&rdquo;的那本设计模式，任何自重的程序员都会告诉你那本书是语言中立的(agnostic)，模式在软件工程中是通用的，和使用的语言无关。这个说法颇为高贵，故而不幸的是，有违现实。 
</p><p>函数式编程极具表达能力。在函数式语言中，语言既已达此高度，设计模式就不再是必需，最终你将设计模式彻底消除而以概念编程。适配器(Adapter)模式就是这样的一个例子。(究竟适配器和 Facade 模式区别在哪里？可能有些人需要在这里再多费些篇章)。一旦语言有了叫作 currying 的技术，这一模式就可以被消除。 
</p><p>currying. 
</p><p>适配器模式最有名的是被应用在 Java 的&ldquo;默认&rdquo;抽象单元&mdash;&mdash;class 上。在函数式编程里，模式被应用到函数。模式带有一个接口并将它转换成另一个对他人有用的接口。这有一个适配器模式的例子： 
</p><blockquote>
<p>int pow(int i, int j);<br />int square(int i)<br />{<br />return pow(i, 2);<br />}</p></blockquote>
<p>上面的代码把一个整数幂运算接口转换成为了一个平方接口。在学术文章里，这个雕虫小技被叫作currying(得名于逻辑学家Haskell<br />Curry，他曾将相关的数学理论形式化 )。因为在函数式编程中函数（反之如class)被作为参数来回传递，currying 很频繁地被用来把函数调整为更适宜的接口。因为函数的接口是他的参数，使用 currying 可以减少参数的数目(如上例所示)。 
</p><p>函数式语言内建了这一技术。不用手动地创建一个包装了原函数的函数，函数式语言可以为你代劳。同样地，扩展我们的语言，让他支持这个技术： 
</p><blockquote>
<p>square = int pow(int i, 2);</p></blockquote>
<p>这将为我们自动创建出一个有一个参数的函数 square。他把第二个参数设置为 2 再调用函数 pow。这行代码会被编译为如下的 Java 代码： 
</p><blockquote>
<p>class square_function_t {<br />int square(int i) {<br />return pow(i, 2);<br />}<br />} 
</p><p>square_function_t square = new square_function_t();</p></blockquote>
<p>正如你所见，通过简单地创建一个对原函数的包装，在函数式编程中，这就是 currying &mdash;&mdash; 快速简易创建包装的捷径。把精力集中在你的业务上，让编译器为你写出必要的代码！什么时候使用 currying？这很简单，任何时候你想要使用适配器模式（包装）时。 
</p><p><strong>惰性求值</strong> 
</p><p>惰性（或延迟）求值这一技术可能会变得非常有趣一旦我们采纳了函数式哲学。在讨论并行时已经见过下面的代码片断： 
</p><blockquote>
<p>String s1 = somewhatLongOperation1();<br />String s2 = somewhatLongOperation2();<br />String s3 = concatenate(s1, s2);</p></blockquote>
<p>在一个命令式语言中求值顺序是确定的，因为每个函数都有可能会变更或依赖于外部状态，所以就必须有序的执行这些函数：首先是<br />somewhatLongOperation1，然后 somewhatLongOperation2，最后 concatenate，在函数式语言里就不尽然了。 
</p><p>前面提到只要确保没有函数修改或依赖于全局变量，somewhatLongOperation1 和 somewhatLongOperation2 可以被并行执行。但是如果我们不想同时运行这两个函数，还有必要保证有序的执行他们呢？答案是不。我们只在其他函数依赖于s1和s2时才需要执行这两个函数。我们甚至在concatenate调用之前都不必执行他们&mdash;&mdash;可以把他们的求值延迟到concatenate函数内实际用到他们的位置。如果用一个带有条件分支的函数替换concatenate并且只用了两个参数中的一个，另一个参数就永远没有必要被求值。在 Haskell 语言中，不确保一切都（完全）按顺序执行，因为 Haskell 只在必要时才会对其求值。 
</p><p>惰性求值优点众多，但缺点也不少。我们会在这里讨论它的优点而在下一节中解释其缺点。 
</p><p><strong>优化</strong> 
</p><p>惰性求值有客观的优化潜力。惰性编译器看函数式代码就像数学家面对的代数表达式&mdash;&mdash;&mdash;&mdash;可以注销一部分而完全不去运行它，重新调整代码段以求更高的效率，甚至重整代码以降低出错，所有确定性优化(guaranteeing optimizations)不会破坏代码。这是严格用形式原语描述程序的巨大优势&mdash;&mdash;&mdash;&mdash;代码固守着数学定律并可以数学的方式进行推理。 
</p><p><strong>抽象控制结构</strong> 
</p><p>惰性求值提供了更高一级的抽象，它使得不可能的事情得以实现。例如，考虑实现如下的控制结构： 
</p><blockquote>
<p>unless(stock.isEuropean()) {<br />sendToSEC(stock);<br />}</p></blockquote>
<p>我们希望只在祖先不是欧洲人时才执行sendToSEC。如何实现 unless？如果没有惰性求值，我们需要某种形式的宏（macro）系统，但<br />Haskell 这样的语言不需要它。把他实现为一个函数即可： 
</p><blockquote>
<p>void unless(boolean condition, List code) {<br />if(!condition)<br />code;<br />}</p></blockquote>
<p>注意如果条件为真代码将不被执行。我们不能在一个严格（strict）的语言中再现这种求值，因为 unless 调用之前会先对参数进行求值。 
</p><p><strong>无穷(infinite)数据结构</strong> 
</p><p>惰性求值允许定义无穷数据结构，对严格语言来说实现这个要复杂的多。考虑一个 Fibonacci 数列，显然我们无法在有限的时间内计算出或在有限的内存里保存一个无穷列表。在严格语言如 Java 中，只能定义一个能返回 Fibonacci 数列中特定成员的 Fibonacci 函数，在 Haskell<br />中，我们对其进一步抽象并定义一个关于 Fibonacci 数的无穷列表，因为作为一个惰性的语言，只有列表中实际被用到的部分才会被求值。这使得可以抽象出很多问题并从一个更高的层次重新审视他们。（例如，我们可以在一个无穷列表上使用表处理函数）。 
</p><p><strong>缺点</strong> 
</p><p>当然从来不存在免费的午餐。惰性求值有很多的缺点，主要就在于，懒。有很多现实世界的问题需要严格求值。例如考虑下例： 
</p><blockquote>
<p>System.out.println(&rdquo;Please enter your name: &ldquo;);<br />System.in.readLine();</p></blockquote>
<p>在惰性求值的语言里，不能保证第一行会在第二行之前执行！那么我们就不能进行输入输出操作，不能有意义地使用本地（native）接口（因为他们相互依赖其副作用必须被有序的调用），从而与整个世界隔离。如果引入允许特定执行顺序的原语又将失去数学地推理代码的诸多好处（为此将葬送函数式编程与其相关的所有优点）。幸运的是，并非丧失了一切，数学家为此探索并开发出了许多技巧来保证在一定函数设置下(function setting)代码以一特定的顺序执行。这样我们就赢得了两个世界。这些技术包括 continuation， monad 和 uniqueness typing<br />（一致型别）。我只会在本文中解释continuation，把 monad 和 uniqueness typing 留到将来的文章中。有趣的是，除了确保函数求值顺序， continuation 在很多别的情况下也很有用。这点等一会儿就会提到。 
</p><p><strong>Continuations</strong> 
</p><p>Continuations 对于程序设计的意义，就像《达芬奇密码》对人类历史的意义：即对人类最大秘密的惊人揭示。也许不是，但他在概念上的突破性至少和揭示了负数的平方根意义等同。 
</p><p>我们在学习函数时，只是学到了一半的事实，因为我们基于一个错误的假定:函数只能将结果返回到它的调用函数。在这个意思上continuation 是广义的函数。函数不必要返回到其调用函数而可以返回到程序的任何地方。我们把&rdquo;continuation&rdquo; 作为参数传给一个函数，它指定了这个函数返回的位置。这个描述可能听起来更加复杂。看一下下面的代码： 
</p><blockquote>
<p>int i = add(5, 10);<br />int j = square(i);</p></blockquote>
<p>函数 add 在其被调用的位置将结果 15 赋给了 i，接下来 i 的值被用来调用 square。注意所有的惰性求值编译器都不能调整这几行代码因为第二行依赖着第一行的成功求值。下面用 continuation 风格又称 CPS (Continuation Programming Style) 来重写这段代码，这里函数 add 会将结果返回到 square 而不是原来的调用函数。 
</p><blockquote>
<p>int j = add(5, 10, square);</p></blockquote>
<p>这个例子中 add 有了另一个参数 &mdash;&mdash; 一个 add 必须在它求值结束时用其返回值调用的函数。这里 square 是 add 的一个 continuation。这两种情况下，j 都将等于 255。 
</p><p>这就是强制使惰性语言有序地求值两个表达式的第一个技巧。考虑下面这个（熟悉的）IO代码： 
</p><blockquote>
<p>System.out.println(&rdquo;Please enter your name: &ldquo;);<br />System.in.readLine();</p></blockquote>
<p>这两行不相依赖所以编译器会自由的重新调整他们的执行顺序。然而，如果我们用 CPS 来重写这段代码，就会有一个依赖，编译器会因此而强制对这两行代码有序执行！ 
</p><blockquote>
<p>System.out.println(&rdquo;Please enter your name: &ldquo;, System.in.readLine);</p></blockquote>
<p>这里 println 需要用自己的返回结果作为参数去调用 readLine 并将 readLine 返回值作为自己的返回值。这样就能确保这两行被有序执行而且 readLine 一定被执行（因为整个计算期望最后的结果为结果）。Java 的 println 返回 void 但如果它返回的是一个抽象值（readLine所期待的），我们就解决了这个问题！当然这样的链接函数调用很快就会使代码难以读懂，不过这个可以避免。比如我们可以给语言添加些语法甜点(syntactic sugar)就可以简单的按顺序输入表达式，然后由编译器自动为我们链接这些函数调用。这样就可以如愿地使用期望的求值顺序并保留一切函数式编程的好处（包括数学地对我们程序进行推理的能力）！如果还是有迷惑，记住函数是只有一个成员的类的实例。重写上述代码使得 println 和 readLine 成为类的实例，这样就对一切都清楚了。 
</p><p>如果我在此结束本节，那将仅仅涉及到 continuation 最浅显的应用。用 CPS 重写整个程序，那里所有的函数都增加一个额外的 continuation 参数并把函数结果传给它。也可以通过简单地把函数当作 continuation 函数（总是返回到调用者的函数）的特殊实例来将程序转为 CPS 风格。这种转换很容易被自动化（事实上，许多编译器就是这么做的）。 
</p><p>一旦我们将一个程序转为了CPS，那么很明显每个指令都将有些 continuation, 这是一个该指令在执行结束时会用其执行结果调用的函数，通常的程序中，这是一个它要返回的地址。从上面的例子中随便举个例子，比如 add(5, 10)。在用CPS风格写的程序里，add 的continuation很明显&mdash;&mdash;这是一个 add 在其执行结束时会调用的函数。那么如果在非CPS的程序里，它是什么呢？当然我们可以把程序转为 CPS ，但有这个必要吗？ 
</p><p>其实没有必要。仔细看一下我们的 CPS 转换过程。如果尝试为它写一个编译器，然后经过长期的思考后，你意识到这个 CPS 的版本根本不需要栈！没有函数会以传统的意义&ldquo;返回&rdquo;，它只是用结果调用了另一个函数。我们无需在调用时将函数参数压栈再于调用结束时弹出栈，而只是简单的把他们保存在一大块内存中，然后使用跳转指令。不再需要原来的参数&mdash;&mdash;他们不会再次被用到，因为没有函数会返回！ 
</p><p>所以，用 CPS 风格写成的程序没有堆栈，但每个函数却有一个额外的参数可被调用。不是 CPS 风格的程序没有可以被调用的这个参数，但却有栈。栈中存放着什么？只是参数和一个指向函数返回地址的指针。你看到光了吗？栈中只是放着 continuation 的信息！ 栈中指向返回指令的指针本质上和 CPS 程序里将被调用的函数是等价的。如果你想探究 add(5,10) 的 continuation，只要简单地检查它在堆栈的执行点！ 
</p><p>这的确很简单。continuation 和栈上指向返回地址的指针是等价的，只是 continuation 是被显式传递，所以不必和函数被调用点是同一位置。如果还记得 continuation 就是一个函数，并且在我们的语言里，函数被编译为一个类的实例，你就会理解指向栈中返回指令的指针实际就是传递给 continuation 的参数，因为我们的函数（就像一个类的实例）只是一个指针。这意味着给定程序中任意时间和任意位置，你都可以去请求一个当前的 continuation (current continuation)（它就是当前的栈的信息）。 
</p><p>好的，这样我们就知道了什么是 current continuation。他有什么意义？一旦我们得到了当前的 continuation 并将它保存在某处，我们就最终将程序当前的状态保存了下来&mdash;&mdash;及时地冷冻下来。这就像操作系统将其置为休眠状态。一个 continuation 对象里保存了在我们获得它的地方重新启动程序的必要信息。操作系统在每次发生线程间的上下文切换时也是如此。唯一的区别是它保留着全部控制。请求一个continuation 对象（在Scheme里，可以调用 call-with-current-continuation 函数）后，你就会获得一个包括了当前 continuation<br />的对象&mdash;&mdash;堆栈（或者在CPS情况下则是下一个要调用的函数）。可以把这个对象保存在一个变量（或者是磁盘）里。当你用这 continuation &ldquo;重启&rdquo;程序时，就会转回到处你取得这个对象的那个状态。这就象切换回一个被挂起的线程或唤醒休眠着的操作系统，区别是用 continuation，你可以多次地重复这一过程。当操作系统被唤醒时，休眠信息就被销毁了。但如果那些信息没有被销毁，你也就可以一次次地将它唤醒到同一点，就象重返过去一样。有了 continuation 你就有了这个控制力！ 
</p><p>Continuation 应该在什么情况下使用呢？通常在尝试模拟一个本质上是无状态的应用时可以简化你的任务。Continuation 很适合在Web应用程序中使用。微软公司的 ASP.NET 技术极尽苦心地模拟状态以便你在开发 Web 应用时少费周折。可如果 C# 支持了continuation，ASP.NET 的复杂度就可以减半&mdash;&mdash;你只需要保存一个 continuation，当用户下次发出 web 请求时重启它即可。对程序员来说，web 应用程序将不再有中断&mdash;&mdash;程序只是简单的从下一行重启！利用 continuation 这一抽象解决问题真是令人难以置信的便利。考虑到越来越多的胖客户端应用程序正在向服务器端转移，将来 continuation 也会变得越来越重要。 
</p><p><strong>模式匹配</strong> 
</p><p>模式匹配不是什么新的创新的特性。事实上，它和函数式编程的关系不大。把产生模式匹配归因于函数式编程的唯一的原因是函数式语言一度提供了模式匹配，然而现在的命令式语言还做不到。 
</p><p>让我们用一个例子深入了解一下模式匹配。这是一个Java的Fibonacci函数： 
</p><blockquote>
<p>int fib(int n) {<br />if(n == 0) return 1;<br />if(n == 1) return 1; 
</p><p>return fib(n - 2) + fib(n - 1);<br />}</p></blockquote>
<p>让我们从Java衍生出的语言来支持模式匹配： 
</p><blockquote>
<p>int fib(0) {<br />return 1;<br />} 
</p><p>int fib(1) {<br />return 1;<br />} 
</p><p>int fib(int n) {<br />return fib(n - 2) + fib(n - 1); 
</p><p>}</p></blockquote>
<p>两者有什么区别？编译器为我们实现了分支。这有什么大不了？的确没什么。有人注意到很多函数包括了复杂的 swith 语句（尤其是在函数式程序中）所以认为这种抽象形式很好。我们把一个函数定义分离成多个，然后把模式置于参数中（有点象重载）。当这个函数被调用时，编译器使其比较参数和其运行时的定义然后选择其中正确的一个。这一般是通过选择可选的最特定的定义来完成。例如，int fib(int n) 可以以 n 等于 1 被调用，但是实际上 fib(n) 没有被调用，因为 fib(1) 更加特定。 
</p><p>模式匹配通常要比我这个例子复杂，比如，高级模式匹配系统可以让我们这样做： 
</p><blockquote>
<p>int f(int n &lt; 10) { ... }<br />int f(int n) { ... }</p></blockquote>
<p>模式匹配什么时候适用？情况太多了！每当你有一个嵌套着 if 的复杂的数据结构，这时就可以用模式匹配以更少的代码完成得更好。一个很好的例子闪现在我脑海，这就是所有 Win32 平台都提供了的标准的 WinProc 函数（即使它通常被抽象了）。通常模式匹配系统能检测集合也可以应付简单的值。例如，当传给函数一个数组后，就可以找出所有首元素为 1 第三个元素大于 3 的所有数组。 
</p><p>模式匹配还有一个好处即如果需要增加或修改条件，那么不必对付一个巨大的函数。只需增加或修改适合的定义即可。这消除了&ldquo;四人帮&rdquo;（GoF）书中的一大类设计模式。条件越复杂，模式匹配就越有用。一旦习惯了它，你就会担心没有了模式匹配的日子如何打发。 
</p><p><strong>Closures</strong> 
</p><p>到此我们已经讨论了纯的函数式语言&mdash;&mdash;实现了lambda演算又不包括与丘奇形式系统矛盾的语言&mdash;&mdash;环境里的特性，可是还有很多在lambda演算框架之外的函数语言的有用特征。虽然一个公理系统的实现可以让我们象数学表达式那样思考程序但它未必是实际可行的。许多语言选择去合并一些函数式的元素而没有严格的坚持函数式的教条。很多象这样的语言（如Common Lisp）不要求变量是 final 的&mdash;&mdash;可以即处对其修改。他们还不要求函数只依赖于其参数&mdash;&mdash;允许函数访问外部状态。但这些语言也的确包含着函数式的特征&mdash;&mdash;如高阶函数，在非纯粹的函数式语言里传递函数作为参数和限制在 lambda 演算系统中的作法有些不同，它需要一种常被称为词法（lexical）closure 的有趣特性。下面我给出几个例子。记住，这里变量不再是final的，函数可以引用其作用域外的变量： 
</p><blockquote></blockquote>
<blockquote>
<p>Function makePowerFn(int power) {<br />int powerFn(int base) {<br />return pow(base, power);<br />}<br />return powerFn;<br />} 
</p><p>Function square = makePowerFn(2);<br />square(3); // returns 9</p></blockquote>
<p>函数 make-power-fn 返回了一个函数，它有一个参数，并对这个参数进行一定阶的幂运算。如果对 square(3) 求值会有什么结果？变量 power 不在 powerFn 的作用域中，因为 makePowerFn 已经返回它的栈桢而不复存在。那么square如何工作？一定是这个语言以某种方式将power的值保存了起来以便 square 使用。如果我们再新建一个函数cube，用来计算参数的立方又会怎样？运行环境必须存储两个power的拷贝，每个我们用 make-power-fn 生成的函数都用一个拷贝。保存这些值的现象就被称为 closure。 closure 不只保存宿主函数的参数。例如，closure可能会是这样： 
</p><blockquote>
<p>Function makeIncrementer() {<br />int n = 0; 
</p><p>int increment() {<br />return ++n;<br />}<br />} 
</p><p>Function inc1 = makeIncrementer();<br />Function inc2 = makeIncrementer(); 
</p><p>inc1(); // returns 1;<br />inc1(); // returns 2;<br />inc1(); // returns 3;<br />inc2(); // returns 1;<br />inc2(); // returns 2;<br />inc2(); // returns 3;</p></blockquote>
<p>运行时已保存了n，所以递增器可以访问它，而且运行时为每个递增器都保存了一个 n 的拷贝，即使这些拷贝本应在 makeIncrementer<br />返回时消失。这些代码被如何编译？closure 在底层是如何工作的？很幸运，我们可以去幕后看看。 
</p><p>一点常识会很有帮助，首先会注意到的是局部变量的生命期不再由简单的作用域限定而是不确定的。那么显然可以由此得出结论它们不再被保存在栈上&mdash;&mdash;反之必须被保存在堆上[<a href="http://xfguo.spaces.live.com/mmm2006-10-27_23.09/#footer"><u><font color="#0000ff">8</font></u></a>]。这样一来，closure 的实现就象我们前面讨论的函数一样了，只是它还有一个指向周围变量的引用。 
</p><blockquote>
<p>class some_function_t {<br />SymbolTable parentScope; 
</p><p>// &hellip; 
</p><p>}</p></blockquote>
<p>当一个 closure 引用了一个不在其作用域的变量时，它会在其祖先作用域中查找这个引用。就是这样！Closure 将函数式和面向对象的世界紧密结合。当你创建了一个包含了一些状态的类并把它传到别处时，考虑一下 closure。Closure 就是这样在取出作用域中的变量的同时创建&ldquo;成员变量&rdquo;，所以你不必亲自去做这些！ 
</p><p><strong>下一步的计划？</strong> 
</p><p>关于函数式编程，本文作了浅显地讨论。有时候一次粗浅的射猎可能会进展为重大的收获与我也受益匪浅。将来我还计划写写 category 理论，monad，函数式数据结构，函数式语言中的类型(type)体系，函数式并发，函数式数据库等等还有很多。如果我得以（在学习的过程中）写出了上述诸多主题中的一半，我的生命就会完整了。还有，Google 是我们的朋友。 
</p><p><strong>评论</strong>？ 
</p><p>如果你有任何问题，意见或建议，请发到邮箱 coffee&hellip;@gmail.com。很高兴收到你的反馈 
</p><p>=========================== 
</p><p>[1] 我在2005年找工作时常常提出这个问题，当时我得到的是数量可观的一脸茫然。想像一下，这些人至少每人会得到30万美元，如果他们理解了他们可以得到的大部分工具。 
</p><p>[2] 这像是个悖论。物理学家和数学家被迫确认他们还不完全清楚是否宇宙万物遵循着可以被数学描述的规则。 
</p><p>[3] 我一直厌恶提供了一堆枯燥的日期，人名和地点的纪年式历史课。对我而言，历史是改变了这个世界的人的生活，是他们行为之后的个人动机，是他们得以影响亿万生灵的体制。所以这个关于历史的小节注定无法完整，只讨论了于此关系及其密切的人物与事件。 
</p><p>[4] 我在学习函数式编程的时候，很不喜欢术语 lambda，因为我没有真正理解它的意义。在这个环境里，lambda 是一个函数，那个希腊字母只是方便书写的数学记法。每当你听到 lambda 时，只要在脑中把它翻译成函数即可。 
</p><p>[5] 有趣的是 Java 的字符串是不可变更的，探讨这一离经叛道的设计的原因也非常有趣，不过在这里会分散我们对原目标的注意力 
</p><p>[6] 大多数函数式编程语言的编译器能通过将递归尽可能转为迭代来进行优化，这被称为尾递归。 
</p><p>[7] 相反未必成立，虽然有时可以证明两端代码等价，但这不是所有情况下都成立。 
</p><p>[8] 这实际上不比存储在栈上慢，因为一旦引入了垃圾回收器，内存分配就成为了一个O(1)的操作。 </p></div>]]></description>
		</item>
		    
		
		<item>
			<title>网站开发规范</title>
			<link>http://hxjblog.blog.sohu.com/101319222.html</link>
			<comments>http://hxjblog.blog.sohu.com/101319222.html#comment</comments>
			<dc:creator>WEB设计和开发</dc:creator>
			<pubDate>Mon, 6 Oct 2008 11:29:34 +0800</pubDate>
			<guid>http://hxjblog.blog.sohu.com/101319222.html</guid>
			<description><![CDATA[<div>网站开发规范</div>
<div>总 论 </div>
<div>本规范既是一个开发规范，也是一个脚本语言参考，本规范并不是一个一成不变的必须严格遵守的条文，特殊情况下要灵活运用，做一定的变通。但是，请大家千万不要随意更改规范。如果有任何问题，请及时与我联系，我会及时更改本规范的相关代码样例和文档。 </div>
<div>基 本 要 求 </div>
<div>1. 在网站根目录中开设images common temp 三个子目录，根据需要再开设media 子目录，images目录中放不同栏目的页面都要用到的公共图片，例如公司的标志、banner 条、菜单、按钮等等；common 子目录中放css、js,、php、include 等公共文件；temp 子目录放客户提供的各种文字图片等等原始资料；media 子目录中放flash, avi, quick time 等多媒体文件 。 </div>
<div>2. 在根目录中原则上应该按照首页的栏目结构，给每一个栏目开设一个目录，根据需要在每一个栏目的目录中开设一个images 和media 的子目录用以放置此栏目专有的图片和多媒体文件，如果这个栏目的内容特别多，又分出很多下级栏目，可以相应的再开设其他目录。 </div>
<div>3. temp 目录中的文件往往会比较多，建议以时间为名称开设目录，将客户陆续提供的资料归类整理。 </div>
<div>4. 除非有特殊情况，目录、文件的名称全部用小写英文字母、数字、下划线的组合，其中不得包含汉字、空格和特殊字符；目录的命名请尽量以英文为指导，不到万不得已不要以拼音作为目录名称，经验证明，用拼音命名的目录往往连一个月后的自己都看不懂， </div>
<div>脚 本 编 写 </div>
<div>我们应该有一个脚本整体风格一致的概念，意思是一个月后和一个月前的你写的脚本风格保持一致，以及同一个工作组中不同的开发人员编写的脚本风格保持一致，因为我们不可能永远孤立的开发，你随时都有可能和三个月前的自己合作（你的客户要求改版），也经常要和工作室中不同的同事共同开发一个项目，还有可能被要求修改已经离职人员开发的脚本，当然你自己也有可能会扔下一个项目给后来的同事。 </div>
<div>1. Html 文件的通用模板： </div>
<div>&lt;html&gt; <br />&lt;!-- <br />Generator: Sub Design Studio ( <a href="http://www.eastline.net.cn/">http://www.eastline.net.cn</a>) <br />Creation Data: 2000-8-1 <br />Original Author: eastline <br />--&gt; <br />&lt;head&gt; <br />&lt;title&gt; 文档标题 &lt;/title&gt; <br />&lt;meta http-equiv=&quot;content-type&quot; content=&quot;text/html; charset=gb2312&quot;&gt; <br />&lt;meta name=&quot;author&quot; content=&quot;eastline&quot;&gt; </div>
<div>其他meta 标 记 <br />&lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;style/style.css&quot;&gt; </div>
<div>样式表定义 </div>
<div>客户端javascript 函数定义及初始化操作 <br />&lt;/head&gt; <br />&lt;body bgcolor=&quot;#ffffff&quot;&gt; <br />&hellip; &hellip; <br />&lt;/body&gt; </div>
<div>补充： <br />为了保证网站能够与下一代的web 语言xml 标准兼容，所有的HTML 标签的属性都要用单引号或者双引号括起，即我们应该写 &lt;a href=&rdquo;url&rdquo;&gt; 而不 是 &lt;a href=url&gt;. </div>
<div>2. 允许全文检索的页面，为了使Internet 上的搜索引擎能够有效检索，在频道的首页的html的&lt;head&gt;&lt;/head&gt;之间应该加入Keywords 和Description 元标记，例如 ： </div>
<div>&lt;meta name=&rdquo;keywords&rdquo; content=&rdquo;东方新干线，汽车，买车&rdquo;&gt; <br />&lt;meta name=&rdquo;description&rdquo; content=&rdquo;东方新干劲线，全球中文汽车信息第一站&rdquo;&gt; </div>
<div>3. CSS 文件的格式样例代码 ： </div>
<div>&lt;style type=&quot;text/css&quot;&gt; <br />&lt;!&mdash; <br />p { text-indent: 2em; } <br />body { font-family: &quot;宋体&quot;; font-size: 9pt; color: #000000; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px} <br />table { font-family: &quot;宋体&quot;; font-size: 9pt; line-height: 20px; color: #000000} <br />a:link { font-size: 9pt; color: #FFFFFF; text-decoration: none} <br />a:visited { font-size: 9pt; color: #99FFFF; text-decoration: none} <br />a:hover { font-size: 9pt; color: #FF9900; text-decoration: none} <br />a:active { font-size: 9pt; color: #FF9900; text-decoration: none} <br />a.1:link { font-size: 9pt; color: #3366cc; text-decoration: none} <br />a.1:visited { font-size: 9pt; color: #3366cc; text-decoration: none} <br />a.1:hover { font-size: 9pt; color: #FF9900; text-decoration: none} <br />a.1:active { font-size: 9pt; color: #FF9922; text-decoration: none} <br />.blue { font-family: &quot;宋体&quot;; font-size: 10.5pt; line-height: 20px; color: #0099FF; letter-spacing: 5em} <br />--&gt; <br />&lt;/style&gt; </div>
<div>这里尤其要注意的是a:link a:visited a:hover a:actived 的排列顺序一定要严格照上面的样例代码，否则或多或少会出问题。另外我们规定重定义的最先，伪类其次，自定义最后，便于自己和他人阅读！ </div>
<div>为了保证不同浏览器上字号保持一致，字号建议用点数pt和像素px来定义，pt一般使用中文宋体的9pt和11pt，px一般使用中文宋体12px 和14.7px 这是经过优化的字号，黑体字或者宋体字加粗时，一般选用11pt和14.7px 的字号比较合适。 </div>
<div>在写 &lt;table&gt; 互相嵌套时，严格按照的规范，对于单独的一个&lt;table&gt;来说，&lt;table&gt;&lt;tr&gt;对齐，&lt;td&gt; 缩进两个半角空格，&lt;td&gt; 中如果还有嵌套的表格，&lt;table&gt;也缩进两个半角空格，如果&lt;td&gt;中没有任何嵌套的表格，&lt;/td&gt; 结束标记应该与 &lt;td&gt; 处于同一行，不要换行， </div>
<div>如我们注意在源代码中不应有这样的代码： </div>
<div>&lt;td&gt;&lt;img src=&rdquo;../images/sample.gif&rdquo;&gt; <br />&lt;/td&gt; </div>
<div>而应该是这样的： <br />&lt;td&gt;&lt;img src=&rdquo;../images/sample.gif&rdquo;&gt;&lt;/td&gt; </div>
<div>这是因为浏览器认为换行相当于一个半角空格，以上不规范的写法相当于无意中增加一个半角空格，如果确实有必要增加一个半角空格，也应该这样写： </div>
<div>&lt;td&gt;&lt;img src=&rdquo;../images/sample.gif&rdquo;&gt; &lt;/td&gt; </div>
<div>属于同一个级别 的 &lt;table&gt; 一定是左首对齐的，另外不允许没有任何内容的空的单元格存在，高度大于等于12px 的单元格应该 在 &lt;td&gt; 和 &lt;/td&gt; 之间写一 个 如果高度小于12px, 则应该 在 &lt;td&gt; 和 &lt;/td&gt; 之间插入一个1*1 大小的透明的gif 图片，这是因为某些浏览器认为空单元格非法而不会予以解释。如果代码顺序较乱，在DW3中可以通过command-&gt;apply souce formatting进行重新整理！ </div>
<div>5. Width 和height 的写法也有统一的规范，一般情况下只有一列的表格，width 写在&lt;table&gt; 的标签内，只有一行的表格，height 写在 &lt;table&gt; 的标签内，多行多列的表格，width 和height 写在第一行或者第一列的 &lt;td&gt; 标签内。总之遵循一条原则：不出现多于一个的控制同一个单元格大小的height 和width, 保证任何一个width 和height 都是有效的，也就是你改动代码中任何一个width 和height 的数值，都应该在浏览器中看到变化。做到这一条不容易，需要较长时间的练习和思考。 </div>
<div>一 般 原 则 </div>
<div>1. 在排布表格之前，请大家一定要好好思考一个最佳的方案，表格的嵌套尽量控制在三层以内，并且应该尽量避免 &lt;colspan&gt; &lt;rowspan&gt; 两个标记，经验表明，这两个标记会带来许多麻烦。 </div>
<div>2. 一个网页要尽量避免用整个一张大表格，所有的内容都嵌套在这个大表格之内，因为浏览器在解释页面的元素时，是以表格为单位逐一显示，如果一张网页是嵌套在一个大表格之内，那么很可能造成的后果就是，当浏览者敲入网址，他要先面对一片空白很长时间，然后所有的网页内容同时出现。如果必须这样做，请使用 &lt;tbody&gt;标记，以便能够使这个大表格分块显示。 </div>
<div>3. 排版中我们经常会遇到需要进行首行缩进的处理，不要使用 或者全角空格来达到效果，规范的做法是在样式表中定义 p { text-indent: 2em; } 然后给每一段加上 &lt;p&gt; 标记，注意，一般情况下，请不要省略 &lt;/p&gt; 结束标记 。 </div>
<div>4. 原则上，我们禁止用 &lt;img width=? height=?&gt; 来人为干预图片显示的尺寸，而且建议 &lt;img&gt; 标签中不要带上width 和height 两个属性，这是因为制作过程中，图片往往需要反复的修改，这样可以避免人为干预图片显示的尺寸，尽可能的发挥浏览器自身的功能；但是这样的一个副作用是当网页还未加载图片时，不会留出图片的站位大小，可能会造成网页在加载过程中抖动（如果图片是插在一个固定大小的表格里的，不会有这个现象），尤其是当图片的尺寸较大时，这种现象会很明显，所以当预料到这种会明显导致网页抖动的情况会发生时，请大家务必在最后给 &lt;img&gt;附上 width 和 height 属性。 </div>
<div>5. 为了最大程度的发挥浏览器自动排版的功能，在一段完整的文字中请尽量不要使用&lt;br&gt; 来人工干预分段。 </div>
<div>6. 不同语种的文字之间应该有一个半角空格，但避头的符号之前和避尾的符号之后除外汉字之间的标点要用全角标点，英文字母和数字周围的括号应该使用半角括号。 </div>
<div>7. 所有的字号都应该用样式表来实现，禁止在页面中出现 &lt;font size=?&gt; 标记。 </div>
<div>8. 请不要在网页中连续出现多于一个 的 也尽量少使用全角空格（英文字符集下，全角空格会变成乱码），空白应该尽量使用 text-indent, padding, margin, hspace, vspace 以及透明的gif 图片来实现。 </div>
<div>9. 中英文混排时，我们尽可能的将英文和数字定义为verdana 和arial 两种字体。 </div>
<div>10. 行距建议用百分比来定义，常用的两个行距的值是line-height:120%/150%. </div>
<div>11. 网站中的路径全部采用相对路径，一般链接到某一目录下的缺省文件的链接路径不必写全名，如我们不必这样：&lt;a href=&rdquo;aboutus/index.htm&rdquo;&gt; 而应该这样：&lt;a href=&rdquo;aboutus/&rdquo;&gt; </div>
<div>12、嵌入图形文本的使用较大的字体，建议不要在图形中包括文本。 </div>
<div>13、&ldquo;网页大小&rdquo;定义为网页的所有文件大小的总和，包括HTML文件和所有的嵌入的对象。用户喜欢快的而不是新奇的站点。对于解调器用户，网页大小保持在34K以下为合适。 </div>
<div>文 件 命 名 原 则 </div>
<div>1. 每一个目录中应该包含一个缺省的html 文件，文件名统一用index.htm </div>
<div>2． 件名称统一用小写的英文字母、数字和下划线的组合。 </div>
<div>3. 命名原则的指导思想一是使得你自己和工作组的每一个成员能够方便的理解每一个文件的意义，二是当我们在文件夹中使用&ldquo;按名称排例&rdquo;的命令时，同一种大类的文件能够排列在一起，以便我们查找、修改、替换、计算负载量等等操作 。 </div>
<div>4. 下面以&ldquo;新闻&rdquo;（包含&ldquo;国内新闻&rdquo;和&ldquo;国际新闻&rdquo;）这个栏目来说明html 文件的命名原则 ： </div>
<div>☆ 在根目录下开设news目 录 </div>
<div>☆ 第一条缺省新闻取名index.htm </div>
<div>☆ 所有属于&ldquo;国内新闻&rdquo;的新闻依次取名为：china_1.htm, china_2.htm, &hellip; </div>
<div>☆ 所有属于&ldquo;国际新闻&rdquo;的新闻依次取名为：internation_1.htm, internation _2.htm, &hellip; </div>
<div>☆ 如果文件的数量是两位数，请将前九个文件命名为：china_01.htm, china_02.htm 以保证所有的文件能够在文件夹中正确排序。 </div>
<div>5. 图片的命名原则遵循以下几条规范 ： </div>
<div>☆ 名称分为头尾两两部分，用下划线隔开。 </div>
<div>☆ 头部分表示此图片的大类性质，例如广告、标志、菜单、按钮等等 。 </div>
<div>☆ 一般来说: </div>
<div>放置在页面顶部的广告、装饰图案等长方形的图片我们取名：banner <br />标志性的图片我们取名为：logo <br />在页面上位置不固定并且带有链接的小图片我们取名为button <br />在页面上某一个位置连续出现，性质相同的链接栏目的图片我们取名：menu <br />装饰用的照片我们取名：pic <br />不带链接表示标题的图片我们取名：title <br />依照此原则类推。 </div>
<div>☆ 尾部分用来表示图片的具体含义。 </div>
<div>☆ 下面是几个样例，大家应该能够一眼看明白图片的意义： <br />banner_sohu.gif banner_sina.gif menu_aboutus.gif menu_job.gif title_news.gif logo_police.gif logo_national.gif pic_people.jpg pic_hill.jpg. </div>
<div>目录建立的原则：以最少的层次提供最清晰简便的访问结构。 </div>
<div>根目录。<br />根目录指DNS域名服务器指向的索引文件的存放目录。<br />服务器的ftp上传目录默认为html</div>
<div>根目录文件<br />根目录只允许存放index.html和main.html文件，以及其他必须的系统文件。</div>
<div>每个语言版本存放于独立的目录。已有版本语言设置为：<br />简体中文 \gb<br />繁体中文 \big5<br />英 语 \en<br />日 语 \jp</div>
<div>每个主要功能(主菜单)建立一个相应的独立目录。</div>
<div>根目录下的images为存放公用图片目录，每个目录下私有图片存放于各自独立images目录.<br />例如：\menu1\images<br />\menu2\images</div>
<div>所有的js文件存放在根目录下统一目录\script <br />所有的CSS文件存放在根目录下的style目录<br />所有的CGI程序存放在根目录并列目录\cgi_bin目录 <br />head区是指首页HTML代码的&lt;head&gt;和&lt;/head&gt;之间的内容。 <br />必须加入的标签</div>
<div>1.公司版权注释&lt;!--- The site is designed by Maketown,Inc 06/2000 ---&gt; </div>
<div>2.网页显示字符集<br />简体中文：&lt;META HTTP-EQUIV=&quot;Content-Type&quot; CONTENT=&quot;text/html; charset=gb2312&quot;&gt;<br />繁体中文：&lt;META HTTP-EQUIV=&quot;Content-Type&quot; CONTENT=&quot;text/html; charset=BIG5&quot;&gt;<br />英 语：&lt;META HTTP-EQUIV=&quot;Content-Type&quot; CONTENT=&quot;text/html; charset=iso-8859-1&quot;&gt;</div>
<div>3.网页制作者信息<br />&lt;META name=&quot;author&quot; content=&quot;<a href="mailto:webmaster@maketown.com">webmaster@maketown.com</a>&quot;&gt;</div>
<div>4.网站简介<br />&lt;META NAME=&quot;DESCRIPTION&quot; CONTENT=&quot;xxxxxxxxxxxxxxxxxxxxxxxxxx&quot;&gt;</div>
<div>5.搜索关键字<br />&lt;META NAME=&quot;keywords&quot; CONTENT=&quot;xxxx,xxxx,xxx,xxxxx,xxxx,&quot;&gt; </div>
<div>6.网页的css规范<br />&lt;LINK href=&quot;style/style.css&quot; rel=&quot;stylesheet&quot; type=&quot;text/css&quot;&gt;<br />(参见目录及命名规范)</div>
<div>7.网页标题<br />&lt;title&gt;xxxxxxxxxxxxxxxxxx&lt;/title&gt;</div>
<div>.可以选择加入的标签</div>
<div>1.设定网页的到期时间。一旦网页过期，必须到服务器上重新调阅。<br />&lt;META HTTP-EQUIV=&quot;expires&quot; CONTENT=&quot;Wed, 26 Feb 1997 08:21:57 GMT&quot;&gt;</div>
<div>2.禁止浏览器从本地机的缓存中调阅页面内容。<br />&lt;META HTTP-EQUIV=&quot;Pragma&quot; CONTENT=&quot;no-cache&quot;&gt; </div>
<div>3.用来防止别人在框架里调用你的页面。<br />&lt;META HTTP-EQUIV=&quot;Window-target&quot; CONTENT=&quot;_top&quot;&gt;</div>
<div>4.自动跳转。<br />&lt;META HTTP-EQUIV=&quot;Refresh&quot; CONTENT=&quot;5;URL=http://www.yahoo.com&quot;&gt;<br />5指时间停留5秒。</div>
<div>5.网页搜索机器人向导.用来告诉搜索机器人哪些页面需要索引，哪些页面不需要索引。<br />&lt;META NAME=&quot;robots&quot; CONTENT=&quot;none&quot;&gt;<br />CONTENT的参数有all,none,index,noindex,follow,nofollow。默认是all。</div>
<div>6.收藏夹图标<br />&lt;link rel = &quot;Shortcut Icon&quot; href=&quot;favicon.ico&quot;&gt;</div>
<div>所有的javascript的调用尽量采取外部调用.<br />&lt;SCRIPT LANGUAGE=&quot;javascript&quot; SRC=&quot;script/xxxxx.js&quot;&gt;&lt;/SCRIPT&gt;</div>
<div>附&lt;body&gt;标签：<br />&lt;body&gt;标签不属于head区，这里强调一下，为了保证浏览器的兼容性，必须设置页面背景&lt;body bgcolor=&quot;#FFFFFF&quot;&gt; <br />网站文件命名规范</div>
<div>文件命名的原则：以最少的字母达到最容易理解的意义。</div>
<div>索引文件统一使用index.html文件名(小写)<br />index.html文件统一作为&quot;桥页&quot;，不制作具体内容，仅仅作为跳转页和meta标签页。主内容页为main.html</div>
<div>按菜单名的英语翻译取单一单词为名称。例如：<br />关于我们 \aboutus<br />信息反馈 \feedback <br />产 品 \product<br />所有单英文单词文件名都必须为小写，所有组合英文单词文件名第二个起第一个字母大写；<br />所有文件名字母间连线都为下划线</div>
<div>图片命名原则以图片英语字母为名。大小原则写同上。<br />例如：网站标志的图片为logo.gif<br />鼠标感应效果图片命名规范为&quot;图片名+_+on/off&quot;。<br />例如：menu1_on.gif/menu1_off.gif</div>
<div>五. 其它文件命名规范<br />1.js的命名原则以功能的英语单词为名。<br />例如：广告条的js文件名为:ad.js<br />2.所有的CGI文件后缀为cgi<br />所有CGI程序的配置文件为config.cgi <br />网站项目管理规范手册</div>
<div>一.概念</div>
<div>网站项目管理就是根据特定的规范、在预算范围内、按时完成的网站开发任务。</div>
<div>二.需求分析</div>
<div>项目立项 </div>
<div>我们接到客户的业务咨询，经过双方不断的接洽和了解，并通过基本的可行性讨论够，初步达成制作协议，这时就需要将项目立项。较好的做法是成立一个专门的项目小组，小组成员包括：项目经理，网页设计，程序员，测试员，编辑/文档等必须人员。项目实行项目经理制。 </div>
<div>客户的需求说明书 </div>
<div>第一步是需要客户提供一个完整的需求说明。很多客户对自己的需求并不是很清楚，需要您不断引导和帮助分析。曾经有一次，我问客户：&ldquo;您做网站的目的是什么？&rdquo;他回答：&ldquo;没有目的，只是因为别人都有，我没有！&rdquo;。这样的客户就需要耐心说明，仔细分析，挖掘出他潜在的，真正的需求。 配合客户写一份详细的，完整的需求说明会花很多时间，但这样做是值得的，而且一定要让客户满意，签字认可。把好这一关，可以杜绝很多因为需求不明或理解偏差造成的失误和项目失败。糟糕的需求说明不可能有高质量的网站。那么需求说明书要达到怎样的标准呢？简单说，包含下面几点：<br />1.正确性：每个功能必须清楚描写交付的功能；<br />2.可行性：确保在当前的开发能力和系统环境下可以实现每个需求；<br />3.必要性：功能是否必须交付，是否可以推迟实现，是否可以在削减开支情况发生时&quot;砍&quot;掉；<br />4.简明性：不要使用专业的网络术语；<br />5.检测性：如果开发完毕，客户可以根据需求检测。 </div>
<div>三.系统分析</div>
<div>网站总体设计 </div>
<div>在拿到客户的需求说明后，并不是直接开始制作，而是需要对项目进行总体设计，详细设计，出一份网站建设方案给客户。总体设计是非常关键的一步。它主要确定：<br />1.网站需要实现哪些功能；<br />2.网站开发使用什么软件，在什么样的硬件环境；<br />3.需要多少人，多少时间；<br />4.需要遵循的规则和标准有哪些。</div>
<div>同时需要写一份总体规划说明书，包括：<br />1.网站的栏目和版块；<br />2.网站的功能和相应的程序；<br />3.网站的链接结构；<br />4.如果有数据库，进行数据库的概念设计；<br />5.网站的交互性和用户友好设计。 </div>
<div>网站建设方案 </div>
<div>在总体设计出来后，一般需要给客户一个网站建设方案。很多网页制作公司在接洽业务时就被客户要求提供方案。那时的方案一般比较笼统，而且在客户需求不是十分明确的情况下提交方案，往往和实际制作后的结果会有很大差异。所以应该尽量取得客户的理解，在明确需求并总体设计后提交方案，这样对双方都有益处。网站建设方案的包括以下几个部分：<br />1.客户情况分析；<br />2.网站需要实现的目的和目标；<br />3.网站形象说明；<br />4.网站的栏目版块和结构；<br />5.网站内容的安排，相互链接关系；<br />6.使用软件，硬件和技术分析说明；<br />7.开发时间进度表；<br />8.宣传推广方案；<br />9.维护方案；<br />10.制作费用；<br />11.本公司简介：成功作品，技术，人才说明等。</div>
<div>当您的方案通过客户的认可，那么恭喜你！您可以开始动手制作网站了。但还不是真正意义上的制作，你需要进行详细设计： </div>
<div>网站详细设计 </div>
<div>总体设计阶段以比较抽象概括的方式提出了解决问题的办法。详细设计阶段的任务就是把解法具体化。详细设计主要是针对程序开发部分来说的。但这个阶段的不是真正编写程序，而是设计出程序的详细规格说明。这种规格说明的作用很类似于其他工程领域中工程师经常使用的工程蓝图，它们应该 包含必要的细节，例如：程序界面，表单，需要的数据等。程序员可以根据它们写出实际的程序代码。 </div>
<div>四. 项目实施</div>
<div>整体形象设计 </div>
<div>在程序员进行详细设计的同时，网页设计师开始设计网站的整体形象和首页。<br />整体形象设计包括标准字，Logo，标准色彩，广告语等。 首页设计包括版面，色彩，图像，动态效果，图标等风格设计，也包括banner，菜单，标题，版权等模块设计。首页一般设计1-3个不同风格，完成后，供客户选择。<br />记住：在客户确定首页风格之后，请客户签字认可。以后不得再对版面风格有大的变动，否则视为第二次设计。 </div>
<div>开发制作 <br />到这里，程序员和网页设计师同时进入全力开发阶段，需要提醒的是，测试人员需要随时测试网页与程序，发现Bug立刻记录并反馈修改。不要等到完全制作完毕再测试，这样会浪费大量的时间和精力。项目经理需要经常了解项目进度，协调和沟通程序员与网页设计师的工作。 </div>
<div>调试完善 </div>
<div>在网站初步完成后，上传到服务器，对网站进行全范围的测试。包括速度，兼容性，交互性，链接正确性，程序健壮性，超流量测试等，发现问题及时解决并记录下来。<br />为什么要记录文档呢？其实本软件工程本身就是一个文档，是一个不断充实和完善的标准。通过不断的发现问题，解决问题，修改，补充文档，使这个标准越来越规范，越来越工业化。进而使得网站开发趋向规范，趋向合理。 </div>
<div>宣传推广 </div>
<div>宣传推广的基本方法有：<br />1.网页里设置适当的META标签；<br />2.各搜索引擎登录；<br />3.准备新闻稿件在各新闻公告板发表；<br />4.合理使用Email邮件列表；<br />5.广告条交换；<br />6.付费广告。<br />至此，网站项目建设完毕，将有关网址，使用操作说明文档等提交客户验收。如果需要维护，另行签定维护项目。</div>
<div>维护 </div>
<div>网站成功推出后，长期的维护工作才刚刚开始，我们需要做到的是<br />1.及时响应客户反馈；例如可以采取Email自动回复功能，然后在1-3个工作日里解决问题，再次回复；<br />2.网站流量统计分析和相应对策；<br />3.尽量推广和使用您的网址；<br />4.网站内容的及时更新和维护。</div>
<div>五.遵循的规范</div>
<div>1.网站建设目录规范<br />2.网站文件命名规范<br />3.网站建设尺寸规范<br />4.网站首页head区代码规范<br />5.网站连接结构规范 <br />网站建设尺寸规范</div>
<div>页面标准按800*600分辨率制作，实际尺寸为778*434px<br />1024*768分辨率实际尺寸为：1002*603PX</div>
<div>页面长度原则上不超过3屏，宽度不超过1屏 </div>
<div>每个标准页面为A4幅面大小，即8.5X11英寸 </div>
<div>全尺寸banner为468*60px，半尺寸banner为234*60px，小banner为88*31px</div>
<div>另外120*90，120*60也是小图标的标准尺寸 </div>
<div>每个非首页静态页面含图片字节不超过60K，全尺寸banner不超过14K </div>]]></description>
		</item>
		    
		
		<item>
			<title>子弟规</title>
			<link>http://hxjblog.blog.sohu.com/100166716.html</link>
			<comments>http://hxjblog.blog.sohu.com/100166716.html#comment</comments>
			<dc:creator>WEB设计和开发</dc:creator>
			<pubDate>Fri, 19 Sep 2008 10:47:15 +0800</pubDate>
			<category>经典文章</category>
			<guid>http://hxjblog.blog.sohu.com/100166716.html</guid>
			<description><![CDATA[<p>子弟规</p>
<div>
<div style="WIDTH: 760px">
<p>总叙</p>
<p>弟子规 圣人训 首孝悌 次谨信</p>
<p>泛爱众 而亲仁 有余力 则学文</p>
<p>孝</p>
<p>父母呼 应勿缓 父母命 行勿懒</p>
<p>父母教 须敬听 父母责 须顺承</p>
<p>冬则温 夏则清 晨则省 昏则定</p>
<p>出必告 返必面 居有常 业无便</p>
<p>事虽小 勿擅为 苟擅为 子道亏</p>
<p>物虽小 勿私藏 苟私藏 亲心伤</p>
<p>亲所好 力为具 亲所恶 谨为去</p>
<p>身有伤 贻亲优 德有伤 贻亲羞</p>
<p>亲爱我 孝何难 亲憎我 孝方贤</p>
<p>亲有过 谏使更 怡吾色 柔吾声</p>
<p>谏不入 悦复谏 号泣随 挞无怨</p>
<p>亲有疾 药先尝 昼夜侍 不离床</p>
<p>丧三年 常悲咽 居处变 酒肉绝</p>
<p>丧尽礼 祭尽诚 事死者 如事生</p>
<p>悌</p>
<p>兄道友 弟道恭 兄弟睦 孝在中</p>
<p>财物轻 怨何生 言语忍 忿自泯</p>
<p>或饮食 或坐走 长者先 幼者后</p>
<p>长人呼 即代叫 人不在 己先到</p>
<p>称尊长 勿呼名 对尊长 勿见能</p>
<p>路遇长 疾趋揖 长无言 退恭立</p>
<p>骑下马 乘下车 过犹待 百步馀</p>
<p>长者立 幼勿坐 长者坐 命乃坐</p>
<p>尊长前 声要低 低不闻 却非宜</p>
<p>进必趋 退必迟 问起对 视勿移</p>
<p>事诸父 如事父 事诸兄 如事兄</p>
<p>谨</p>
<p>朝起早 夜眠迟 老易至 惜此时</p>
<p>晨必盥 兼漱口 便溺回 辄净手</p>
<p>冠必正 纽必结 袜与履 俱紧切</p>
<p>置冠服 有定位 勿乱顿 致污秽</p>
<p>衣贵洁 不贵华 上循分 下称家</p>
<p>对饮食 勿拣择 食适可 勿过则</p>
<p>年方少 勿饮酒 饮酒醉 最为丑</p>
<p>步从容 立端正 揖深圆 拜恭敬</p>
<p>勿践阈 勿跛倚 勿箕踞 勿摇髀</p>
<p>缓揭廉 勿有声 宽转弯 勿触棱</p>
<p>执虚器 如执盈 入虚室 如有人</p>
<p>事勿忙 忙多错 勿畏难 勿轻略</p>
<p>斗闹场 绝勿近 邪僻事 绝无问</p>
<p>将入门 问孰存 将上堂 声必扬</p>
<p>人问谁 对以名 吴与我 不分明</p>
<p>用人物 须明求 倘不问 即为偷</p>
<p>借人物 及时还 后有急 借不难</p>
<p>信</p>
<p>凡出言 信为先 诈与妄 奚可焉</p>
<p>话说多 不如少 惟其是 勿佞巧</p>
<p>奸巧语 秽污词 市井气 切戒之</p>
<p>见未真 勿轻言 知未的 勿轻传</p>
<p>事非宜 勿轻诺 苟轻诺 进退错</p>
<p>凡道字 重且舒 勿急疾 勿模糊</p>
<p>彼说长 此说短 不关己 莫闲管</p>
<p>见人善 即思齐 纵去远 以渐跻</p>
<p>见人恶 即内省 有则改 无加警</p>
<p>惟德学 惟才艺 不如人 当自励</p>
<p>若衣服 若饮食 不如人 勿生戚</p>
<p>闻过怒 闻誉乐 损友来 益友却</p>
<p>闻誉恐 闻过欣 直谅士 渐相亲</p>
<p>无心非 名为错 有心非 名为恶</p>
<p>过能改 归於无 倘掩饰 增一辜</p>
<p>泛爱众</p>
<p>凡是人 皆须爱 天同覆 地同载</p>
<p>行高者 名自高 人所重 非貌高</p>
<p>才大者 望自大 人所服 非言大</p>
<p>己有能 勿自私 人有能 勿轻訾</p>
<p>勿诌富 勿骄贫 勿厌故 勿喜新</p>
<p>人不闲 勿事搅 人不安 勿话扰</p>
<p>人有短 切莫揭 人有私 切莫说</p>
<p>道人善 即是善 人知之 愈思勉</p>
<p>扬人恶 即是恶 疾之甚 祸且作</p>
<p>善相劝 德皆建 过不规 道两亏</p>
<p>凡取与 贵分晓 与宜多 取宜少</p>
<p>将加人 先问己 己不欲 即速已</p>
<p>恩欲报 怨欲忘 报怨短 报恩长</p>
<p>待婢仆 身贵端 虽贵端 慈而宽</p>
<p>势服人 心不然 理服人 方无言</p>
<p>亲仁</p>
<p>同是人 类不齐 流俗众 仁者稀</p>
<p>果仁者 人多畏 言不讳 色不媚</p>
<p>能亲仁 无限好 德日进 过日少</p>
<p>不亲仁 无限害 小人进 百事坏</p>
<p>学文</p>
<p>不力行 但学文 长浮华 成何人</p>
<p>但力行 不学文 任己见 昧理真</p>
<p>读书法 有三到 心眼口 信皆要</p>
<p>方读此 勿慕彼 此未终 彼勿起</p>
<p>宽为限 紧用功 工夫到 滞塞通</p>
<p>心有疑 随札记 就人问 求却义</p>
<p>房室清 墙壁净 几案洁 笔砚正</p>
<p>墨磨偏 心不端 字不敬 心先病</p>
<p>列典籍 有定处 读看毕 还原处</p>
<p>虽有急 卷束齐 有缺坏 就补之</p>
<p>非圣书 屏勿视 蔽聪明 坏心志</p>
<p>勿自暴 勿自弃 圣与贤 可驯致</p></div></div>]]></description>
		</item>
		    
		
		<item>
			<title>c# 写文件</title>
			<link>http://hxjblog.blog.sohu.com/97308589.html</link>
			<comments>http://hxjblog.blog.sohu.com/97308589.html#comment</comments>
			<dc:creator>WEB设计和开发</dc:creator>
			<pubDate>Fri, 15 Aug 2008 14:02:36 +0800</pubDate>
			<category>c#</category>
			<guid>http://hxjblog.blog.sohu.com/97308589.html</guid>
			<description><![CDATA[<h2><a href="http://www.cnblogs.com/kiddo/archive/2008/03/17/1110061.html">c#写文件</a> </h2>
<div>
<p><strong><em><strong style="COLOR: black; BACKGROUND-COLOR: #99ff99">C#</strong><a name="baidusnap3"></a><strong style="COLOR: black; BACKGROUND-COLOR: #ff9999">写文件</strong></em></strong></p>
<p>2007-12-31 01:01<strong style="COLOR: black; BACKGROUND-COLOR: #99ff99">C#</strong>文件处理操作必须先导入命名空间：using System.<a name="baidusnap1"></a><strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">IO</strong>;</p>
<p>//实现背景：一个文本框、一个按钮、VS2005</p>
<p>方式一：用FileStream</p>
<p>//实例化一个保存文件对话框<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SaveFileDialog sf = new SaveFileDialog();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //设置文件保存类型<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sf.Filter = &quot;txt文件|*.txt|所有文件|*.*&quot;;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //如果用户没有输入扩展名，自动追加后缀<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sf.AddExtension = true;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //设置标题<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sf.Title = &quot;<strong style="COLOR: black; BACKGROUND-COLOR: #ff9999">写文件</strong>&quot;;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //如果用户点击了保存按钮<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(sf.ShowDialog()==DialogResult.OK)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //实例化一个文件流---&gt;与写入文件相关联<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FileStream fs = new FileStream(sf.FileName,FileMode.Create);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //获得字节<a name="baidusnap6"></a><strong style="COLOR: white; BACKGROUND-COLOR: #00aa00">数组</strong><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <a name="baidusnap5"></a><strong style="COLOR: white; BACKGROUND-COLOR: #880000">byte</strong> [] data =new UTF8Encoding().GetBytes(this.textBox1.Text);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //开始写入<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fs.Write(data,0,data.Length);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //清空缓冲区、关闭流<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fs.Flush();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fs.Close();</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>方式二：用StreamWriter</p>
<p>//实例化一个保存文件对话框<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SaveFileDialog sf = new SaveFileDialog();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //设置文件保存类型<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sf.Filter = &quot;txt文件|*.txt|所有文件|*.*&quot;;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //如果用户没有输入扩展名，自动追加后缀<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sf.AddExtension = true;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //设置标题<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sf.Title = &quot;<strong style="COLOR: black; BACKGROUND-COLOR: #ff9999">写文件</strong>&quot;;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //如果用户点击了保存按钮<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (sf.ShowDialog() == DialogResult.OK)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //实例化一个文件流---&gt;与写入文件相关联<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FileStream fs = new FileStream(sf.FileName, FileMode.Create);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //实例化一个StreamWriter--&gt;与fs相关联<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; StreamWriter sw = new StreamWriter(fs);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //开始写入<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sw.Write(this.textBox1.Text);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //清空缓冲区<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sw.Flush();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //关闭流<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sw.Close();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fs.Close();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>方式三：用BinaryWriter</p>
<p>//实例化一个保存文件对话框<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SaveFileDialog sf = new SaveFileDialog();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //设置文件保存类型<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sf.Filter = &quot;txt文件|*.txt|所有文件|*.*&quot;;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //如果用户没有输入扩展名，自动追加后缀<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sf.AddExtension = true;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //设置标题<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sf.Title = &quot;<strong style="COLOR: black; BACKGROUND-COLOR: #ff9999">写文件</strong>&quot;;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //如果用户点击了保存按钮<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (sf.ShowDialog() == DialogResult.OK)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //实例化一个文件流---&gt;与写入文件相关联<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FileStream fs = new FileStream(sf.FileName, FileMode.Create);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //实例化BinaryWriter<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BinaryWriter bw = new BinaryWriter(fs);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; bw.Write(this.textBox1.Text);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //清空缓冲区<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; bw.Flush();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //关闭流<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; bw.Close();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fs.Close();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br /></p>
<p><strong><em><strong style="COLOR: black; BACKGROUND-COLOR: #99ff99">C#</strong>缓存流示例------&gt;用缓存流复制文件</em></strong></p>
<p><strong style="COLOR: black; BACKGROUND-COLOR: #99ff99">C#</strong>文件处理操作必须先导入命名空间：using System.<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">IO</strong>;</p>
<p>背景：使用VS2005、一个按钮、一个窗体、<strong style="COLOR: black; BACKGROUND-COLOR: #99ff99">C#</strong>缓存流、把D:\KuGoo\爱得太多.wma复制到D:\并更名为love.wma,即：D:\love.wma</p>
<p>在按钮的Click事件中添加如下代码：</p>
<p>private void button1_Click(object sender, EventArgs e)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //创建两个文件流 一个是源文件相关，另一个是要写入的文件<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FileStream fs = new FileStream(@&quot;D:\KuGoo\爱得太多.wma&quot;,FileMode.Open);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FileStream fs2 = new FileStream(@&quot;D:\love.wma&quot;,FileMode.Create);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //创建一个字节<strong style="COLOR: white; BACKGROUND-COLOR: #00aa00">数组</strong>，作为两者之间的媒介<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //好比两个人拿苹果，这个字节<strong style="COLOR: white; BACKGROUND-COLOR: #00aa00">数组</strong>就好比一个篮子，一个人作死的把苹果送到篮子里面，<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //而我就可以作死得拿苹果，通过这个媒介我们互不干扰，<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //不需要互相等待【她往篮子里面放了苹果我才可以去拿】，提高了效率<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong style="COLOR: white; BACKGROUND-COLOR: #880000">byte</strong>[] data = new <strong style="COLOR: white; BACKGROUND-COLOR: #880000">byte</strong>[1024];<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //创建两个缓冲流，与两个文件流相关联<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BufferedStream bs = new BufferedStream(fs);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BufferedStream bs2= new BufferedStream(fs2);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //fs作死的读，fs2作死的写，直到fs没有字节可读fs2就不写了<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //好比，一个人作死的往篮子里面丢苹果，另一个人作死得往篮子里面拿苹果，直到篮子里面没有苹果拿了为止<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //即--&gt;那个人没有苹果往篮子里面放了<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while(fs.Read(data,0,data.Length)&gt;0)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fs2.Write(data,0,data.Length);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fs2.Flush();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //关闭流，好比两个人累了，都要休息 呵呵o(&cap;_&cap;)o...<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fs.Close();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fs2.Close();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;</p>
<p><strong><em><strong style="COLOR: black; BACKGROUND-COLOR: #99ff99">C#</strong>内存流示例-----&gt;用内存流来读取图片</em></strong><br /><strong style="COLOR: black; BACKGROUND-COLOR: #99ff99">C#</strong>文件处理操作必须先导入命名空间：using System.<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">IO</strong>;</p>
<p>背景：一个窗体、一个pictureBox、一个lable[没有选择图片,lable的text为&quot;图片未选择&quot;],在pictureBox1的Click事件中添加如下代码：</p>
<p>private void pictureBox1_Click(object sender, EventArgs e)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //实例化一个打开文件对话框<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; OpenFileDialog op = new OpenFileDialog();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //设置文件的类型<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; op.Filter = &quot;JPG图片|*.jpg|GIF图片|*.gif&quot;;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //如果用户点击了打开按钮、选择了正确的图片路径则进行如下操作：<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(op.ShowDialog()==DialogResult.OK)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //清空文本<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.label1.Text = &quot;&quot;;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //实例化一个文件流<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FileStream fs = new FileStream(op.FileName, FileMode.Open);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //把文件读取到字节<strong style="COLOR: white; BACKGROUND-COLOR: #00aa00">数组</strong><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong style="COLOR: white; BACKGROUND-COLOR: #880000">byte</strong>[] data = new <strong style="COLOR: white; BACKGROUND-COLOR: #880000">byte</strong>[fs.Length];<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fs.Read(data, 0, data.Length);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fs.Close();</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //实例化一个内存流---&gt;把从文件流中读取的内容[字节<strong style="COLOR: white; BACKGROUND-COLOR: #00aa00">数组</strong>]放到内存流中去<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MemoryStream ms = new MemoryStream(data);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //设置图片框 pictureBox1中的图片<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.pictureBox1.Image = Image.FromStream(ms);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p></div>]]></description>
		</item>
		    
		
		<item>
			<title>javascript   字符串函数</title>
			<link>http://hxjblog.blog.sohu.com/93967019.html</link>
			<comments>http://hxjblog.blog.sohu.com/93967019.html#comment</comments>
			<dc:creator>WEB设计和开发</dc:creator>
			<pubDate>Tue, 8 Jul 2008 10:36:22 +0800</pubDate>
			<guid>http://hxjblog.blog.sohu.com/93967019.html</guid>
			<description><![CDATA[<p>string.toLowerCase()&nbsp; 小写</p>
<p>string.toUpperCase()&nbsp; 大写<br />string.substring(num1,num2); 截取字符串，从num1后到num2</p>
<p>string.substr(num1,num2); 截取字符串，从num1开始 num2个长度</p>
<p>比较<br />&quot;absdefg&quot;.substring(1,3);&nbsp;&nbsp; //&nbsp; bs<br />&quot;absdefg&quot;.substr(1,3);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //bsd</p>
<p>url.lastIndexOf(&quot;s&quot;)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 查询某个字符最后的</p>
<p>url.length</p>]]></description>
		</item>
		    
		
		<item>
			<title>Winrar  破解</title>
			<link>http://hxjblog.blog.sohu.com/93868386.html</link>
			<comments>http://hxjblog.blog.sohu.com/93868386.html#comment</comments>
			<dc:creator>WEB设计和开发</dc:creator>
			<pubDate>Mon, 7 Jul 2008 08:32:34 +0800</pubDate>
			<category>电脑技术</category>
			<guid>http://hxjblog.blog.sohu.com/93868386.html</guid>
			<description><![CDATA[<p>转自[http://blog.csdn.net/wp_84/archive/2007/09/01/1768383.aspx]</p>
<p>Winrar这款软件想必大家都用过,但是也正是因为它是免费的,在试用期过了之后,我们每次使用都会出现提示注册的对话框.虽然,不影响软件的使用,但是每次使用的时候出现这个提示信息,想必会影响到你的心情. </p>
<p><font style="FONT-SIZE: 14px" face="Courier New">&nbsp;&nbsp;&nbsp; 好了,今天我将自己的破解文件免费献给大家.</font></p>
<p><font style="FONT-SIZE: 14px" face="Courier New">&nbsp;&nbsp;&nbsp; 博客没有存储文件的功能,所以我不能把自己的破解文件给大家传上来.现在,我告诉大家如何做同样也能达到效果:</font></p>
<p><font style="FONT-SIZE: 14px" face="Courier New">&nbsp;&nbsp;&nbsp; 1.首先到http://www.winrar.com.cn下载Winrar3.71正式版,并且进行安装.(好像是废话......)</font></p>
<p><font style="FONT-SIZE: 14px" face="Courier New">&nbsp;&nbsp;&nbsp; 2.新建一个文本文件(名字是&quot;RarReg&quot;,扩展名是&quot;.txt&quot;),将下面部分的内容复制到文本文件中.</font></p>
<p><font style="FONT-SIZE: 14px" face="Courier New">&nbsp;</font></p>
<p><font style="FONT-SIZE: 14px" face="Courier New">RAR registration data<br />cafevn<br />Single PC usage license<br />UID=bff246844b5c5708595a<br />6412212250595aa9dfb5db06ca5c5b29cb38d3c346df871e689fcd<br />26d40cd6724c7fea7a256035c6ab9048e2c5c62f0238f183d28519<br />aa87488bf38f5b634cf28190bdf438ac593b1857cdb55a7fcb0eb0<br />c3e4c2736090b3dfa45384e08e9de05c58601faaa5bf2fd204421a<br />0af4a66c04f5f95c750b942c262c8defc92d51753d37c41772b696<br />179d2401468afd8cdf6b526bd713b62d9d1c8b39f8f5c862600bd1<br />6f2be0ead43d89dc20da9c292bbc37165bb690729a430802790431</font></p>
<p><font style="FONT-SIZE: 14px" face="Courier New">&nbsp;</font></p>
<p><br /><font style="FONT-SIZE: 14px" face="Courier New">&nbsp;&nbsp;&nbsp; 3.最后一步把名字是&quot;RarReg.txt&quot;的文件扩展名&quot;txt&quot;改成&quot;key&quot;,使文件&quot;RarReg.txt&quot;改名成&quot;RarReg.key&quot;,好了把修改好的&quot;RarReg.key&quot;文件拷贝到Winrar的安装目录就好了,打开你的Winrar看看还有没有讨厌的提示框呢?^_^</font></p>
<p><font style="FONT-SIZE: 14px" face="Courier New">&nbsp;&nbsp;&nbsp; 注意事项:</font></p>
<p><font style="FONT-SIZE: 14px" face="Courier New">&nbsp;&nbsp;&nbsp; 文件的名字是&quot;RarReg&quot;注意大小写,文件的扩展名是&quot;.key&quot;,祝你成功......&nbsp;</font></p>]]></description>
		</item>
		    
		
		<item>
			<title>textarea 显示图片</title>
			<link>http://hxjblog.blog.sohu.com/93649447.html</link>
			<comments>http://hxjblog.blog.sohu.com/93649447.html#comment</comments>
			<dc:creator>WEB设计和开发</dc:creator>
			<pubDate>Fri, 4 Jul 2008 10:44:52 +0800</pubDate>
			<category>HTML和JAVASCRIPT</category>
			<guid>http://hxjblog.blog.sohu.com/93649447.html</guid>
			<description><![CDATA[&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0 Transitional//EN&quot;&gt;<br />&lt;HTML&gt;<br />&lt;HEAD&gt;<br />&lt;TITLE&gt;&lt;/TITLE&gt;<br />&lt;/HEAD&gt;<br />&lt;BODY&gt;<br />&lt;textarea id=&quot;oText&quot; cols=&quot;40&quot; rows=&quot;8&quot;&gt;&lt;/textarea&gt;<br />&lt;script type=&quot;text/javascript&quot;&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />function img(){<br />var oText = document.getElementById(&quot;oText&quot;);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var oImg = document.createElement(&quot;IMG&quot;);<br />&nbsp;&nbsp;var nr;<br />&nbsp;&nbsp;oImg.src = &quot;http://www.baidu.com/img/baidu.gif&quot;;<br />&nbsp;&nbsp;oText.appendChild(oImg);<br />&nbsp;&nbsp;alert(oText.innerHTML);<br />}<br />&lt;/script&gt;<br />&lt;img src=&quot;http://www.baidu.com/img/baidu.gif&quot; onclick=&quot;img()&quot;&gt;<br />&lt;/BODY&gt;<br />&lt;/HTML&gt;]]></description>
		</item>
		    
		
		<item>
			<title>在Linux系统下如何利用SSH进行远程控制</title>
			<link>http://hxjblog.blog.sohu.com/88110041.html</link>
			<comments>http://hxjblog.blog.sohu.com/88110041.html#comment</comments>
			<dc:creator>WEB设计和开发</dc:creator>
			<pubDate>Thu, 22 May 2008 09:50:44 +0800</pubDate>
			<category>linux和unix</category>
			<guid>http://hxjblog.blog.sohu.com/88110041.html</guid>
			<description><![CDATA[<div>在Linux系统下如何利用SSH进行远程控制</div>
<div>2007年11月17日 星期六 下午 08:14</div>
<table style="TABLE-LAYOUT: fixed">
<tbody>
<tr>
<td>
<div><font face="宋体" color="#000080">来源: 赛迪网<br />　　网络被攻击，很多情况是由于服务器提供了Telnet服务引起的。的确，对于UNIX系统，如果要远程管理它，必定要使用远程终端，而要使用远程终端，自然要在服务器上启动Telnet服务。但是Telnet服务有一个致命的弱点&mdash;&mdash;它以明文的方式传输用户名及口令，所以，很容易被别有用心的人窃取口令。目前，一种有效代替Telnet服务的有用工具就是SSH服务。SSH客户端与服务器端通讯时，用户名及口令均进行了加密，有效防止了对口令的窃听。本文向大家介绍运行在常用操作系统上的SSH服务器软件包的使用。 <br /><br />　　首先，SSH软件包由两部分组成，一部分是服务器端软件包，另一部分是客户软件包。针对UNIX、Linux系统，这两个软件包是分开打包在两个不同的文件中的。在Windows 9x/NT/2000中，也分为两部分，不同之处在于，服务器软件包只能运行在Windows NT及 windows 2000 Server以上的版本中，而客户端SSH可以运行在所有的Windows系统中。此外，SSH还分为SSH1及SSH2两个版本，SSH1是一个完全免费的软件包，而SSH2在商业使用时则要付费。由其命名也可知SSH1是第一版，它的功能没有SSH2强大，但是，由于它是免费的，所以广泛地使用在很多网站中。SSH2中加入了很多功能，并且兼容SSH1服务器，可以对SSH1的客户端提供很好的服务支持。所以，如果你的系统中安装了SSH2，那就没有必要再安装SSH1软件包了。 <br /><br />　　UNIX/Linux下SSH2安装步骤 <br /><br />　　1.下载软件包，下载地址www.ssh.com，下载最新软件包SSH2，最好下载源程序软件包自己进行自行编译。 <br />　　2.解压及安装： <br /># tar -zxvf ssh2-2.4.0.tar.gz <br /># cd ssh2-2.4.0 <br /># ./configure <br /># make <br />#make install <br /><br />　　安装完成。这一过程实际上将服务器软件包及客户端软件一起安装了，不必再次安装客户端软件包。编译好的二进制软件包以rpm格式存放在FTP://ftp.ssh.com/pub/ssh/rpm目录下。它是一个给非商业用户使用的软件包，软件包名称为：ssh-2.4.0-1.i386.rpm，其中包含了对X Window的支持，另一个不支持X Window的软件包为ssh- .0-1nox.i386.rpm，下载后可以直接安装。安装程序将SSH2软件包安装在/usr/local/bin及 /usr/local/sbin下。 <br /><br />　　Windows NT上安装SSH <br /><br />　　在NT及Windows 2000 Server环境下，可选择的服务器软件有：Vshell、ssh2-2.4.0.win-server。 Vshell是由Van Dyke提供的一个可以在Windows NT/2000环境下提供SSH2服务器的软件包，其下载地址如下： //www.vandyke.com/download/index.Html。另一个运行在Windows环境下的SSH服务器是 SSHWinServer.exe，可以直接从ftp://ftp.ssh.com/pub/ssh目录下下载。 <br /><br />　　Windows环境下的安装十分简单，本文不再多介绍。 <br /><br />　　与UNIX不同，在Windows环境下，需要分别安装服务器及客户端软件包。运行在Windows环境下的客户端软件，也可以从以上两个站点下载得到，文件名分别为SecureCRT及SSHWin-2.4.0-pl2。 <br /><br />　　关于密钥的准备工作 <br /><br />　　A.服务器端产生用户的自己的加密密钥及对外公开使用的公钥。在UNIX环境下，产生密钥的方法如下： <br /><br />　　要求用户输入一个长的认证字串，这个字串的功能同passWord相当，但是，它更长，一般是在20个字符以内。再次输入相同的字串以确认输入的正确，之后，系统产生一对密钥及公钥。将公钥复制到本地，以便客户端对服务器发送的信息进行解密用。当然，如果你不复制，在第一次登录时，服务器会将它的公钥自动推给客户机，以便客户机能对服务器提供的信息进行解密识别。 <br /><br />　　B.客户端产生用户的加密密钥及公钥。客户端产生自己的密钥及公钥的方法与服务器端相同。而Windows环境下的一些支持SSH的客户端软件都采用自己生成的方法，具体情况各不相同，但是可以肯定的是所有的支持SSH的客户端都可以而且必须产生。以sshWin2.4为例说明如下： <br /><br />　　打开选单：Edit&rarr;Settings&rarr;Globe settings&rarr;User keys&rarr;Generate New keypairs，按照提示会自动产生新的密钥及公钥对。 <br /><br />　　最后，将客户机产生的公钥复制到服务端的主机上用户的目录中(在UNIX下应为/home/usrname/.ssh2目录中)。不同的版本的SSH对公钥及密钥的文件名有特定的要求，具体情况请阅读软件包中的安装说明。 <br /><br />　　启动SSH服务器 <br /><br />　　在UNIX/Linux环境下，服务器程序放置在/usr/local/sbin目录下，启动方法如下： <br /># sshd <br /># ps x <br />　　可以看到SSHD已经启动了。如果不希望每次重启动系统，都要手工运行启动SSHD，则可以自己写一个脚本，放置在init.d目录下，让系统启动后，自动执行SSHD服务的启动工作。或者直接在rc.local中加入一行/usr/local/sbin/sshd也可。 Windows NT/2000/下启动SSH2 Server，运行程序组中的start SSH2 Server即可。 <br />　　使用SSH <br /><br />　　客户端在UNIX/Linux系统中就是SSH，存放在/usr/local/bin目录下。其中有SSH1、SSH2、scp等客户端工具，使用SSH登录远程主机方法如下<br />host.ip.of.remote <br /><br />　　如同使用Telnet一样，不同之处是要求用户输入认证字串，如果认证字串通过了认证，则用户直接登录成功; 如果不成功，则是要求用户输入系统口令。口令认证成功后，用户也可以成功登录系统。从使用上看，与Telnet没有什么不同之处。而且有了SSH客户端软件，如果你要上传文件，不必向以前一样再开一个FTP窗口，再次认证，然后上传文件。使用SSH客户端自带的scp工具，就可以直接将文件上传到远端服务器上。使用方法如下： <br /><br />host1:dir/filename host2:/home/abc/filename <br /><br />　　在Windows系统中，可供使用的SSH客户端有：SecurCRT，也即CRT的支持SSH的版本(下载地址： //www.vandyke.com/)，这是一个很好的支持SSH的远程终端，它同时支持SSH1及SSH2。用户可以根据服务器端自由选择，让它支持相应的标准。另一个可供选择的是ssh.com提供的客户端，下载地址：ftp://ftp.ssh.com/pub/ssh/SSHWin-2.4.0-pl2.exe，这是新版本的SSH2客户端。另外，还有支持SSH的FTP客户端工具，其中sshwin-2.4中就有一个SSH Secure File Transfer ient，它可以用来在两个主机之间传输加密后的文件。也即scp的功能。配合SecureCRT的也有一个相应的支持SSH的FTP工具，其名称为： SecureFX，可以从www.vandyke.com/下载使用。 <br /><br />　　由于种种原因，一些支持SSH的GUI客户端不一定会很好地支持以上各个服务器，大家可以自行组合以上工具，找到适合自己的工具。一般来说，在 UNIX下的客户端对各种服务器的支持是最好的。通常在选择服务器及客户端软件时，最好选择同一软件商的产品，这样不会出现不兼容的问题。 <br /><br />　　需要补充的是，如果你既想使用SSH2又不想付费，那么一个可供选择的自由软件是Openssh，它是一个遵守GPL协议的软件包，同时支持SSH1 及SSH2标准，是另一个被广泛使用的SSH软件包(可以从www.openssh.com下载)。Openssh的最新版本为Openssh- 2.5.1，提供全部源码。不过，在编译前，应仔细阅读其说明文件。编译过程中要用到zlib及openssl两个软件包，用户首先需要下载并安装它们，之后再编译openssh。具体过程请阅读软件包中的install文件。</font></div></td></tr></tbody></table>]]></description>
		</item>
		    
		
		<item>
			<title>好看的按钮css</title>
			<link>http://hxjblog.blog.sohu.com/87491923.html</link>
			<comments>http://hxjblog.blog.sohu.com/87491923.html#comment</comments>
			<dc:creator>WEB设计和开发</dc:creator>
			<pubDate>Fri, 16 May 2008 09:12:07 +0800</pubDate>
			<category>HTML和JAVASCRIPT</category>
			<guid>http://hxjblog.blog.sohu.com/87491923.html</guid>
			<description><![CDATA[<p>&lt;style&gt;<br />.btn {<br />&nbsp;BORDER-RIGHT: #7b9ebd 1px solid; PADDING-RIGHT: 2px; BORDER-TOP: #7b9ebd 1px solid; PADDING-LEFT: 2px; FONT-SIZE: 12px; FILTER: progid:DXImageTransform.Microsoft.Gradient(GradientType=0, StartColorStr=#ffffff, EndColorStr=#cecfde); BORDER-LEFT: #7b9ebd 1px solid; CURSOR: hand; COLOR: black; PADDING-TOP: 2px; BORDER-BOTTOM: #7b9ebd 1px solid<br />}<br />.btn1_mouseout {<br />&nbsp;BORDER-RIGHT: #7EBF4F 1px solid; PADDING-RIGHT: 2px; BORDER-TOP: #7EBF4F 1px solid; PADDING-LEFT: 2px; FONT-SIZE: 12px; FILTER: progid:DXImageTransform.Microsoft.Gradient(GradientType=0, StartColorStr=#ffffff, EndColorStr=#B3D997); BORDER-LEFT: #7EBF4F 1px solid; CURSOR: hand; COLOR: black; PADDING-TOP: 2px; BORDER-BOTTOM: #7EBF4F 1px solid<br />}<br />.btn1_mouseover {<br />&nbsp;BORDER-RIGHT: #7EBF4F 1px solid; PADDING-RIGHT: 2px; BORDER-TOP: #7EBF4F 1px solid; PADDING-LEFT: 2px; FONT-SIZE: 12px; FILTER: progid:DXImageTransform.Microsoft.Gradient(GradientType=0, StartColorStr=#ffffff, EndColorStr=#CAE4B6); BORDER-LEFT: #7EBF4F 1px solid; CURSOR: hand; COLOR: black; PADDING-TOP: 2px; BORDER-BOTTOM: #7EBF4F 1px solid<br />}<br />.btn2 {padding: 2 4 0 4;font-size:12px;height:23;background-color:#ece9d8;border-width:1;}<br />.btn3_mouseout {<br />&nbsp;BORDER-RIGHT: #2C59AA 1px solid; PADDING-RIGHT: 2px; BORDER-TOP: #2C59AA 1px solid; PADDING-LEFT: 2px; FONT-SIZE: 12px; FILTER: progid:DXImageTransform.Microsoft.Gradient(GradientType=0, StartColorStr=#ffffff, EndColorStr=#C3DAF5); BORDER-LEFT: #2C59AA 1px solid; CURSOR: hand; COLOR: black; PADDING-TOP: 2px; BORDER-BOTTOM: #2C59AA 1px solid<br />}<br />.btn3_mouseover {<br />&nbsp;BORDER-RIGHT: #2C59AA 1px solid; PADDING-RIGHT: 2px; BORDER-TOP: #2C59AA 1px solid; PADDING-LEFT: 2px; FONT-SIZE: 12px; FILTER: progid:DXImageTransform.Microsoft.Gradient(GradientType=0, StartColorStr=#ffffff, EndColorStr=#D7E7FA); BORDER-LEFT: #2C59AA 1px solid; CURSOR: hand; COLOR: black; PADDING-TOP: 2px; BORDER-BOTTOM: #2C59AA 1px solid<br />}<br />.btn3_mousedown<br />{<br />&nbsp;BORDER-RIGHT: #FFE400 1px solid; PADDING-RIGHT: 2px; BORDER-TOP: #FFE400 1px solid; PADDING-LEFT: 2px; FONT-SIZE: 12px; FILTER: progid:DXImageTransform.Microsoft.Gradient(GradientType=0, StartColorStr=#ffffff, EndColorStr=#C3DAF5); BORDER-LEFT: #FFE400 1px solid; CURSOR: hand; COLOR: black; PADDING-TOP: 2px; BORDER-BOTTOM: #FFE400 1px solid<br />}<br />.btn3_mouseup {<br />&nbsp;BORDER-RIGHT: #2C59AA 1px solid; PADDING-RIGHT: 2px; BORDER-TOP: #2C59AA 1px solid; PADDING-LEFT: 2px; FONT-SIZE: 12px; FILTER: progid:DXImageTransform.Microsoft.Gradient(GradientType=0, StartColorStr=#ffffff, EndColorStr=#C3DAF5); BORDER-LEFT: #2C59AA 1px solid; CURSOR: hand; COLOR: black; PADDING-TOP: 2px; BORDER-BOTTOM: #2C59AA 1px solid<br />}<br />.btn_2k3 {<br />&nbsp;BORDER-RIGHT: #002D96 1px solid; PADDING-RIGHT: 2px; BORDER-TOP: #002D96 1px solid; PADDING-LEFT: 2px; FONT-SIZE: 12px; FILTER: progid:DXImageTransform.Microsoft.Gradient(GradientType=0, StartColorStr=#FFFFFF, EndColorStr=#9DBCEA); BORDER-LEFT: #002D96 1px solid; CURSOR: hand; COLOR: black; PADDING-TOP: 2px; BORDER-BOTTOM: #002D96 1px solid<br />}<br />&lt;/style&gt;<br />&lt;body&gt;</p>
<p>&lt;button class=btn title=&quot;好看的按钮&quot;&gt;好看的按钮&lt;/button&gt;&lt;P&gt;&lt;/p&gt;<br />&lt;button <br />class=btn1_mouseout <br />&nbsp;<br />&nbsp;title=&quot;好看的按钮&quot;&gt;好看的按钮&lt;/button&gt;&nbsp;&nbsp; <br />&lt;button <br />class=btn1_mouseout <br />&nbsp; DISABLED&gt;好看的按钮&lt;/button&gt;<br />&lt;P&gt;<br />&lt;button class=btn2 title=&quot;好看的按钮&quot;&gt;好看的按钮&lt;/button&gt;<br />&lt;P&gt;<br />&lt;button class=btn3_mouseout<br />&nbsp; title=&quot;好看的按钮&quot;&gt;好看的按钮&lt;/button&gt;<br />&lt;P&gt;<br />&lt;button class=btn_2k3 title=&quot;好看的按钮&quot;&gt;好看的按钮&lt;/button&gt;</p>
<p>&nbsp;<br /><a href="http://zt.blog.sohu.com/s2008/wenchuandizhen/" target="_blank"><img alt="再小的力量也是一种支持" src="http://js1.pp.sohu.com.cn/ppp/blog/styles_ppp/images/banner_512_support.jpg" /></a><br /><br /></p>]]></description>
		</item>
		    
		
		<item>
			<title>HTML应用程序</title>
			<link>http://hxjblog.blog.sohu.com/87489394.html</link>
			<comments>http://hxjblog.blog.sohu.com/87489394.html#comment</comments>
			<dc:creator>WEB设计和开发</dc:creator>
			<pubDate>Fri, 16 May 2008 08:34:06 +0800</pubDate>
			<category>HTML和JAVASCRIPT</category>
			<guid>http://hxjblog.blog.sohu.com/87489394.html</guid>
			<description><![CDATA[&nbsp; 
<div>
<h1>HTML应用程序(HTML Application)</h1>
<h3><a href="http://www.builder.com.cn/" target="_blank">开发者在线 Builder.com.cn</a> 更新时间:<cite>2008-03-29</cite>作者：iLinux 来源:CSDN</h3>
<h2>本文关键词： <a href="http://www.builder.com.cn/files/list-0-0-110646-1-1.htm" target="_blank">程序</a> <a href="http://www.builder.com.cn/files/list-0-0-107342-1-1.htm" target="_blank">应用</a> <a href="http://www.builder.com.cn/files/list-0-0-108134-1-1.htm" target="_blank">Html</a> <a href="http://www.builder.com.cn/files/list-0-0-54396-1-1.htm" target="_blank">Web开发</a> </h2></div>
<p><b>hta</b><b>就是一个</b><b>HTML</b><b>应用程序</b><b>(HTML Application)</b><b>，</b><b><br /><br /></b>hta就是一个HTML应用程序(HTML Application)，只要你双击就能运行 <br /><br />只要简单的用.hta为扩展名保存HTML页面就行了 <br /><br />下面的一个标准的HTML应用程序： <br />&lt;HTML&gt; <br />&lt;HEAD&gt; <br />&lt;TITLE&gt;HTA Demo&lt;/TITLE&gt; <br />&lt;HTA:APPLICATION ID=&ldquo;oHTA&rdquo; <br />APPLICATIONNAME=&ldquo;myApp&rdquo; <br />BORDER=&ldquo;thin&rdquo; <br />BORDERSTYLE=&ldquo;normal&rdquo; <br />CAPTION=&ldquo;yes&rdquo; <br />ICON=&ldquo;filename.ico&rdquo; <br />MAXIMIZEBUTTON=&ldquo;yes&rdquo; <br />MINIMIZEBUTTON=&ldquo;yes&rdquo; <br />SHOWINTASKBAR=&ldquo;no&rdquo; <br />INGLEINSTANCE=&ldquo;no&rdquo; <br />SYSMENU=&ldquo;yes&rdquo; <br />VERSION=&ldquo;1.0&rdquo; <br />WINDOWSTATE=&ldquo;normal&rdquo;&gt; <br />&lt;/HEAD&gt; <br />&lt;BODY SCROLL=&ldquo;no&rdquo;&gt; <br />&lt;H1&gt;HELLO, WORLD!&lt;/H1&gt; <br />&lt;/BODY&gt; <br />&lt;/HTML&gt; <br /><br /><br />它多了HTA:APPLICATION标签。其实就是这个标签提供了一系列面向应用程序的功能。 <br /><br />它的各种属性： <br /><br />APPLICATIONNAME属性(applicationName) <br />此属性为设置HTA的名称。 <br /><br />BORDER属性(border) <br />此属性为设置为HTA的窗口边框类型，默认值为 thick。 <br />它可以设为 thick 指定窗口为粗边框 <br />dialog window 指定窗口为对话框 <br />none 指定窗口无边框 <br />thin 指定窗口为窄边框 <br /><br />BORDERSTYLE属性(borderStyle) <br />此属性为设置HTA窗口的边框格式，默认值为 normal。 <br />它可以设为 <br />normal 普通边框格式 <br />complex 凹凸格式组合边框 <br />raised 凸出的3D边框 <br />static 3D边框格式 <br />sunken 凹进的3D边框 <br /><br />CAPTION属性(caption) <br />此属性为设置HTA窗口是否显示标题栏或标题，默认值为 yes。 <br /><br />ICON属性(icon) <br />此属性为设置应用程序的图标。 <br /><br />MAXIMIZEBUTTON属性(maximizeButton) <br />此属性为设置是否在HTA窗口中显示最大化按钮，默认值为 yes。 <br /><br />MINIMIZEBUTTON属性(minimizeButton) <br />此属性为设置是否在HTA窗口中显示最小化按钮，默认值为 yes。 <br /><br />SHOWINTASKBAR属性(showInTaskBar) <br />此属性为设置是否在任务栏中显示此应用程序，默认值为 yes。 <br /><br />SINGLEINSTANCE属性(singleInstance) <br />此属性为设置是否此应用程序同时只能运行一次。次属性以APPLICATIONNAME属性作为标识，默认值为 no。 <br /><br />SYSMENU属性(sysMenu) <br />此属性为设置是否在HTA窗口中显示系统菜单，默认值为 yes。 <br /><br />VERSION属性(version) <br />此属性为设置应用程序的版本，默认值为空。 <br /><br />WINDOWSTATE属性(windowState) <br />此属性为设置HTA窗口的初始大小，默认值为 normal。 <br />它可以设为 normal 默认大小 <br />minmize 最小化 <br />maximize 最大化 <br /><br />以上括号中的是在脚本引用的属性。在脚本中以上属性皆为只读属性。此外，在脚本中还可以使用commandLine属性来检索应用程序启动时的参数。 <br />在HTA中还可以使用html中的绝大多数标签、脚本等。 <br /><br />将用了fso的页面作成hta，双击运行时就没有安全警告的<br />var Controller = WScript.CreateObject(&quot;WSHController&quot;);<br />var RemoteScript = Controller.CreateScript(&quot;test.js&quot;, &quot;remoteserver&quot;);<br />WScript.ConnectObject(RemoteScript, &quot;remote_&quot;);<br />RemoteScript.Execute();<br /><br />while (RemoteScript.Status != 2) <br /><br />WScript.DisconnectObject(RemoteScript);<br /><br />function remote_Error()<br />{<br />var theError = RemoteScript.Error;<br />WScript.Echo(&quot;Error &quot; + theError.Number + &quot; - Line: &quot; + theError.Line + &quot;, Char: &quot; + theError.Character + &quot;nDescription: &quot; + theError.Description);<br />WScript.Quit(-1);<br />}</p>
<p>&nbsp;<br /><a href="http://zt.blog.sohu.com/s2008/wenchuandizhen/" target="_blank"><img alt="再小的力量也是一种支持" src="http://js1.pp.sohu.com.cn/ppp/blog/styles_ppp/images/banner_512_support.jpg" /></a><br /><br /></p>]]></description>
		</item>
		    
		
	</channel>
</rss>
