﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-编程语言杂谈</title><link>http://www.cppblog.com/wuwu/</link><description /><language>zh-cn</language><lastBuildDate>Sun, 05 Apr 2026 16:01:28 GMT</lastBuildDate><pubDate>Sun, 05 Apr 2026 16:01:28 GMT</pubDate><ttl>60</ttl><item><title>迭代器的设计</title><link>http://www.cppblog.com/wuwu/archive/2014/10/11/208539.html</link><dc:creator>呜呜</dc:creator><author>呜呜</author><pubDate>Sat, 11 Oct 2014 09:10:00 GMT</pubDate><guid>http://www.cppblog.com/wuwu/archive/2014/10/11/208539.html</guid><wfw:comment>http://www.cppblog.com/wuwu/comments/208539.html</wfw:comment><comments>http://www.cppblog.com/wuwu/archive/2014/10/11/208539.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cppblog.com/wuwu/comments/commentRss/208539.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/wuwu/services/trackbacks/208539.html</trackback:ping><description><![CDATA[<div>对于一个编程语言来说，定制各种需要的数据结构是一项重要的能力，如果定制的手段非常精简，将会获得极大的开发效率提升，而构建的手段针对当前计算机底层很贴合，则可以有机会获得非常高的性能。<br />对于定制数据结构来说，最基础的一项操作就是，遍历所有内容，因为有了遍历，基本上就有了&#8220;读&#8221;的功能。<br />大家都知道，不同的数据结构无论是外在形态还是内部结构都相差甚远，有什么办法能提供一个统一的接口对内部所有数据进行便利呢？这就是所谓&#8220;迭代器&#8221;。<br />以C++为例，遍历一个vector：<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">#include&nbsp;</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">iostream</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; "><br />#include&nbsp;</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">vector</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; "><br /><br /></span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;main(</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;argc,&nbsp;</span><span style="color: #0000FF; ">char</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">*</span><span style="color: #000000; ">argv[])<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;std::vector</span><span style="color: #000000; ">&lt;</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;a&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;{</span><span style="color: #000000; ">1</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">2</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">3</span><span style="color: #000000; ">};<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">for</span><span style="color: #000000; ">&nbsp;(std::vector</span><span style="color: #000000; ">&lt;</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">::iterator&nbsp;iter&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;a.begin();&nbsp;iter&nbsp;</span><span style="color: #000000; ">!=</span><span style="color: #000000; ">&nbsp;a.end();&nbsp;</span><span style="color: #000000; ">++</span><span style="color: #000000; ">iter)<br />&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;std::cout&nbsp;</span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">*</span><span style="color: #000000; ">iter&nbsp;</span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">&nbsp;std::endl;<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">0</span><span style="color: #000000; ">;<br />}</span></div><br /><br />我们可以看到，它定义了一个新类型，叫std::vector&lt;int&gt;::iterator，这么个类型就是迭代器类型。从直觉上看他就是给访问数据提供个类似指针的接口而已，实际上这个迭代器变量还有另一个重要功能，就是&#8220;保存状态&#8221;，有了这个状态，才能知道下一次迭代能得到什么结果。<br />后来C++11标准出来后在语法方面进行了加强，淡化了保存状态这么个过程，写出的代码显得更加简洁：<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">#include&nbsp;</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">iostream</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; "><br />#include&nbsp;</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">vector</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; "><br /><br /></span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;main(</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;argc,&nbsp;</span><span style="color: #0000FF; ">char</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">*</span><span style="color: #000000; ">argv[])<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;std::vector</span><span style="color: #000000; ">&lt;</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;a&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;{</span><span style="color: #000000; ">1</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">2</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">3</span><span style="color: #000000; ">};<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">for</span><span style="color: #000000; ">&nbsp;(auto&nbsp;iter&nbsp;:&nbsp;a)<br />&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;std::cout&nbsp;</span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">&nbsp;iter&nbsp;</span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">&nbsp;std::endl;<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">0</span><span style="color: #000000; ">;<br />}</span></div><br />当然底层该有的操作是不会少的。<br /><br />有了&#8220;保存状态&#8221;这个概念，实现编程语言的时候，就可以快速排除掉一些不靠谱的方案。<br />比如，不要迭代器而把状态保存在数据结构内部行不行？当然不行，考虑以下代码：<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">#include&nbsp;</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">iostream</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; "><br />#include&nbsp;</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">vector</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; "><br /><br /></span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;main(</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;argc,&nbsp;</span><span style="color: #0000FF; ">char</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">*</span><span style="color: #000000; ">argv[])<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;std::vector</span><span style="color: #000000; ">&lt;</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;a&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;{</span><span style="color: #000000; ">1</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">2</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">3</span><span style="color: #000000; ">};<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">for</span><span style="color: #000000; ">&nbsp;(std::vector</span><span style="color: #000000; ">&lt;</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">::iterator&nbsp;iter&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;a.begin();&nbsp;iter&nbsp;</span><span style="color: #000000; ">!=</span><span style="color: #000000; ">&nbsp;a.end();&nbsp;</span><span style="color: #000000; ">++</span><span style="color: #000000; ">iter)<br />&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;std::cout&nbsp;</span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">*</span><span style="color: #000000; ">iter&nbsp;</span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">:&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">for</span><span style="color: #000000; ">&nbsp;(std::vector</span><span style="color: #000000; ">&lt;</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">::iterator&nbsp;iter2&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;a.begin();&nbsp;iter2&nbsp;</span><span style="color: #000000; ">!=</span><span style="color: #000000; ">&nbsp;a.end();&nbsp;</span><span style="color: #000000; ">++</span><span style="color: #000000; ">iter2)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;std::cout&nbsp;</span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">*</span><span style="color: #000000; ">iter2&nbsp;</span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;std::cout&nbsp;</span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">&nbsp;std::endl;<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">0</span><span style="color: #000000; ">;<br />}</span></div><br />我们就知道，访问状态有时候是需要保存多份的，而对于引用类型的对象来说，状态要想在内部保存多份会更加复杂，还不如用显式的状态保存。<br />直接修改数据结构内部呢？比如用类似car和cdr循环赋值的方式进行遍历。这一样行不通，如果复制保存的引用类型真实数据，则代价太大，看不出引用类型的好处，而直接修改则会破坏数据本身，而数据可能之后还要使用。<br /><br />以下Python作为例子如何创建一个可迭代的，Range对象：<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;Range:<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">def</span><span style="color: #000000; ">&nbsp;</span><span style="color: #800080; ">__init__</span><span style="color: #000000; ">(self,&nbsp;low,&nbsp;high):<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;self.cur&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;low<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;self.high&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;high<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">def</span><span style="color: #000000; ">&nbsp;</span><span style="color: #800080; ">__iter__</span><span style="color: #000000; ">(self):<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;self<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">def</span><span style="color: #000000; ">&nbsp;next(self):<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;self.cur&nbsp;</span><span style="color: #000000; ">&gt;=</span><span style="color: #000000; ">&nbsp;self.high:<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">raise</span><span style="color: #000000; ">&nbsp;StopIteration<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">else</span><span style="color: #000000; ">:<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;v&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;self.cur<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;self.cur&nbsp;</span><span style="color: #000000; ">+=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">1</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;v<br /><br /></span><span style="color: #0000FF; ">for</span><span style="color: #000000; ">&nbsp;item&nbsp;</span><span style="color: #0000FF; ">in</span><span style="color: #000000; ">&nbsp;Range(</span><span style="color: #000000; ">1</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">3</span><span style="color: #000000; ">):<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">print</span><span style="color: #000000; ">&nbsp;item</span></div><br />创建对象的时候，执行了初始化方法和__iter__方法，在迭代的过程中则执行了和next方法，并且以StopIteration异常作为迭代结束的标志。<br /></div><img src ="http://www.cppblog.com/wuwu/aggbug/208539.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/wuwu/" target="_blank">呜呜</a> 2014-10-11 17:10 <a href="http://www.cppblog.com/wuwu/archive/2014/10/11/208539.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>高级语言与低级语言交互的若干问题--引言</title><link>http://www.cppblog.com/wuwu/archive/2014/07/28/207839.html</link><dc:creator>呜呜</dc:creator><author>呜呜</author><pubDate>Sun, 27 Jul 2014 19:58:00 GMT</pubDate><guid>http://www.cppblog.com/wuwu/archive/2014/07/28/207839.html</guid><wfw:comment>http://www.cppblog.com/wuwu/comments/207839.html</wfw:comment><comments>http://www.cppblog.com/wuwu/archive/2014/07/28/207839.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/wuwu/comments/commentRss/207839.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/wuwu/services/trackbacks/207839.html</trackback:ping><description><![CDATA[首先定义一下题目中出现的&#8220;高级语言&#8221;和&#8220;低级语言&#8221;，这里的高级和低级指的不是语言特性的丰富程度区别、也不是开发环境和工具的区别，既然谈编译，我打算说的是编程语言实现中很常见的一种模式，即用相对原生的（比如可以方便灵活地构造复杂的数据结构，和操作系统交互极其容易），来实现一个相对高级的运行时环境（例如有垃圾回收等比较复杂的特性）以及运行在这个运行时环境的编程语言。一个很典型的实例就是，用C/C++来实现一个小型的编程语言虚拟机，有自己的指令集，以及其上的编程语言（例如Python的最原始实现CPython和Lua的官方实现这样的模式）。这样的编程语言实现模式相对简单，可以暂时跳过后端繁琐的事务，但是又不至于过分依赖高级工具（例如某些支持JIT的成品虚拟机或者LLVM）以至于让自己觉得好像略过了很多工作自己做得不够。<br /><br />我们知道，哪怕是C语言这样的比较&#8220;原生&#8221;的语言，自带的标准库功能都是相当原始的。如果一个人从头包办所有的工作用C语言开发，在现在这个社会对应用快速开发的要求下，首先是很困难，因为不少该是库完成的功能要全部自己做工作量将会非常大，然后是有时候这样的愿望根本不可能实现。比如实现一个程序，你需要加密功能，那么得首先花一段时间熟悉数据加密方面的知识、研究到公钥方面甚至可能还得看看数论；而开发一个游戏程序、可能得学习不少图形知识，在没有任何库的辅助下自己实现一个类似&#8220;软渲染&#8221;的东西，中间一堆矩阵变换什么的&#8230;&#8230;那么多知识，一般人根本应付不来。好在可以调用外部库，加密库由密码学专家开发，图形库由图形学专家开发，这样避免了重复工作，我们可以把精力集中在最重要的业务上。原生库，往往和那些原生的编程语言实现（比如C/C++）本质上一样，都是一些二进制机器码，通过操作系统给出的接口调用即可，实在没有操作系统自己也可以做个简单的加载器实现类似的效果。而前文提到的那类高级语言，实现上就不能简单地照搬原生低级语言的模式了，这里面牵涉了很多微妙的问题和技巧。<br /><br />我策划了5篇文章围绕这个主题讲我的一些经验，这些主题涵盖了设计高级语言虚拟机和低级语言交互的若干初级话题：<br />一：操作系统接口<br />二：高级语言调用原生语言<br />三：原生语言调用高级语言<br />四：外部事件<br />五：线程与全局锁<br /><br />以后也许也会补充更多的文章，希望这些文章能给读者解释一些疑惑，也给预备实现编程语言的朋友防止&#8220;踩坑&#8221;做一些提示，也非常欢迎读者和我进行交流。<img src ="http://www.cppblog.com/wuwu/aggbug/207839.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/wuwu/" target="_blank">呜呜</a> 2014-07-28 03:58 <a href="http://www.cppblog.com/wuwu/archive/2014/07/28/207839.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JIT 实践 (Practice Just In Time)</title><link>http://www.cppblog.com/wuwu/archive/2014/07/21/207741.html</link><dc:creator>呜呜</dc:creator><author>呜呜</author><pubDate>Mon, 21 Jul 2014 15:53:00 GMT</pubDate><guid>http://www.cppblog.com/wuwu/archive/2014/07/21/207741.html</guid><wfw:comment>http://www.cppblog.com/wuwu/comments/207741.html</wfw:comment><comments>http://www.cppblog.com/wuwu/archive/2014/07/21/207741.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/wuwu/comments/commentRss/207741.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/wuwu/services/trackbacks/207741.html</trackback:ping><description><![CDATA[<div>JIT全称Just In Time，也就是&#8220;即时&#8221;的意思，即时做什么事情呢？即时编译，它被广泛应用在编译器、编程语言实现、虚拟机、模拟器等等产品上。这篇文章就从头讲一下JIT是怎么回事以及为自己动手实现JIT做一个简单引导。</div><div></div><div>大家都知道，生产处理器芯片的公司有很多家，比较流行的处理器体系按粗略的分法都有x86、ARM、MIPS、PowerPC等等很多种，而且每一种下又可以进一步细分，比如x86除了基本的8086指令集之外还添加了32位扩展、64位扩展、还有一系列针对多媒体和多数据流等等应用的专门的指令集，如MMX、SSE等等，这些指令集能执行的程序，往往只能包含它支持指令的子集，比如你购买了一个64位的程序就不能用一个ARM的处理器来运行。</div><div></div><div>在有源代码并且源代码是语言实现是编译型的情况下，可以使用编译器针对多个平台分别编译，但是，两三个平台还可以，10个平台呢？每个平台之后又出现了新的扩展指令集呢？未来出现的新平台呢？没完没了。</div><div></div><div>于是有人采取这样一种做法，就是假想一个不存在的体系结构，包含一些栈或寄存器，以及对应的指令集，然后编译生成针对这个假想机器生成代码，又被称作&#8220;字节码&#8221;。以实现编程语言为例，这样的做法在直接解释编程语言源代码和完全的编译到机器码再执行之间，取了一个平衡点。这个平衡点相对于完全编译，获得了&#8220;一次编译到处运行&#8221;的好处；相对于直接解释执行，又获得了节省每次运行时都要做词法语法分析和简单优化的时间。这样的做法最著名的例子就是Java，Java开发时包含一个编程语言标准和一个虚拟机标准，虚拟机标准为以上这种执行模式打下了基础。要注意的是，不要以为Java是第一个想到这种做法的人，早在Java诞生之前的20世纪80年代甚至更早之前就有人这么做过，只是那时候的计算机性能非常差，差到不足以支撑这种模式的流行。</div><div></div><div>人们对性能的追求是无限的，但是又不甘心舍弃&#8220;一次编译到处运行&#8221;的优点，有什么办法能在字节码解释运行和编译到机器码之间再取一个平衡点呢？于是JIT也就是即时编译技术产生了。它的思想是，在保证字节码能通过解释运行在很多平台的基础上，针对个别流行的平台（比如x86和ARM），再进行一次编译，编译到机器码后再执行。这个再编译的过程确实要消耗一点点时间，但是想象一下，每一条指令都由虚拟机解释执行变为直接由CPU的电路执行，性能提升是非常显著的。</div><div></div><div>Java实现JIT后，据说.NET设计时还特地以实现JIT作为目标之一，除了编译器和运行时/虚拟机之外，JIT还被广泛应用在游戏机模拟器这样的真实硬件虚拟软件上，以获得更好的性能。</div><div></div><div>接下来是动手时间，需要的知识包括对C语言的基本理解和对操作系统的基本理解、会在终端下输入一点命令就可以了。</div><div></div><div>假设我们有以下C程序：</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">int</span>&nbsp;inc(<span style="color: #0000FF; ">int</span>&nbsp;a)<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;a&nbsp;+&nbsp;1;<br />}</div></div><div>我们的目标就是&#8220;即时编译&#8221;这段程序并且执行。</div><div></div><div>这个程序的功能显而易见就是输入一个整数参数，将其加上1后返回。将这个程序保存为obj1.c，进行编译。</div><div>在x86 32bit的环境下，使用GCC编译：</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->$&nbsp;gcc&nbsp;-c&nbsp;obj1.c&nbsp;-o&nbsp;obj1.o&nbsp;-g</div></div><div>我们就会得到一个带有调试符号的目标文件，如果是x86_64的机器，我们为了下文统一，指定输出32位的代码：</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->$&nbsp;gcc&nbsp;-c&nbsp;obj1.c&nbsp;-o&nbsp;obj1.o&nbsp;-g&nbsp;-m32</div></div><div>这样我们就会得到一个32bit的带有上面C程序功能的目标文件，看看这个文件的属性：</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->$&nbsp;file&nbsp;obj1.o<br />obj1.o:&nbsp;ELF&nbsp;32-bit&nbsp;LSB&nbsp;relocatable,&nbsp;Intel&nbsp;80386,&nbsp;version&nbsp;1&nbsp;(SYSV),&nbsp;not&nbsp;stripped</div></div><div>ELF是Linux下流行的可执行文件格式，类似Windows下的PE。32-bit说明我们正确得到了32bit的目标文件。</div><div></div><div>接下来使用objdump指令解析刚才得到的目标文件：</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->$&nbsp;objdump&nbsp;-S&nbsp;obj1.o<br /><br />obj1.o:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;file&nbsp;format&nbsp;elf32-i386<br /><br /><br />Disassembly&nbsp;of&nbsp;section&nbsp;.text:<br /><br />00000000&nbsp;&lt;inc&gt;:<br /><span style="color: #0000FF; ">int</span>&nbsp;inc(<span style="color: #0000FF; ">int</span>&nbsp;a)<br />{<br />&nbsp;&nbsp;&nbsp;0:&nbsp;&nbsp;&nbsp;&nbsp;55&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;push&nbsp;&nbsp;&nbsp;%ebp<br />&nbsp;&nbsp;&nbsp;1:&nbsp;&nbsp;&nbsp;&nbsp;89&nbsp;e5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;%esp,%ebp<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;a&nbsp;+&nbsp;1;<br />&nbsp;&nbsp;&nbsp;3:&nbsp;&nbsp;&nbsp;&nbsp;8b&nbsp;45&nbsp;08&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;0x8(%ebp),%eax<br />&nbsp;&nbsp;&nbsp;6:&nbsp;&nbsp;&nbsp;&nbsp;83&nbsp;c0&nbsp;01&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;add&nbsp;&nbsp;&nbsp;&nbsp;$0x1,%eax<br />}<br />&nbsp;&nbsp;&nbsp;9:&nbsp;&nbsp;&nbsp;&nbsp;5d&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pop&nbsp;&nbsp;&nbsp;&nbsp;%ebp<br />&nbsp;&nbsp;&nbsp;a:&nbsp;&nbsp;&nbsp;&nbsp;c3&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ret &nbsp; &nbsp;</div></div><div></div><div>看到了吗，我们得到了源代码和汇编指令的参照，更加方便的是，我们直接得到了每一条汇编指令对应的机器码，这些机器码在CPU中直接得到执行，执行性能是最好的。</div><div></div><div>如果你要问，不是即时&#8220;编译&#8221;吗？怎么不讲机器码是如何产生的？嗯&#8230;&#8230;这篇文章的内容还是集中在描述JIT的原理，至于输出针对寄存器机器的机器码，那学问就大了，不过在这里可以稍微提一下，这些代码浅显的规律。如果你经常看这种编译器生成的x86汇编，就可以发现，通常进入一个函数，都会有以下两条指令：</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->&nbsp; &nbsp;0:&nbsp;&nbsp;&nbsp;&nbsp;55&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;push&nbsp;&nbsp;&nbsp;%ebp<br />&nbsp;&nbsp;&nbsp;1:&nbsp;&nbsp;&nbsp;&nbsp;89&nbsp;e5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;%esp,%ebp</div></div><div>这两条指令的意思是，先把%ebp寄存器压进栈，然后把%esp的值覆盖进%ebp，这么做的结果是，EBP里直接存放了当前栈帧的&#8220;底部&#8221;，这样我们可以轻易根据%ebp（实际上就是%esp）引用传入的参数。</div><div>之后的：</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->&nbsp; &nbsp;3:&nbsp;&nbsp;&nbsp;&nbsp;8b&nbsp;45&nbsp;08&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;0x8(%ebp),%eax</div></div><div>你要问为什么传入的变量a的位置在0x8(%ebp)的位置，其实不为什么，这就是编译器默认的参数传递约定，对于这个版本的GCC来说，第一个参数就在这个位置上。</div><div>接下来是给%eax增加1，这个没什么好解释的：</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->&nbsp; &nbsp;6:&nbsp;&nbsp;&nbsp;&nbsp;83&nbsp;c0&nbsp;01&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;add&nbsp;&nbsp;&nbsp;&nbsp;$0x1,%eax</div></div><div>最后是：</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->&nbsp; &nbsp;9:&nbsp;&nbsp;&nbsp;&nbsp;5d&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pop&nbsp;&nbsp;&nbsp;&nbsp;%ebp<br />&nbsp;&nbsp;&nbsp;a:&nbsp;&nbsp;&nbsp;&nbsp;c3&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ret &nbsp; &nbsp;</div></div><div>也就是把之前压入栈以保护的%ebp的值弹出放回到%ebp上，最后ret返回，需要说的是，把eax寄存器里的值作为每个函数的返回值，这也是默认的调用约定。</div><div></div><div>为了实现这么个函数，我们用到的机器码装进数组里，就是：</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->uint8_t&nbsp;machine_code[]&nbsp;=&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;0x55,&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;0x89,&nbsp;0xe5,&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;0x8b,&nbsp;0x45,&nbsp;0x08,&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;0x83,&nbsp;0xc0,&nbsp;0x01,<br />&nbsp;&nbsp;&nbsp;&nbsp;0x5d,&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;0xc3&nbsp;};</div></div><div></div><div>接下来是一些人直觉上想不明白的问题，这些代码放进数组里，不是数据吗？数据不是代码如何能执行？再原始一点的模型来说，只要CPU的IP（指令指针）或者PC（程序计数器）指向的地方的数据，都会被当作指令执行，但是我们目前的计算机有所不同，真实的内存被操作系统用虚拟内存包装起来，哪些地址的内存放的数据能被当作指令执行是操作系统说了算，所以你需要申请一段内存，明确告诉操作系统&#8220;我需要一段内存，请将这段内存给我并且标记为可执行&#8221;。</div><div></div><div>在Linux上，可以用mmap函数申请这样的可执行的内存，该函数的原型是：</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->#include&nbsp;&lt;sys/mman.h&gt;<br /><br /><span style="color: #0000FF; ">void</span>&nbsp;*mmap(<span style="color: #0000FF; ">void</span>&nbsp;*addr,&nbsp;size_t&nbsp;length,&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;prot,&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;flags,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;fd,&nbsp;off_t&nbsp;offset);</div></div><div>其中flags参数要带有PROT_EXEC标记即可执行了，比如：</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->mmap(0,&nbsp;size,&nbsp;PROT_READ|PROT_WRITE|PROT_EXEC,&nbsp;MAP_PRIVATE|MAP_ANON,&nbsp;-1,&nbsp;0);</div></div><div>释放mmap得到的内存使用munmap函数。</div><div></div><div>Windows也有类似的函数VirtualAlloc，原型是：</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->LPVOID&nbsp;WINAPI&nbsp;VirtualAlloc(<br />&nbsp;&nbsp;_In_opt_&nbsp;&nbsp;LPVOID&nbsp;lpAddress,<br />&nbsp;&nbsp;_In_&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SIZE_T&nbsp;dwSize,<br />&nbsp;&nbsp;_In_&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DWORD&nbsp;flAllocationType,<br />&nbsp;&nbsp;_In_&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DWORD&nbsp;flProtect<br />);<br />比如：<br />VirtualAlloc(0,&nbsp;size,&nbsp;MEM_RESERVE|MEM_COMMIT,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;PAGE_EXECUTE_READWRITE);</div></div><div></div><div>释放VirtualAlloc得到的内存使用VirtualFree函数。</div><div></div><div>得到了可执行的内存后，我们将之前准备好的机器码复制进去就行了：</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->memcpy(executable_code,&nbsp;machine_code,&nbsp;<span style="color: #0000FF; ">sizeof</span>(machine_code));</div></div><div></div><div>接下来，要使用C语言函数指针，将指向一段代码的内存空间指针的类型，强行转换为一个函数并且执行：</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">int</span>&nbsp;(*func)(<span style="color: #0000FF; ">int</span>)&nbsp;=&nbsp;(<span style="color: #0000FF; ">int</span>&nbsp;(*)(<span style="color: #0000FF; ">int</span>))executable_code;</div></div><div></div><div>复杂的C类型表示方法有时候是有点奇怪，但它就是这么写的。</div><div></div><div>以整数2为参数运行这段代码：</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">int</span>&nbsp;result&nbsp;=&nbsp;(*func)(2);</div></div><div></div><div>需要再说明一点的是，因为以上我们生成的是32bit的机器码，如果你的操作系统是64bit的，编译后运行可能会缺乏一些32bit的库，以Debian GNU/Linux为例，用以下命令安装32bit的库：</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->#&nbsp;dpkg&nbsp;--add-architecture&nbsp;i386<br />#&nbsp;apt-<span style="color: #0000FF; ">get</span>&nbsp;update<br />#&nbsp;apt-<span style="color: #0000FF; ">get</span>&nbsp;install&nbsp;ia32-libs<br />#&nbsp;apt-<span style="color: #0000FF; ">get</span>&nbsp;install&nbsp;libc6-dev-i386</div></div><div></div><div>最后，别忘了释放申请的内存：</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->munmap(executable_code,&nbsp;0);</div></div><div></div><div>下面列出最终的完整程序：</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->#include&nbsp;&lt;stdlib.h&gt;<br />#include&nbsp;&lt;stdio.h&gt;<br />#include&nbsp;&lt;<span style="color: #0000FF; ">string</span>.h&gt;<br />#include&nbsp;&lt;stdint.h&gt;<br />#include&nbsp;&lt;sys/mman.h&gt;<br /><br /><span style="color: #0000FF; ">int</span>&nbsp;main(<span style="color: #0000FF; ">int</span>&nbsp;argc,&nbsp;<span style="color: #0000FF; ">char</span>&nbsp;*argv[])<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;uint8_t&nbsp;machine_code[]&nbsp;=&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0x55,&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0x89,&nbsp;0xe5,&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0x8b,&nbsp;0x45,&nbsp;0x08,&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0x83,&nbsp;0xc0,&nbsp;0x01,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0x5d,&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0xc3&nbsp;};<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;uint8_t&nbsp;*executable_code&nbsp;=&nbsp;NULL;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;executable_code&nbsp;=&nbsp;(uint8_t&nbsp;*)mmap(0,&nbsp;<span style="color: #0000FF; ">sizeof</span>(machine_code),&nbsp;PROT_READ|PROT_WRITE|PROT_EXEC,&nbsp;MAP_PRIVATE|MAP_ANON,&nbsp;-1,&nbsp;0);<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">if</span>&nbsp;(executable_code&nbsp;==&nbsp;NULL)<br />&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fprintf(stderr,&nbsp;"mmap&nbsp;failed\n");<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;exit(1);<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;memcpy(executable_code,&nbsp;machine_code,&nbsp;<span style="color: #0000FF; ">sizeof</span>(machine_code));<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;(*func)(<span style="color: #0000FF; ">int</span>)&nbsp;=&nbsp;(<span style="color: #0000FF; ">int</span>&nbsp;(*)(<span style="color: #0000FF; ">int</span>))executable_code;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;result&nbsp;=&nbsp;(*func)(2);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;printf("%d\n",&nbsp;result);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;munmap(executable_code,&nbsp;0);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;0;<br />}</div></div><div></div><div>接下来编译并运行该程序，保存为jit.c：</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->$&nbsp;gcc&nbsp;jit.c&nbsp;-o&nbsp;jit<br />$&nbsp;./jit<br />3</div></div><div>看来已经执行成功了。</div><div></div><div>这就是JIT的雏形了，实际应用中的JIT还需要做什么工作呢？以一个编程语言实现为例，已经开发出了能生成字节码的编译器，还要做的事情有：把字节码再一次编译为x86或者其它指令集的机器码，而这其中又包括寄存器分配，指令的选择以及其它一系列常见的编译器后端优化，而进一步深入挖掘还有程序热点统计这样就可以实现根据运行时信息优化，这么多话题可能可以写好几本书。</div><div></div><img src ="http://www.cppblog.com/wuwu/aggbug/207741.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/wuwu/" target="_blank">呜呜</a> 2014-07-21 23:53 <a href="http://www.cppblog.com/wuwu/archive/2014/07/21/207741.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>简评中文编程语言</title><link>http://www.cppblog.com/wuwu/archive/2014/07/16/207661.html</link><dc:creator>呜呜</dc:creator><author>呜呜</author><pubDate>Tue, 15 Jul 2014 18:03:00 GMT</pubDate><guid>http://www.cppblog.com/wuwu/archive/2014/07/16/207661.html</guid><wfw:comment>http://www.cppblog.com/wuwu/comments/207661.html</wfw:comment><comments>http://www.cppblog.com/wuwu/archive/2014/07/16/207661.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/wuwu/comments/commentRss/207661.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/wuwu/services/trackbacks/207661.html</trackback:ping><description><![CDATA[中文编程，或者汉语编程，不是什么新鲜事物，用&#8220;非英语编程语言&#8221;来进行编程也并非只有中国才有，这里有个叫&#8220;nadeshiko&#8221;的日语编程开发工具：https://code.google.com/p/nadesiko/，我相信还有很多其它&#8220;非英语&#8221;编程语言，有兴趣的可以看看。<br /><div>没用过中文编程语言可以试试，国内有很多类似的东西，要指出的一点是，中文编程语言的所谓&#8220;输入的问题&#8221;没有想象中的困难，它们往往自带一个开发环境，只需要输入一个词语的拼音首字母即可完成输入（比如输入b就会弹出一个补全菜单，里面有&#8220;播放音乐&#8220;、&#8221;保存页面&#8221;等等选项，和你在常见IDE里按下.看到的一样）。<br /><br /><strong>编程语言</strong><br /><br />计算机本不认识语言，而仅仅认识数字，然后根据一定的规则在存储器之间传输处理好的数字，人类按照机器底层的特性进行编程难度是非常大的，但是按照自然语言指示机器该做什么可以吗？首先是机器无法识别人类的自然语言，其次大部分人类自然语言无法表达清晰的逻辑。所以一些人进行了折中，设计了所谓编程语言的东西。编程语言是一种形式语言，用一系列的符号在计算机识别能力范围内和人类表达逻辑范围内寻找不同的平衡点。根据编程语言所处环境不同、设计目标的不同、编译器实现者能力不同等等因素，不同的变成语言所取的这个平衡点也不同。<br />以C语言为例，C语言所处的环境是，软件用汇编语言开发无法在各个不同硬件上移植，但是那个时期的硬件往往性能都比较低下，所以出现了刚好计算机编译器（早期是解释器）能识别（编译或解释），同时满足了当时开发操作系统直接操作内存的需求（具备有算术运算能力的指针）。如果你细心点可以发现C语言的很多特征迎合了那个时代的需求，C语言里有register、auto、inline关键字，说明当时的编译器水平很差，还不能做到高效处理寄存器分配和内联。int、short、long、char、unsigned、signed等等也恰恰描述了那个时代寄存器处理的数字常见类型有哪些。<br /><br /><strong>中文编程语言</strong><br /><br />再以某个中文编程语言为例，写一个Hello World程序：<br /><div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%; word-break: break-all;"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">#包含&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">某语言系统.接口</span><span style="color: #000000; ">"</span><span style="color: #000000; "><br /><br />整数类型&nbsp;主函数()<br />{<br />&nbsp;&nbsp;输出(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">你好世界</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;返回&nbsp;</span><span style="color: #000000; ">0</span><span style="color: #000000; ">;<br />}</span></div><br /><br />其实本质和C语言：<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">#include&nbsp;</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">stdio.h</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; "><br /><br /></span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;main()<br />{<br />&nbsp;&nbsp;printf(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">Hello&nbsp;World</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">0</span><span style="color: #000000; ">;<br />}</span></div><br />外形几乎没有区别，能看得到的区别也就在关键字和标志符被&#8220;汉化&#8221;了。那么这些汉化到底能对&#8220;不懂英语&#8221;的人起到多少帮助呢？可以尝试拿上面的&#8220;中文版C语言程序&#8221;给一个没学过编程的人看，他几乎是不可能看懂的，也不可能立即用这种语言写个其他类似的程序，因为汉化了的那几个关键字和标志符尽管写成了汉字，但还是没有描述他们在实际的计算机程序中表示的是什么。比如#include ，#开头的往往是预处理宏，而预处理宏程序的功能是在编译前对程序进行的所谓预处理，比如include功能就类似与把stdio.h里声明的东西都&#8220;复制&#8221;到当前文件，使得当前文件可以看到stdio.h里的函数原型等等内容。而int表示的是整数类型，或者说当前计算机系统C语言编译器认为的默认宽度的整数类型，而不是无限精度的任意整数类型。那么把这两个换成&#8220;包含&#8221;和&#8220;整数&#8221;类型之后呢？包含的含义和include的含义还是相同，理解了include处理过程的人（或者仅仅理解它有什么作用的人）固然是会毫无顾忌地写下这行代码，而不懂的人还是不会写，其他的标识符和关键字的汉化也是一样，说到底，关于写程序的人，不是因为理解了这些符号在中文或者英文中的含义所以才会用中文或者英文编程语言写程序，而是因为他理解了这些符号在这个计算机系统和编程语言环境里的含义。<br />不要觉得这两种语句几乎一模一样语言对应起来很搞笑，其实很多所谓&#8220;中文编程语言&#8221;真的就是在预处理器上改改，把关键字和标准库的一些函数弄成中文，然后做个图形界面的开发环境就发布了，没有什么非常重大的科技含量。它们的底层（尤其是后端）本质还是现有常见编程语言的常见实现（比如GCC或者Mono之类的），有的甚至在不遵循自己引用的开源软件许可证的情况下，闭源还卖钱。<br /><br /><strong>编程语言的目的</strong><br /><br />我们为什么要使用编程语言？因为用机器能识别的机器语言写代码太痛苦、而且没有移植性。我们想用编程语言做到的是什么？是在一个更高层次清晰地描述希望计算机执行的逻辑。而描述逻辑的过程无论是使用&#8220;整数&#8221;还是&#8220;Int&#8221;、或者&#8220;int&#8221;、&#8220;Int32&#8221;、&#8220;Integer&#8221;，难度并不会降低，中文编程仅仅是让一些脑子中有定势&#8220;我不会英文、所以中文能帮我学会编程&#8221;的人第一眼看上去害怕的程度稍微降低一点点，一旦学会了那几个关键字或者业务相关标识符相关的中文，之后的整理和表达逻辑的过程难度丝毫不会减轻，而这个&#8220;之后&#8221;，也就是学习这几个关键字和标识符的时间可能占整个编程的时间的99.99%，我们可以说中文编程仅仅减轻了这部分人0.01%的负担。<br /><br /><strong>中文编程的害处</strong><br /><br />有人说减轻了一部分人0.01%的负担还不错，还算是改进，但是为了这0.01%的&#8220;改进&#8221;，又产生了其它更加严重的问题。<br />（1）：编程语言实现的匮乏<br />这些所谓中文编程语言的实现和维护者往往是个人和非常小的公司，而且以自己的实现来定义语言，他们往往不会开源，一旦这些个人不打算继续维护、或者该公司倒闭，则该语言写出的代码能运行的平台就仅仅被锁定在最后一个实现的发布，而且以后也不会再添加新特性和新功能了，用该语言写的代码几乎没有未来的发展余地。<br />（2）：库和其他支持的匮乏<br />中文编程语言用户少，而且仅有的用户还往往连那普通编程语言的几个英文关键词都害怕学习，更不可能开发高质量的、尤其是底层的库，于是编程语言的维护者和少量的高级用户只能担起开发库的重担，大部分库来自封装操作系统的API、常见功能的库（比如MP3播放、XML解析）的封装，但是这些库是非常不够的。<br />（3）：交流的困难<br />就如我们在国际性的论坛和irc交流使用英语一样，这些论坛和irc的用户除了中国人之外还有大量非英语国家的人，我们使用英语不是因为英语这语言非常精确、非常优美，而仅仅是因为英语用户多，已经几乎是国际语言了，大家都多少会点，交流起来非常方便。而使用这些非主流的中文编程语言则会使得自己和大家交流&#8220;没有共同语言&#8221;。<br /><br /><strong>结语</strong><br /><br />我到这里结论已经很明显了，总结一下就是：所谓&#8220;中文编程语言&#8221;解决的问题不多，但是带了很多麻烦。如果有读者属于仅仅是因为认为自己不会英文而选择这些&#8220;中文编程语言&#8221;，请理解&#8220;编程语言的目的和编程的真正的难度在于描述逻辑，而不是关键字和标识符字面上所对应的自然语言&#8221;，然后尝试一下自己害怕的&#8220;英文编程语言&#8221;，买一本优质的学习该编程语言的图书，相信会很快发现，英语真的不是问题。</div><img src ="http://www.cppblog.com/wuwu/aggbug/207661.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/wuwu/" target="_blank">呜呜</a> 2014-07-16 02:03 <a href="http://www.cppblog.com/wuwu/archive/2014/07/16/207661.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Lisp概况与学习方法</title><link>http://www.cppblog.com/wuwu/archive/2014/07/07/207560.html</link><dc:creator>呜呜</dc:creator><author>呜呜</author><pubDate>Mon, 07 Jul 2014 14:51:00 GMT</pubDate><guid>http://www.cppblog.com/wuwu/archive/2014/07/07/207560.html</guid><wfw:comment>http://www.cppblog.com/wuwu/comments/207560.html</wfw:comment><comments>http://www.cppblog.com/wuwu/archive/2014/07/07/207560.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/wuwu/comments/commentRss/207560.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/wuwu/services/trackbacks/207560.html</trackback:ping><description><![CDATA[<div>Lisp名字来源于LISt Processor，也就是列表处理的意思。初学者第一眼见到Lisp往往印象最深的也就是里面成堆的括号。的确没错，括号就是该语言最显著的特点。整个语言的源代码也是以括号作为构造其语法树的依据。<br />很多初学者有考古的爱好，听闻了传说中的7公理，所谓7个操作符可以构造一切，并且为止着迷。且不说这7公理是不是真的能构造出目前很多应用上所需要的一切，就算真的能，性能也必定很低，因为具备的材料太少，很多基础的东西也要从头开始构造。而精简的构造似乎并没有为实际应用带来多少好处，图灵机的构造比这几个公理更简单，但是你不会看到有多少人用图灵机编程。制造计算机系统就是一个寻找扩展性、成本、体积、功耗等等参考的平衡点的过程，不在那个平衡点则很容易被淘汰。<br /><br />最初版的Lisp早已没人使用，取而代之的是无数人基于Lisp的特点构造出的一系列类似Lisp的语言，无论他们名字里有没有Lisp，他们已经不是Lisp了，但是他们又是带有Lisp特点的语言，所以又被叫做Lisp的方言。所以看到某本书里介绍的某个Lisp的源代码，里面无数的括号的组织和括号之间关键字，千万不要觉得很神秘，因为那已经完全是个人或者某个组织定义的，不是非得那么设计的，如果你愿意，你也可以实现一个自己的Lisp方言。<br /><br />Lisp以括号的简洁形态激励了无数人和无数组织制定和实现自己的Lisp方言、一旦有后来者不满意又会对之前存在的Lisp标准和实现进行&#8220;总结&#8221;而开发新的Lisp，加上开发新的Lisp的确简单（因为其语法简单，非常繁琐的语法分析部分很容易写），则更是激励无数初学者实现自己的Lisp，甚至有教科书的作业就是实现Lisp，所以到目前，已经根本无法统计到底存在多少种Lisp或者说Lisp的方言了。但是总的来说，目前还是有一些名气比较大的Lisp分类，用户比较多、实现比较多、资料也比较多，学习的时候可以优先选择下面三种：<br /><br /><strong>Scheme</strong>：非常小型的Lisp方言，内容少至早期标准也仅仅有几十页，非常适合初学者学习。由于早期似乎被用作教学语言，所以并没有针对项目开发设计一些必要的措施，包括模块、名字空间等等。最新的标准正在尝试弥补这个缺陷但是进展似乎比较缓慢。目前来说比较好的实现有：<br />1. DrRacket，开源实现，自带IDE，有高亮、调试功能，支持Scheme相关的几种方言，自带手册比较完备，适合初学者。<br />2. Chez Scheme，传说中的异常高效的Scheme实现，支持最新标准，且作者是Scheme实现的权威，品质有保证。但是该软件为专有软件，需要购买使用。作者在其首页提供免费精简版Petite Chez Scheme。<br />3. Guile，GNU的扩展语言，一些GNU的软件就是用这个实现进行扩展的。<br />等等&#8230;&#8230;<br />当然，因为Scheme语言的确比较精简，自己实现一个Scheme也是不错的选择。难度并没有想象中的大，实现后还可以嵌入在自己的项目中于扩展用。<br />Scheme学习资料非常多，包括<br />《The Scheme Programming Language》这本书是之前介绍的Chez Scheme的作者R. Kent Dybvig写的，内容详尽、语言的每个特性都给出示例。<br />《Structure and Interpretation of Computer Programs》这本书又叫做&#8220;计算机程序的构造和解释&#8221;，虽然通常被认为是一本入门书，但是实际上内容涵盖很广，包括计算机原理、编译器构造、符号计算、电子电路等等，编程语言成了描述这些内容的无关紧要的工具，做完大部分习题很有挑战性。<br />《Revised5 Report on the Algorithmic Language Scheme》又被叫做&#8220;R5RS&#8221;，初看这个题目有点莫名其妙，实际上这是一份类似Scheme标准的东西，里面最直接的描述了Scheme的关键特性，甚至有点面向语言实现者的意味。该标准之后还在不断地出第六份第七份等等，增加了一些标准库的内容。读通这本可以几乎完全掌握Scheme了。<br />因为Scheme的资料太多，暂时就列出上面三份，能看完这些掌握得就差不多了。<br /><br /><strong>Common Lisp</strong>：又被叫做CL，是一个典型的&#8220;总结性&#8221;Lisp方言，也就是一次把各个Lisp方言的特性进行总结的尝试，并且获得了一定的成功。该语言极其复杂以至于很少有实现能比较完整实现其标准（虽然不少CL的实现都自称自己完整实现了标准）。比较常见的实现有：<br />1. SBCL，开源实现，来自与CMUCL，编译到原生码执行性能有保障。<br />2. CLISP，一些教科书推荐的Common Lisp实现，性能比较差。<br />等等&#8230;&#8230;<br />PS：据Common Lisp界著名人物小妮补充的部分CL实现现状：<br /><div>Allegro Cl, Lispworks, CCL, SBCL, ECL, CMUCL(已死)，CLISP（垂死），ABCL，MKCL（半主流，这个是从ECL改过来的），mocl（小众），Corman Lisp（已死）</div>常见的教材有：<br />《ANSI Common Lisp》传统风格的编程语言教科书，内容详尽。<br />《Practical Common Lisp》据说得了Jolt大奖，里面以一系列所谓现实生活中的例子来推进教学，喜欢这种类型教材的可以看看。<br /><br /><strong>Emacs Lisp</strong>：<br />这算是一种专用Lisp，也就是说它并不是通用编程语言，而仅仅是用于扩展一个叫Emacs的文本编辑器。这个文本编辑器历史悠久，按键绑定灵活，用这种编程语言进行扩展后实现一些比较简单的功能可以简化文本编辑工作（当然本身还有其它问题，是否值得专门学习有很大争议）。学习Emacs Lisp是在对Emacs这个文本编辑器产生兴趣并且初步掌握后，想进一步探索和扩展的很自然的选择。<br />比较常见的实现有&#8230;&#8230;当然是GNU Emacs，本身就是内嵌在Emacs编辑器中的。而最好的教材，毫无疑问就是自带的手册。推荐学习过程为<br />第一步：初步掌握Emacs编辑器的操作<br />第二步：学会一些基本的语法<br />第三部：尝试写一些扩展，需要的功能就去查找手册，找到该功能的接口后用之前学到的基本语法组合起来。<br /><br />正如之前介绍的Lisp的特性，仅仅以括号作为明显的特征，激励无数人不断总结和发明自己的方言，这些方言没法介绍完，具体哪些值得学习观察。有一些现代的Lisp方言和实现挺有价值，比如Clojure，可以运行在JVM上，丰富的语法，还能运行在JVM上，重复利用之前用别的运行在JVM上的语言写的程序，这就是一个不错的选择。<br /><br />参考资料：<br />LISP：http://zh.wikipedia.org/zh-cn/LISP<br />图灵机：http://zh.wikipedia.org/zh-cn/%E5%9B%BE%E7%81%B5%E6%9C%BA</div><img src ="http://www.cppblog.com/wuwu/aggbug/207560.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/wuwu/" target="_blank">呜呜</a> 2014-07-07 22:51 <a href="http://www.cppblog.com/wuwu/archive/2014/07/07/207560.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>一步一步讲解Y组合子 (Y-Combinator Explained Step by Step)</title><link>http://www.cppblog.com/wuwu/archive/2014/07/07/207556.html</link><dc:creator>呜呜</dc:creator><author>呜呜</author><pubDate>Mon, 07 Jul 2014 08:54:00 GMT</pubDate><guid>http://www.cppblog.com/wuwu/archive/2014/07/07/207556.html</guid><wfw:comment>http://www.cppblog.com/wuwu/comments/207556.html</wfw:comment><comments>http://www.cppblog.com/wuwu/archive/2014/07/07/207556.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/wuwu/comments/commentRss/207556.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/wuwu/services/trackbacks/207556.html</trackback:ping><description><![CDATA[<div><div><div>你也许听说过Y组合子（又叫Y Combinator），也查过一些资料看过一些示例代码，但就是不明白什么意思，可能是因为自己平常使用的开发语言先入为主阻碍了对函数式独特的运算规则和一些细节没想清楚。</div><div><strong>一：Lambda演算（Lambda Calculus）</strong></div><div>Lambda又写作希腊字母&#955;，Lambda演算由Alonzo Church引入以定义&#8220;可计算函数&#8221;。该演算影响了一系列所谓函数式编程语言，如Lisp、ML系列。</div><div>一个Lambda表达式用以下格式定义：</div><div>&#955;变量.表达式体</div><div>Scheme里要这么写：</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->(lambda&nbsp;(变量)<br />&nbsp;&nbsp;表达式体)</div></div><div>比如，传入一个数后返回加1的结果，Lambda表达式写作：</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->&#955;a.a+1</div></div><div>Scheme可以写作：</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->(lambda&nbsp;(a)<br />&nbsp;&nbsp;(+&nbsp;a&nbsp;1))</div></div><div>当然，变量可以是多个，比如求两个数之和的Lambda表达式可以写作：</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->&#955;a&nbsp;b.a+b</div></div><div>用Scheme语言可以写作：</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->(lambda&nbsp;(a&nbsp;b)<br />&nbsp;&nbsp;(+&nbsp;a&nbsp;b))</div></div><div>原始的Lambda演算甚至连逻辑和算术运算和数字都没有，所有的一切都是可以用Lambda演算定义出来的，当然在现代的编程语言中没必要做到那么&#8220;纯粹&#8221;，包括各种数据类型和运算该用都能用。</div><div>比如判断一个数字是否大于3，Scheme里写作：</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->(lambda&nbsp;(a)<br />&nbsp;&nbsp;(<span style="color: #0000FF; ">if</span>&nbsp;(&gt;&nbsp;a&nbsp;3)<br />&nbsp;&nbsp;&nbsp;&nbsp;"Yes"<br />&nbsp;&nbsp;&nbsp;&nbsp;"No"))</div></div><div>具体Lambda函数是怎么工作的呢，答案是归约。</div><div>归约有三种规则：</div><div><strong>&#945;-转换</strong>(&#945;-conversion)</div><div>&#945;读作alpha。alpha转换的意思是变量名不影响函数含义的意思。比如：</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->&#955;a&nbsp;b.a+b</div></div><div>把变量名的a和b换为x和y：</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->&#955;x&nbsp;y.x+y</div></div><div>该函数的功能并没有发生改变，不管传哪两个数字进去，都会得到一样的结果即两者的和。</div><div><strong>&#946;归约</strong>(&#946;-reduction)</div><div>&#946;读作beta。beta归约的规则是把函数&#8220;应用&#8221;到传入的参数上。</div><div>比如这么个Lambda函数：</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->&#955;a&nbsp;b.a+b</div></div><div>应用在传入的两个参数1和2，那么a和b分别就替换成1和2，表达式体中的a和b的位置也分别被替换成1和2：</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->1+2</div>结果为3.</div><div>Scheme该这么写：</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->((lambda&nbsp;(a)&nbsp;(+&nbsp;a&nbsp;b))&nbsp;1&nbsp;2)</div></div><div><strong>&#951;变换</strong>(&#951;-conversion)</div><div>&#946;读作eta，eta转换表达的意思是，如果两个函数对于所有相同的传入的参数都能得到一样的结果，则两个函数相等。</div><div><strong>二：循环与递归</strong></div><div>现在看一个稍微复杂一点的问题，假设要求n!，也就是1*2*3*4*5*...*n该如何编写程序？</div><div>一种想法用所谓循环，比如用最常见的C语言，可以这么写：</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">int</span>&nbsp;i;<br /><span style="color: #0000FF; ">int</span>&nbsp;s&nbsp;=&nbsp;1;<br /><span style="color: #0000FF; ">for</span>&nbsp;(i&nbsp;=&nbsp;1;&nbsp;i&nbsp;!=&nbsp;n;&nbsp;i++)<br />{<br />&nbsp;&nbsp;s&nbsp;*=&nbsp;i;<br />}</div></div><div>但是普通的函数式语言是不提倡甚至不允许这么写的，原因就在于上面的写法有一个保存状态的行为，也就是给一些变量赋值，保存了中间结果，而函数式语言则基于Lambda演算，Lambda演算可没有&#8220;保存状态&#8221;这种行为。</div><div></div><div>你可能会想到使用递归来实现类似循环的效果，比如Scheme里:</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->(define&nbsp;factorial<br />&nbsp;&nbsp;(lambda&nbsp;(n)<br />&nbsp;&nbsp;&nbsp;&nbsp;(<span style="color: #0000FF; ">if</span>&nbsp;(&gt;&nbsp;n&nbsp;0)&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(*&nbsp;n&nbsp;(factorial&nbsp;(-&nbsp;n&nbsp;1)))<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1)))</div></div><div>这样做运行起来是没有问题的，可是我们给这个函数绑定了一个名字Lambda运算本身不支持这种做法，也就是说第一行得擦掉</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->(lambda&nbsp;(n)<br />&nbsp;&nbsp;(<span style="color: #0000FF; ">if</span>&nbsp;(&gt;&nbsp;n&nbsp;0)&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(*&nbsp;n&nbsp;(factorial&nbsp;(-&nbsp;n&nbsp;1)))<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1))</div></div><div>但是这样factorial则不再存在了，调用它是不会有结果的，我们只能调用点别的东西：</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->(lambda&nbsp;(n)<br />&nbsp;&nbsp;(<span style="color: #0000FF; ">if</span>&nbsp;(&gt;&nbsp;n&nbsp;0)&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(*&nbsp;n&nbsp;(???&nbsp;(-&nbsp;n&nbsp;1)))<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1))</div></div><div>此时对于常用普通的非函数式语言的程序员比较费解的一点来了，就是函数也是一种&#8220;值&#8221;或者&#8220;对象&#8221;，它不但可以绑定到一个变量上，而且还能调用某函数时作为实际参数传入、并且在被调用的函数内部通过参数列表绑定的名字把传入的函数取出来。这么听上去似乎和函数指针之类的东西比较也没什么了不起，但是流行的函数式语言或者常被用于举例解释Y-Combinator的函数式语言往往是动态类型的或者有一定类型推导能力的，写起来十分简洁，看上去就似乎特别神奇。</div><div>以Scheme为例，定义一个返回两数和函数是：</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->(define&nbsp;(add&nbsp;a&nbsp;b)<br />&nbsp;&nbsp;(+&nbsp;a&nbsp;b))</div></div><div>但是这只是一种缩略的写法，Scheme里所有函数都是Lambda函数，本质上它等同于:</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->(define&nbsp;add<br />&nbsp;&nbsp;(lambda&nbsp;(a&nbsp;b)<br />&nbsp;&nbsp;&nbsp;&nbsp;(+&nbsp;a&nbsp;b)))</div></div><div>含义是，有那么个Lambda函数，功能是返回两数的和，然后把这个函数绑定在add这个变量上，或者说&#8220;赋值&#8221;给add变量。</div><div>我们可以直接调用这个add函数：</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->(add&nbsp;1&nbsp;2)</div></div><div>但是实际上add不是这个函数，只是这个函数绑定的名字，实际执行时会根据绑定的名字取出原来的Lambda函数：</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->((lambda&nbsp;(a&nbsp;b)<br />&nbsp;&nbsp;&nbsp;&nbsp;(+&nbsp;a&nbsp;b))&nbsp;1&nbsp;2)</div></div><div>然后把函数应用在1和2上，得到结果3</div><div>还可以把一个函数作为参数传给另一个函数：</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->(define&nbsp;(foo&nbsp;f)<br />&nbsp;&nbsp;(f&nbsp;1&nbsp;2))<br />(define&nbsp;add<br />&nbsp;&nbsp;(lambda&nbsp;(a&nbsp;b)<br />&nbsp;&nbsp;&nbsp;&nbsp;(+&nbsp;a&nbsp;b)))<br />(foo&nbsp;add)</div></div><div>这个程序就是把add函数作为参数传给foo函数，foo函数内部则取出绑定在f变量的传入的add函数，将该函数应用在1和2上，执行得到3后返回。</div><div>当然我们传入的函数不一定要绑定add这个名字，直接传入lambda函数也是一样的效果：</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->(define&nbsp;(foo&nbsp;f)<br />&nbsp;&nbsp;(f&nbsp;1&nbsp;2))<br />(foo&nbsp;(lambda&nbsp;(a&nbsp;b)<br />&nbsp;&nbsp;&nbsp;&nbsp;(+&nbsp;a&nbsp;b)))</div></div><div>既然能把Lambda传入一个绑定名字的函数，那能不能不要绑定名字而是直接把Lambda函数传递到另一个Lambda函数中呢？当然可以。我们可以看到上面的foo函数本质上就是Lambda函数，也就是：</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->(define&nbsp;(foo&nbsp;f)<br />&nbsp;&nbsp;(f&nbsp;1&nbsp;2))</div></div><div>和：</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->(define&nbsp;foo<br />&nbsp;&nbsp;(lambda&nbsp;(f)<br />&nbsp;&nbsp;&nbsp;&nbsp;(f&nbsp;1&nbsp;2)))</div></div><div>是等价的。我们继续做上面做过的类似C语言的inline操作，也就是手工把函数&#8220;展开&#8221;，可以得到</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->((lambda&nbsp;(f)<br />&nbsp;&nbsp;&nbsp;&nbsp;(f&nbsp;1&nbsp;2))&nbsp;(lambda&nbsp;(a&nbsp;b)<br />&nbsp;&nbsp;&nbsp;&nbsp;(+&nbsp;a&nbsp;b)))</div></div><div>此时原本清晰的程序已经非常难看了，但是运行的结果也是一样的。经过上面这一系列示例，你对Lambda演算有初步的概念了吗？</div><div></div><div><strong>三：传入自己</strong></div><div>回到之前的求阶乘的问题上：</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->(lambda&nbsp;(n)<br />&nbsp;&nbsp;(<span style="color: #0000FF; ">if</span>&nbsp;(&gt;&nbsp;n&nbsp;0)&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(*&nbsp;n&nbsp;(???&nbsp;(-&nbsp;n&nbsp;1)))<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1))</div></div><div>lambda变量里不能调用一个外部的绑定名字的函数，当然Lambda函数本身也不能有名字（所以在某些编程语言里Lambda函数这个概念又叫做&#8220;匿名函数&#8221;）,既然自己没有名字那如何调用自己呢？通过上一节的讨论结论很明显了，就是把&#8220;自己&#8221;或者说跟自己功能一样的函数作为参数传给自己，然后自己就可以从参数列表中取出&#8220;自己&#8221;或者说跟自己功能一样的函数进行调用。</div><div>程序修改为：</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->(lambda&nbsp;(f)<br />&nbsp;&nbsp;(lambda&nbsp;(n)<br />&nbsp;&nbsp;&nbsp;&nbsp;(<span style="color: #0000FF; ">if</span>&nbsp;(=&nbsp;n&nbsp;0)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(*&nbsp;n&nbsp;(f&nbsp;(-&nbsp;n&nbsp;1))))))</div></div><div>其中f为&#8220;跟&#8216;自己&#8217;功能一样的函数&#8221;，上面写的函数是阶乘函数吗？不是，他本身是一个Lambda函数，接受了一个f参数，并且返回了一个使用f参数的Lambda函数。有人觉得很奇怪为什么返回的另一个函数居然能使用f这个参数，这就涉及函数式编程语言中流行的另一个概念，叫&#8220;闭包&#8221;。关于闭包的更多信息请参考**这篇文章**。可见，以上的函数不是阶乘函数而是&#8220;阶乘函数生成器&#8221;，为了方便下面解释暂时绑定一个名字。</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->(define&nbsp;factorial-maker<br />&nbsp;&nbsp;(lambda&nbsp;(f)<br />&nbsp;&nbsp;&nbsp;&nbsp;(lambda&nbsp;(n)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(<span style="color: #0000FF; ">if</span>&nbsp;(=&nbsp;n&nbsp;0)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(*&nbsp;n&nbsp;(f&nbsp;(-&nbsp;n&nbsp;1)))))))</div></div><div>那么传入的&#8220;跟自己功能一样的函数&#8221;是一个什么样的函数呢？是一个阶乘函数，阶乘函数的写法是：</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->(define&nbsp;factorial<br />&nbsp;&nbsp;(lambda&nbsp;(n)<br />&nbsp;&nbsp;&nbsp;&nbsp;(<span style="color: #0000FF; ">if</span>&nbsp;(=&nbsp;n&nbsp;0)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(*&nbsp;n&nbsp;(factorial&nbsp;(-&nbsp;n&nbsp;1))))))</div></div><div>但是慢着，factorial函数可不能调用自己啊，那不能调用自己调用谁呢？可以调用之前写的&#8220;阶乘生成器&#8221;，于是阶乘函数改为：</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->(define&nbsp;factorial<br />&nbsp;&nbsp;(lambda&nbsp;(n)<br />&nbsp;&nbsp;&nbsp;&nbsp;(<span style="color: #0000FF; ">if</span>&nbsp;(=&nbsp;n&nbsp;0)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(*&nbsp;n&nbsp;((factorial-maker&nbsp;factorial)&nbsp;(-&nbsp;n&nbsp;1))))))</div></div><div>这里用到的参数factorial还是不存在的，那么如何得到factorial呢？还是得调用factorial-maker产生：</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->(define&nbsp;factorial<br />&nbsp;&nbsp;(lambda&nbsp;(n)<br />&nbsp;&nbsp;&nbsp;&nbsp;(<span style="color: #0000FF; ">if</span>&nbsp;(=&nbsp;n&nbsp;0)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(*&nbsp;n&nbsp;((factorial-maker&nbsp;(factorial-maker&nbsp;factorial))&nbsp;(-&nbsp;n&nbsp;1))))))</div></div><div>这似乎要无数次调用factorial-maker，没完没了&#8230;&#8230;</div><div><strong>四：不动点</strong></div><div>从上一节引出的问题是，虽然factorial-maker能生成factorial，但是还是需要以factorial作为参数传入，而这与转而使用factorial-maker的目的相违背，所以我们得引入一个概念，叫&#8220;不动点&#8221;。</div><div>不动点的概念以前大家都应该接触过，维基百科里解释&#8220;在数学中，函数的不动点或定点是指被这个函数映射到其自身一个点。&#8221;</div><div>举例解释，比如函数：</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->f(x)&nbsp;=&nbsp;x&nbsp;*&nbsp;x</div></div><div>当x分别为0或1时，函数的值也分别为0或1即原来的数，则0和1为函数f的不动点，也就是：</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->x&nbsp;=&nbsp;f(x)&nbsp;=&nbsp;f(f(x))&nbsp;=&nbsp;f(f(f(x)))</div></div><div>在编程语言里，这个概念又要进一步扩展，因为函数也可以作为输入。</div><div>假设有函数f(fn)，fn为函数f的输入参数，并且</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->fn&nbsp;=&nbsp;f(fn)</div></div><div>很容易发现f应用在fn上不管多少次，结果都一样：</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->fn&nbsp;=&nbsp;f(fn)&nbsp;=&nbsp;f(f(fn))&nbsp;=&nbsp;f(f(f(fn)))</div></div><div>函数可以作为参数传递、返回值也可以是个函数，说起来容易但是对于平常不使用&#8220;函数式语言&#8221;的程序员理解起来总是不太顺畅。</div><div>五：Y函数</div><div>有了不动点的概念，再考虑上面的问题，我们有了factorial-maker，也就是可以生成factorial的函数，但是需要的是factorial本身作为参数传入，那么如何获得factorial本身？假设我们有一个函数叫Y，这个函数的作用是输入一个函数的生成器也就是factorial-maker输出该函数本身如factorial：</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->factorial&nbsp;=&nbsp;Y(factorial-maker)&nbsp;&nbsp;&nbsp;(1)</div></div><div>用Scheme的语法写作(Y factorial-maker)可以得到我们想要的factorial。而且：</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->factorial&nbsp;=&nbsp;factorial-maker(factorial)&nbsp;=&nbsp;factorial-maker(factorial-maker(factorial))&nbsp;=&nbsp;<img src="http://www.cppblog.com/Images/dot.gif" alt="" />&nbsp;(2)</div></div><div>对于函数factorial应用任意次factorial-maker函数，都得到factorial本身，说明factorial本身是函数factorial-maker的不动点。</div><div>结合(1)和(2)可以得到：</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->Y(factorial-maker)&nbsp;=&nbsp;factorial-maker(factorial)&nbsp;(3)</div></div><div>(1)代入(3)的右边得到：</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->Y(factorial-maker)&nbsp;=&nbsp;factorial-maker(Y(factorial-maker))</div></div><div>于是我们需要的Y就出来了，用Scheme语言写出来就是：</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->(define&nbsp;Y<br />&nbsp;&nbsp;(lambda&nbsp;(f)<br />&nbsp;&nbsp;&nbsp;&nbsp;(f&nbsp;(Y&nbsp;f))))</div></div><div>整个程序则为：</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->(define&nbsp;Y<br />&nbsp;&nbsp;(lambda&nbsp;(f)<br />&nbsp;&nbsp;&nbsp;&nbsp;(f&nbsp;(Y&nbsp;f))))<br />(define&nbsp;factorial-maker<br />&nbsp;&nbsp;(lambda&nbsp;(f)<br />&nbsp;&nbsp;&nbsp;&nbsp;(lambda&nbsp;(n)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(<span style="color: #0000FF; ">if</span>&nbsp;(=&nbsp;n&nbsp;0)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(*&nbsp;n&nbsp;(f&nbsp;(-&nbsp;n&nbsp;1)))))))<br />(display&nbsp;((Y&nbsp;factorial-maker)&nbsp;5))</div></div><div>这个程序从字面上是正确的，但一旦运行则会根据运行环境的不同有不同的运行结果，卡死直至消耗完计算机资源、提示栈溢出、运行得到正确的120，都是有可能的。原因在于Y函数内部的Lambda函数可能会调用Y不断增加新的栈帧，还来不及执行函数f。</div><div>当然一个更明显的问题是，这个程序仍然没有仅仅借用Lambda运算完成重复的操作。</div><div><strong>六：Y函数（改进）</strong></div><div>接下来的推导就比较困难了，我现在还没能完全弄清楚怎么到我们常见的最终形式。</div><div>Y组合子常见的最终形式是：</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->Y&nbsp;=&nbsp;&#955;f.(&#955;x.f&nbsp;(x&nbsp;x))&nbsp;(&#955;x.f&nbsp;(x&nbsp;x))</div></div><div>用Scheme写出来则是：</div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->(define&nbsp;y-combinator<br />&nbsp;&nbsp;(lambda&nbsp;(f)<br />&nbsp;&nbsp;&nbsp;&nbsp;((lambda&nbsp;(x)&nbsp;(f&nbsp;(lambda&nbsp;(y)&nbsp;((x&nbsp;x)&nbsp;y))))<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(lambda&nbsp;(x)&nbsp;(f&nbsp;(lambda&nbsp;(y)&nbsp;((x&nbsp;x)&nbsp;y)))))))</div></div><div>这个最终形式的Y组合子可以工作在非Lazy的正确实现的Scheme里。</div><div><strong>七：用途</strong></div><div>在&#8220;大多数&#8221;普通的命令式编程语言里、甚至某些支持函数式编程的不标榜函数式的编程语言里，的确很难想象到为什么表达一个重复的过程，非得求助于Y，正如前面的例子所描述，绑定一个名字往往就可以直接解决问题。不过大部分函数式编程语言既然构建在Lambda演算的基础上，底层通常也是把我们看到的高级语言的假象展开为Lambda演算，对于这些编程语言的实现者来说Y组合子是实现一些特殊语法的必要设施。</div><div><strong>参考资料：</strong></div><div>Lambda calculus：<a href="http://en.wikipedia.org/wiki/Lambda_calculus">http://en.wikipedia.org/wiki/Lambda_calculus</a></div><div>不动点：<a href="http://zh.wikipedia.org/wiki/%E4%B8%8D%E5%8A%A8%E7%82%B9">http://zh.wikipedia.org/wiki/%E4%B8%8D%E5%8A%A8%E7%82%B9</a></div><div></div></div><div></div></div>
<div></div><img src ="http://www.cppblog.com/wuwu/aggbug/207556.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/wuwu/" target="_blank">呜呜</a> 2014-07-07 16:54 <a href="http://www.cppblog.com/wuwu/archive/2014/07/07/207556.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>