<?xml version="1.0" encoding="UTF-8"?><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/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Ruby | 酷 壳 - CoolShell</title>
	<atom:link href="https://coolshell.cn/tag/ruby/feed" rel="self" type="application/rss+xml" />
	<link>https://coolshell.cn</link>
	<description>享受编程和技术所带来的快乐 - Coding Your Ambition</description>
	<lastBuildDate>Mon, 28 Dec 2020 08:22:50 +0000</lastBuildDate>
	<language>zh-CN</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.2</generator>
	<item>
		<title>数据即代码：元驱动编程</title>
		<link>https://coolshell.cn/articles/10337.html</link>
					<comments>https://coolshell.cn/articles/10337.html#comments</comments>
		
		<dc:creator><![CDATA[Todd]]></dc:creator>
		<pubDate>Fri, 09 Aug 2013 02:18:31 +0000</pubDate>
				<category><![CDATA[C/C++语言]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[杂项资源]]></category>
		<category><![CDATA[编程语言]]></category>
		<category><![CDATA[C++]]></category>
		<category><![CDATA[Javascript]]></category>
		<category><![CDATA[Lisp]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=10337</guid>

					<description><![CDATA[<p>（感谢 @文艺复兴记（todd） 投递此文） 几个小伙伴在考虑下面这个各个语言都会遇到的问题： 问题：设计一个命令行参数解析API 一个好的命令行参数解析库一般...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/10337.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/10337.html">数据即代码：元驱动编程</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script><strong>（感谢 <a href="http://weibo.com/weidagang" target="_blank">@文艺复兴记</a>（todd） 投递此文）</strong></p>
<p>几个小伙伴在考虑下面这个各个语言都会遇到的问题：</p>
<p><strong>问题：设计一个命令行参数解析API</strong></p>
<p>一个好的命令行参数解析库一般涉及到这几个常见的方面：</p>
<p>1) 支持方便地生成帮助信息</p>
<p>2) 支持子命令，比如：git包含了push, pull, commit等多种子命令</p>
<p>3) 支持单字符选项、多字符选项、标志选项、参数选项等多种选项和位置参数</p>
<p>4) 支持选项默认值，比如：&#8211;port选项若未指定认为5037</p>
<p>5) 支持使用模式，比如：tar命令的-c和-x是互斥选项，属于不同的使用模式</p>
<p>经过一番考察，小伙伴们发现了这个几个有代表性的API设计：</p>
<p><strong>1. getopt()：</strong></p>
<p><a href="http://www.gnu.org/software/libc/manual/html_node/Getopt.html">getopt()</a>是libc的标准函数，很多语言中都能找到它的移植版本。</p>
<p><span id="more-10337"></span></p>
<pre data-enlighter-language="c" class="EnlighterJSRAW">
//C
while ((c = getopt(argc, argv, &quot;ac:d:&quot;)) != -1) {
    int this_option_optind = optind ? optind : 1;
    switch (c) {
    case &#039;a&#039;:
        printf (&quot;option a&quot;);
        aopt = 1;
        break;
    case &#039;c&#039;:
        printf (&quot;option c with value &#039;%s&#039;&quot;, optarg);
        copt = optarg;
        break;
    case &#039;d&#039;:
        printf (&quot;option d with value &#039;%s&#039;&quot;, optarg);
        dopt = optarg;
        break;
    case &#039;?&#039;:
        break;
    default:
        printf (&quot;?? getopt returned character code 0%o ??&quot;, c);
    }
}
</pre>
<p>getopt()的核心是一个类似printf的格式字符串的命令行参数描述串，如上面的&#8221;ac:d:&#8221;定义了&#8221;a&#8221;, &#8220;c&#8221;，&#8221;d&#8221;3个命令行参数，其中，a是一个标志符不需要参数，&#8221;c&#8221;和&#8221;d&#8221;需要跟参数。getopt()功能非常弱，只支持单个字符的标志选项和参数选项。如果按上面的5点来比对，基本上只能说是勉强支持第3点，其他几项只能靠程序自己来实现了，所以，想直接基于getopt()实现一个像git这样复杂的命令行参数是不可能的，只有自己来做很多的解析工作。小伙伴们看过getopt()之后一致的评价是:图样图森破。</p>
<p><strong>2. Google gflags</strong></p>
<p>接着，小伙伴们又发现了<a href="https://code.google.com/p/gflags/">gflags</a>这个Google出品C++命令行参数解析库。</p>
<pre data-enlighter-language="cpp" class="EnlighterJSRAW">
//C++
DEFINE_bool(memory_pool, false, &quot;If use memory pool&quot;);
DEFINE_bool(daemon, true, &quot;If started as daemon&quot;);
DEFINE_string(module_id, &quot;&quot;, &quot;Server module id&quot;);
DEFINE_int32(http_port, 80, &quot;HTTP listen port&quot;);
DEFINE_int32(https_port, 443, &quot;HTTPS listen port&quot;);

int main(int argc, char** argv) {
    ::google::ParseCommandLineFlags(&amp;argc, &amp;argv, true);

    printf(&quot;Server module id: %s&quot;, FLAGS_module_id.c_str());

    if (FLAGS_daemon) {
      printf(&quot;Run as daemon: %d&quot;, FLAGS_daemon);
    }
    if (FLAGS_memory_pool) {
      printf(&quot;Use memory pool: %d&quot;, FLAGS_daemon);
    }

    Server server;

    return 0;
}
</pre>
<p>小伙伴们看了后不由得感叹“真心好用啊”！的确，gflags简单地通过几个宏就定义了命令行选项，基本上很好的支持了上面提到的1，3，4这几项，比起getopt()来强多了。对于类似cp这样的小命令，gflags应该是够用了，但要达到git这种级别就显得有些单薄了。</p>
<p><strong>3. Ruby Commander</strong></p>
<p>接下来小伙伴们又发现了Ruby Commander库：</p>
<pre data-enlighter-language="ruby" class="EnlighterJSRAW">
//Ruby
# :name is optional, otherwise uses the basename of this executable
program :name, &#039;Foo Bar&#039;
program :version, &#039;1.0.0&#039;
program :description, &#039;Stupid command that prints foo or bar.&#039;
command :bar do |c|
  c.syntax = &#039;foobar bar [options]&#039;
  c.description = &#039;Display bar with optional prefix and suffix&#039;
  c.option &#039;--prefix STRING&#039;, String, &#039;Adds a prefix to bar&#039;
  c.option &#039;--suffix STRING&#039;, String, &#039;Adds a suffix to bar&#039;
  c.action do |args, options|
    options.default :prefix =&gt; &#039;(&#039;, :suffix =&gt; &#039;)&#039;
    say &quot;#{options.prefix}bar#{options.suffix}&quot;
  end
end
$ foobar bar
# =&gt; (bar)
$ foobar bar --suffix &#039;}&#039; --prefix &#039;{&#039;
# =&gt; {bar}
</pre>
<p>Commander库利用Ruby酷炫的语法定义了一种描述命令行参数的内部DSL，看起来相当高端大气上档次。除了上面的第5项之外，其他几项都有很好的支持，可以说Commander库的设计基本达到了git这种级别命令行参数解析的要求。只是，要搞懂Ruby这么炫的语法和这个库的使用方法恐怕就不如getopt()和gflags容易了。有小伙伴当场表示想要学习Ruby，但是也有小伙伴表示再看看其他库再说。</p>
<p><strong>4. Lisp cmdline库</strong></p>
<p>接下来，小伙伴们发现了Lisp方言Racket的<a href="http://docs.racket-lang.org/reference/Command-Line_Parsing.html">cmdline库</a>。</p>
<pre data-enlighter-language="ruby" class="EnlighterJSRAW">
//Lisp
(parse-command-line &quot;compile&quot; (current-command-line-arguments)
  `((once-each
     [(&quot;-v&quot; &quot;--verbose&quot;)
      ,(lambda (flag) (verbose-mode #t))
      (&quot;Compile with verbose messages&quot;)]
     [(&quot;-p&quot; &quot;--profile&quot;)
      ,(lambda (flag) (profiling-on #t))
      (&quot;Compile with profiling&quot;)])
    (once-any
     [(&quot;-o&quot; &quot;--optimize-1&quot;)
      ,(lambda (flag) (optimize-level 1))
      (&quot;Compile with optimization level 1&quot;)]
     [(&quot;--optimize-2&quot;)
      ,(lambda (flag) (optimize-level 2))
      ((&quot;Compile with optimization level 2,&quot;
        &quot;which implies all optimizations of level 1&quot;))])
    (multi
     [(&quot;-l&quot; &quot;--link-flags&quot;)
      ,(lambda (flag lf) (link-flags (cons lf (link-flags))))
      (&quot;Add a flag &lt;lf&gt; for the linker&quot; &quot;lf&quot;)]))
   (lambda (flag-accum file) file)
   &#039;(&quot;filename&quot;))
</pre>
<p>这是神马浮云啊?括号套括号，看起来很厉害的样子，但又不是很明白。看到这样的设计，有的小伙伴连评价都懒得评价了，但也有的小伙伴对Lisp越发崇拜，表示Lisp就是所谓的终极语言了，没有哪门语言能写出这么不明觉历的代码来！小伙伴们正准备打完收工，突然&#8230;</p>
<p><strong>5. Node.js的LineParser库</strong></p>
<p>发现了Node.js的<a href="https://github.com/weidagang/line-parser-js">LineParser库</a>:</p>
<p>[javascript]<br />
//JavaScript<br />
var meta = {<br />
    program : &#8216;adb&#8217;,<br />
    name : &#8216;Android Debug Bridge&#8217;,<br />
    version : &#8216;1.0.3&#8217;,<br />
    subcommands : [ &#8216;connect&#8217;, &#8216;disconnect&#8217;, &#8216;install&#8217; ],<br />
    options : {<br />
        flags : [<br />
            [ &#8216;h&#8217;, &#8216;help&#8217;, &#8216;print program usage&#8217; ],<br />
            [ &#8216;r&#8217;, &#8216;reinstall&#8217;, &#8216;reinstall package&#8217; ],<br />
            [ &#8216;l&#8217;, &#8216;localhost&#8217;, &#8216;localhost&#8217; ]<br />
        ],<br />
        parameters : [<br />
            [ null, &#8216;host&#8217;, &#8216;adb server hostname or IP address&#8217;, null ],<br />
            [ &#8216;p&#8217;, &#8216;port&#8217;, &#8216;adb server port&#8217;, 5037 ]<br />
        ]<br />
    },<br />
    usages : [<br />
        [ &#8216;connect&#8217;, [&#8216;host&#8217;, &#8216;[port]&#8217;], null, &#8216;connect to adb server&#8217;, adb_connect ],<br />
        [ &#8216;connect&#8217;, [ &#8216;l&#8217; ], null, &#8216;connect to the local adb server&#8217;, adb_connect ],<br />
        [ &#8216;disconnect&#8217;, null, null, &#8216;disconnect from adb server&#8217;, adb_disconnect ],<br />
        [ &#8216;install&#8217;, [&#8216;r&#8217;], [&#8216;package&#8217;], &#8216;install package&#8217;, adb_install ],<br />
        [ null, [&#8216;h&#8217;], null, &#8216;help&#8217;, adb_help ],<br />
    ]<br />
};</p>
<p>try {<br />
    var lineparser = require(&#8216;lineparser&#8217;);<br />
    var parser = lineparser.init(meta);<br />
    // adb_install will be invoked<br />
    parser.parse([&#8216;install&#8217;, &#8216;-r&#8217;, &#8216;/pkgs/bird.apk&#8217;]);<br />
}<br />
catch (e) {<br />
    console.error(e);<br />
}<br />
[/javascript]</p>
<p>天啊！？这是什么？我和小伙伴们彻底惊呆了！短短十几行代码就获得了上面5点的全面支持，重要的是小伙伴们居然一下子就看懂了，没有任何的遮遮掩掩和故弄玄虚。本来以为Ruby和Lisp很酷，小伙伴们都想马上去学Ruby和Lisp了，看到这个代码之后怎么感觉前面全是在装呢？有个小伙伴居然激动得哭着表示：我写代码多年，以为再也没有什么代码可以让我感动，没想到这段代码如此精妙，我不由得要赞叹了，实在是太漂亮了！</p>
<p>小伙伴们的故事讲完了，您看懂了吗？如果没有看懂的话，正题开始了：</p>
<p>在绝大多数语言中数据和代码可以说是泾渭分明，习惯C++、Java等主流语言的程序员很少去思考数据和代码之间的关系。与多数语言不同的是Lisp以“数据即代码，代码即数据”著称，Lisp用S表达式统一了数据和代码的形式而独树一帜。Lisp奇怪的S表达式和复杂的宏系统让许多人都感到Lisp很神秘，而多数Lisp教程要么强调函数式编程，要么鼓吹宏如何强大，反而掩盖了Lisp真正本质的东西，为此我曾写过一篇<a href="http://www.cnblogs.com/weidagang2046/archive/2012/06/03/tao_of_lisp.html">《Lisp的永恒之道》</a>介绍Lisp思想。</p>
<p>设计思想和具体技术的区别在于前者往往可以在不同的环境中以不同的形式展现出来。比如，熟悉函数式编程的程序员在理解了纯函数的优点后即使是用C语言也会更倾向于写出无副作用的函数来，这就是函数式思想在命令式环境的应用。所以，理解Lisp思想一定要能在非Lisp环境应用，才算是融汇贯通。</p>
<p>如果真正理解了Lisp的本质，那所谓的“数据即代码，代码即数据”一点儿也不神秘，这不就是我们每天打交道的配置文件吗！？如果你还不是很理解的话，我们通过下面几个问题慢慢分析：</p>
<p>1) 配置的本质是什么？为什么要在程序中使用配置文件？</p>
<p>不知道你是否意识到了，我们每天都在使用的各种各样的<strong>配置本质上是一种元数据也是一种DSL</strong>，这和Lisp基于S表达式的“数据即代码，代码即数据”没有本质区别。在C++、Java等程序中引入配置文件的目的正是用DSL弥补通用语言表达能力和灵活性的不足。我知道不少人喜欢从计算的角度来看到程序和语言，似乎只有图灵完备的语言如C++、Java、Python等才叫程序设计语言，而类似CSS和HTML这样的东西根本不能叫做程序设计语言。其实，在我看来这种观点过于狭隘，<strong>程序的本质是语义的表达</strong>，而语义表达不一定要是计算。</p>
<p>2) 配置是数据还是代码？</p>
<p>很明显，Both!说配置是数据，因为它是声明式的描述，能方便地修改和传输；说配置是代码，因为它在表达逻辑，你的程序实际上就是配置的解释器。</p>
<p>3) 配置的格式是什么？</p>
<p>配置的格式是任意的，可以自己定义语法，只要配以相应的解释器就行。不过更简单通用的做法是基于XML、JSON、或S表达式等标准结构，在此之上进一步定义schema。甚至完全不必是文件，在我们的项目中配置经常是放到用关系数据库中的。另外，下面我们还会看到用语言的Literal数据作为配置。</p>
<p>4) 业务逻辑都可以放到配置中吗？</p>
<p>这个问题的答案显然是：Yes！我没有遇到过不可以放入配置的逻辑，只是问题在于这样做是否值得，能达到什么效果。对于需要灵活变化，重复出现，有复用价值的东西放入作为配置是明智的选择。这篇文章的主要目的就在于介绍把<strong>主要业务逻辑都放到配置中，再通过程序解释执行配置的设计方法，我称之为：元驱动编程(Meta Driven Programming)</strong>。</p>
<p><!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" id="wp_rp_first"><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/5202.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg" alt="对象的消息模型" width="150" height="150" /></a><a href="https://coolshell.cn/articles/5202.html" class="wp_rp_title">对象的消息模型</a></li><li ><a href="https://coolshell.cn/articles/1839.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2009/11/oscar-meyer-wienermobile-150x150.jpg" alt="编程语言汽车" width="150" height="150" /></a><a href="https://coolshell.cn/articles/1839.html" class="wp_rp_title">编程语言汽车</a></li><li ><a href="https://coolshell.cn/articles/18360.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg" alt="程序员练级攻略（2018)  与我的专栏" width="150" height="150" /></a><a href="https://coolshell.cn/articles/18360.html" class="wp_rp_title">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href="https://coolshell.cn/articles/10739.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2013/12/lua-150x150.gif" alt="Lua简明教程" width="150" height="150" /></a><a href="https://coolshell.cn/articles/10739.html" class="wp_rp_title">Lua简明教程</a></li><li ><a href="https://coolshell.cn/articles/10169.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg" alt="类型的本质和函数式实现" width="150" height="150" /></a><a href="https://coolshell.cn/articles/10169.html" class="wp_rp_title">类型的本质和函数式实现</a></li><li ><a href="https://coolshell.cn/articles/5709.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg" alt="API设计：用流畅接口构造内部DSL" width="150" height="150" /></a><a href="https://coolshell.cn/articles/5709.html" class="wp_rp_title">API设计：用流畅接口构造内部DSL</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/10337.html">数据即代码：元驱动编程</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/10337.html/feed</wfw:commentRss>
			<slash:comments>77</slash:comments>
		
		
			</item>
		<item>
		<title>API设计：用流畅接口构造内部DSL</title>
		<link>https://coolshell.cn/articles/5709.html</link>
					<comments>https://coolshell.cn/articles/5709.html#comments</comments>
		
		<dc:creator><![CDATA[Todd]]></dc:creator>
		<pubDate>Mon, 31 Oct 2011 00:28:47 +0000</pubDate>
				<category><![CDATA[程序设计]]></category>
		<category><![CDATA[编程语言]]></category>
		<category><![CDATA[API]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[Javascript]]></category>
		<category><![CDATA[Ruby]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=5709</guid>

					<description><![CDATA[<p>感谢@weidagang （Todd）向酷壳投递本文。 程序设计语言的抽象机制包含了两个最基本的方面：一是语言关注的基本元素/语义；另一个是从基本元素/语义到复...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/5709.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/5709.html">API设计：用流畅接口构造内部DSL</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script><strong>感谢<a href="http://weibo.com/n/weidagang">@weidagang</a> （Todd）向酷壳投递本文。</strong></p>
<p>程序设计语言的抽象机制包含了两个最基本的方面：一是语言关注的基本元素/语义；另一个是从基本元素/语义到复合元素/语义的构造规则。在C、C++、Java、C#、Python等通用语言中，语言的基本元素/语义往往离问题域较远，通过API库的形式进行层层抽象是降低问题难度最常用的方法。比如，在C语言中最常见的方式是提供函数库来封装复杂逻辑，方便外部调用。</p>
<p>不过普通的API设计方法存在一种天然的陷阱，那就是不管怎样封装，大过程虽然比小过程抽象层次更高，但本质上还是过程，受到过程语义的制约。也就是说，通过基本元素/语义构造更高级抽象元素/语义的时候，语言的构造规则很大程度上限制了抽象的维度，我们很难跳出这个维度去，甚至可能根本意识不到这个限制。而SQL、HTML、CSS、make等DSL（领域特定语言）的抽象维度是为特定领域量身定做的，从这些抽象角度看问题往往最为简单，所以DSL在解决其特定领域的问题时比通用程序设计语言更加方便。通常，SQL等非通用语言被称为外部DSL（External DSL）；在通用语言中，我们其实也可以在一定程度上突破语言构造规则的抽象维度限制，定义内部DSL（Internal DSL）。</p>
<p>本文将介绍一种被称为流畅接口（Fluent Interface）的内部DSL设计方法。Wikipedia上<a title="Fluent Interface" href="http://en.wikipedia.org/wiki/Fluent_interface">Fluent Interface</a>的定义是：</p>
<blockquote><p>A fluent interface (as first coined by Eric Evans and Martin Fowler) is an implementation of an object oriented API that aims to provide for more readable code. A fluent interface is normally implemented by using method chaining to relay the instruction context of a subsequent call (but a fluent interface entails more than just method chaining).</p></blockquote>
<div>
<p>下面将分4个部分来逐步说明流畅接口在构造内部DSL中的典型应用。</p>
</div>
<h4><strong>1. 基本语义抽象</strong></h4>
<p>如果要输出0..4这5个数，我们一般会首先想到类似这样的代码：</p>
<pre data-enlighter-language="java" class="EnlighterJSRAW">
//Java
for (int i = 0; i &lt; 5; ++i) {
    system.out.println(i);
}</pre>
<p><span id="more-5709"></span></p>
<p>而Ruby虽然也支持类似的for循环，但最简单的是下面这样的实现：</p>
<pre data-enlighter-language="ruby" class="EnlighterJSRAW">
//Ruby
5.times {|i| puts i}</pre>
<p>Ruby中一切皆对象，5是Fixnum类的实例，times是Fixnum的一个方法，它接受一个block参数。相比for循环实现，Ruby的times方式更简洁，可读性更强，但熟悉OOP的朋友可能会有疑问，times是否应该作为整型类的方法呢？在OOP中，方法调用通常代表了向对象发送消息，改变或查询对象的状态，times方法显然不是对整型对象状态的查询和修改。如果你是Ruby的设计者，你会把times方法放入Fixnum类吗？如果答案是否定的，那么Ruby的这种设计本质上代表了什么呢？实际上，这里的times虽然只是一个普通的类方法，但它的目的却与普通意义上的类方法不同，它的语义实际上类似于for循环这样的语言基本语义，可以被视为一种自定义的基本语义。times的语义从一定程度上跳出了类方法的框框，向问题域迈进了一步！</p>
<p>另一个例子来自Eric Evans的“用两个时间点构造一个时间段对象”，普通设计：</p>
<pre data-enlighter-language="java" class="EnlighterJSRAW">
//Java
TimePoint fiveOClock, sixOClock;
TimeInterval meetingTime = new TimeInterval(fiveOClock, sixOClock);</pre>
<p>另一种Evans的设计是这样：</p>
<pre data-enlighter-language="java" class="EnlighterJSRAW">
//Java
TimeInterval meetingTime = fiveOClock.until(sixOClock);</pre>
<p>按传统OO设计，until方法本不应出现在TimePoint类中，这里TimePoint类的until方法同样代表了一种自定义的基本语义，使得表达时间域的问题更加自然。</p>
<p>虽然上面的两个简单例子和普通设计相比看不出太大的优势，但它却为我们理解流畅接口打下了基础。重要的是应该体会到它们从一定程度上跳出了语言基本抽象机制的束缚，我们不应该再用类职责划分、迪米特法则（Law of Demeter）等OO设计原则来看待它们。</p>
<h4><strong>2. 管道抽象</strong></h4>
<p>在Shell中，我们可以通过管道将一系列的小命令组合在一起实现复杂的功能。管道中流动的是单一类型的文本流，计算过程就是从输入流到输出流的变换过程，每个命令是对文本流的一次变换作用，通过管道将作用叠加起来。在Shell中，很多时候我们只需要一句话就能完成log统计这样的中小规模问题。和其他抽象机制相比，管道的优美在于无嵌套。比如下面这段C程序，由于嵌套层次较深，不容易一下子理解清楚：</p>
<pre data-enlighter-language="c" class="EnlighterJSRAW">
//C
min(max(min(max(a,b),c),d),e)
</pre>
<p>而用管道来表达同样的功能则清晰得多：</p>
<pre data-enlighter-language="shell" class="EnlighterJSRAW">
#!/bin/bash
max a b | min c | max d | min e
</pre>
<p>我们很容易理解这段程序表达的意思是：先求a, b的最大值；再把结果和c取最小值；再把结果和d求最大值；再把结果和e求最小值。</p>
<p>jQuery的链式调用设计也具有管道的风格，方法链上流动的是同一类型的jQuery对象，每一步方法调用是对对象的一次作用，整个方法链将各个方法的作用叠加起来。</p>
<pre data-enlighter-language="js" class="EnlighterJSRAW">
//Javascript
$(&#039;li&#039;).filter(&#039;:event&#039;).css(&#039;background-color&#039;, &#039;red&#039;);
</pre>
<h4>3. 层次结构抽象</h4>
<p>除了管道这种“线性”结构外，流畅接口还可用于构造层次结构抽象。比如，用Javascript动态创建创建下面的HTML片段：</p>
<pre data-enlighter-language="html" class="EnlighterJSRAW">
&lt;div id=&quot;’product_123’&quot; class=&quot;’product’&quot;&gt;
&lt;img src=&quot;’preview_123.jpg’&quot; alt=&quot;&quot; /&gt;
&lt;ul&gt;
	&lt;li&gt;Name: iPad2 32G&lt;/li&gt;
	&lt;li&gt;Price: 3600&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;

</pre>
<p>若采用Javascript的DOM API：</p>
<pre data-enlighter-language="js" class="EnlighterJSRAW">
//Javascript
var div = document.createElement(&#039;div&#039;);
div.setAttribute(‘id’, ‘product_123’);
div.setAttribute(‘class’, ‘product’);

var img = document.createElement(&#039;img&#039;);
img.setAttribute(‘src’, ‘preview_123.jpg’);
div.appendChild(img);

var ul = document.createElement(&#039;ul&#039;);
var li1 = document.createElement(&#039;li&#039;);
var txt1 = document.createTextNode(&quot;Name: iPad2 32G&quot;);
li1.appendChild(txt1);
…
div.appendChild(ul);</pre>
<p>而下面流畅接口API则要有表现力得多：</p>
<pre data-enlighter-language="java" class="EnlighterJSRAW">
//Javascript
var obj =
$.div({id:’product_123’, class:’product’})
    .img({src:’preview_123.jpg’})
    .ul()
        .li().text(‘Name: iPad2 32G’)._li()
        .li().text(‘Price: 3600’)._li()
    ._ul()
 ._div();</pre>
<div>和Javascript的标准DOM API相比，上面的API设计不再局限于孤立地看待某一个方法，而是考虑了它们在解决问题时的组合使用，所以代码的表现形式特别贴近问题的本质。这样的代码是自解释的（self-explanatory）在可读性方面要明显胜于DOM API，这相当于定义了一种类似于HTML的内部DSL，它拥有自己的语义和语法。需要特别注意的是，上面的层次结构抽象和管道抽象有着本质的不同，管道抽象的方法链上通常是同一对象的连续传递，而层次抽象中方法链上的对象却在随着层次的变化而变化。此为，我们可以把业务规则也表达在流畅接口中，比如上面的例子中，body()不能包含在div()返回的对象中，div().body()将抛出&#8221;body方法不存在”异常。</div>
<h4><strong>4. 异步抽象</strong></h4>
<div>流畅接口不仅可以构造复杂的层次抽象，还可以用于构造异步抽象。在基于回调机制的异步模式中，多个异步调用的同步和嵌套问题是使用异步的难点所在。有时一个稍复杂的调用和同步关系会导致代码充满了复杂的同步检查和层层回调，难以理解和维护。这个问题从本质上讲和上面HTML的例子一样，是由于多数通用语言并未把异步作为基本元素/语义，许多异步实现模式是向语言的妥协。针对这个问题，我用Javascript编写了一个基于流畅接口的异步DSL，示例代码如下：</div>
<div>[javascript]<br />
//Javascript<br />
$.begin()<br />
    .async(newTask(&#8216;task1&#8217;), &#8216;task1&#8217;)<br />
    .async(newTask(&#8216;task2&#8217;), &#8216;task2&#8217;)<br />
    .async(newTask(&#8216;task3&#8217;), &#8216;task3&#8217;)<br />
.when()<br />
    .each_done(function(name, result) {<br />
        console.log(name + &#8216;: &#8216; + result);})<br />
    .all_done(function(){ console.log(&#8216;good, all completed&#8217;); })<br />
    .timeout(function(){<br />
        console.log(&#8216;timeout!!&#8217;);<br />
        $.begin()<br />
            .async(newTask(&#8216;task4&#8217;), &#8216;task4&#8217;)<br />
        .when()<br />
            .each_done(function(name, result) {<br />
                console.log(name + &#8216;: &#8216; + result); })<br />
        .end();}<br />
        , 3000)<br />
.end();[/javascript]</p>
</div>
<div>上面的代码只是一句Javascript调用，但从另一个角度看它却像一段描述异步调用的DSL程序。它通过流畅接口定义了begin when end的语法结构，begin后面跟的是启动异步调用的代码；when后面是异步结果处理，可以选择each_done, all_done, timeout中的一种或多种。而begin when end结构本身是可以嵌套的，比如上面的代码在timeout处理分支中就包含了另一个begin when end结构。通过这个DSL，我们可以比基于回调的方式更好地表达异步调用的同步和嵌套关系。</div>
<p>上面介绍了用流畅接口构造的4种典型抽象，出此之外还有很多其他的抽象和应用场合，比如：不少单元测试框架就通过流畅接口定义了单元测试的DSL。虽然上面的例子以Javascript等动态语言居多，但其实流畅接口所依赖的语法基础并不苛刻，即使在Java这样的静态语言中，同样可以轻松地使用。流畅接口不同于传统的API设计，理解和使用流畅接口关键是要突破语言抽象机制带来的定势思维，根据问题域选取适当的抽象维度，利用语言的基本语法构造领域特定的语义和语法。</p>
<p><strong>参考</strong></p>
<ul>
<li><a title="Wikipedia: Fluent Interface" href="http://en.wikipedia.org/wiki/Fluent_interface">Wikipedia: Fluent Interface</a></li>
<li><a title="Martin Fowler: Fluent Interface" href="http://www.martinfowler.com/bliki/FluentInterface.html">Martin Fowler: Fluent Interface</a></li>
<li><a title="jQuery is DSL" href="http://www.cnblogs.com/cathsfz/archive/2009/08/10/1543266.html">jQuery is DSL</a></li>
<li><a title="An Approach to Internal Domain-Specific Languages in Java" href="http://www.infoq.com/articles/internal-dsls-java">An Approach to Internal Domain-Specific Languages in Java</a></li>
</ul>
<p><!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" ><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/18360.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg" alt="程序员练级攻略（2018)  与我的专栏" width="150" height="150" /></a><a href="https://coolshell.cn/articles/18360.html" class="wp_rp_title">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href="https://coolshell.cn/articles/10337.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg" alt="数据即代码：元驱动编程" width="150" height="150" /></a><a href="https://coolshell.cn/articles/10337.html" class="wp_rp_title">数据即代码：元驱动编程</a></li><li ><a href="https://coolshell.cn/articles/5202.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg" alt="对象的消息模型" width="150" height="150" /></a><a href="https://coolshell.cn/articles/5202.html" class="wp_rp_title">对象的消息模型</a></li><li ><a href="https://coolshell.cn/articles/3437.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2010/12/ediff-small-150x150.png" alt="一些杂项资源" width="150" height="150" /></a><a href="https://coolshell.cn/articles/3437.html" class="wp_rp_title">一些杂项资源</a></li><li ><a href="https://coolshell.cn/articles/2053.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg" alt="最为奇怪的程序语言的特性" width="150" height="150" /></a><a href="https://coolshell.cn/articles/2053.html" class="wp_rp_title">最为奇怪的程序语言的特性</a></li><li ><a href="https://coolshell.cn/articles/1992.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2009/12/language-fanboys-150x150.jpg" alt="程序员眼中的编程语言" width="150" height="150" /></a><a href="https://coolshell.cn/articles/1992.html" class="wp_rp_title">程序员眼中的编程语言</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/5709.html">API设计：用流畅接口构造内部DSL</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/5709.html/feed</wfw:commentRss>
			<slash:comments>32</slash:comments>
		
		
			</item>
		<item>
		<title>对象的消息模型</title>
		<link>https://coolshell.cn/articles/5202.html</link>
					<comments>https://coolshell.cn/articles/5202.html#comments</comments>
		
		<dc:creator><![CDATA[Todd]]></dc:creator>
		<pubDate>Mon, 15 Aug 2011 02:37:13 +0000</pubDate>
				<category><![CDATA[编程语言]]></category>
		<category><![CDATA[C++]]></category>
		<category><![CDATA[Javascript]]></category>
		<category><![CDATA[OOP]]></category>
		<category><![CDATA[Ruby]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=5202</guid>

					<description><![CDATA[<p>[ ———— 感谢 Todd 同学 投递本文，原文链接 ———— ] C++对象模型 话题从下面这段C++程序说起，你认为它可以顺利执行吗？ //C++ cla...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/5202.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/5202.html">对象的消息模型</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script><strong><span style="color: #cc0000;">[ ———— 感谢</span> <a href="http://www.cnblogs.com/weidagang2046/" target="_blank">Todd 同学</a> <span style="color: #cc0000;">投递本文，<a href="http://www.cnblogs.com/weidagang2046/archive/2011/08/14/2138059.html" target="_blank">原文链接</a> ———— ]</span></strong></p>
<h4><strong>C++对象模型</strong></h4>
<p>话题从下面这段C++程序说起，你认为它可以顺利执行吗？</p>
<pre data-enlighter-language="c" class="EnlighterJSRAW">//C++
class A {
    public:
        void Hello(const std::string&amp; name) {
           std::cout &lt;&lt; &quot;hello &quot; &lt;&lt; name;
         }
};
int main(int argc, char** argv)
{
    A* pa = NULL; //!!
    pa-&gt;Hello(&quot;world&quot;);
    return 0;
}</pre>
<p>试试的确可以顺利运行输出hello world，奇怪吗？其实并不奇怪，根据C++对象模型，类的非虚方法并不会存在于对象内存布局中，实际上编译器是把Hello方法转化成了类似这样的全局函数：</p>
<pre data-enlighter-language="c" class="EnlighterJSRAW">void A_Hello_xxx(A * const this, const std::string&amp; name) {
    std::cout &lt;&lt; “hello “ &lt;&lt; name;
}</pre>
<p>对象指针其实是作为第一个参数被隐式传递的，pa-&gt;Hello(“world”)实际上是调用的A_Hello_xxx(pa, “world”)，而恰好A_Hello_xxx内部没有使用pa，所以这段代码得以顺利运行。</p>
<h4><strong>对象的消息模型</strong></h4>
<p>如果是研究C++对象模型，上面的讨论可以到此为止，不过这里我想从另一个层面来继续探讨这个问题。OOP的先驱人物Alan Kay在总结Smalltalk的OO特征时强调：</p>
<p><span id="more-5202"></span></p>
<blockquote><p>Smalltalk is not only NOT its syntax or the class library, it is not even about classes. I&#8217;m sorry that I long ago coined the term &#8220;objects&#8221; for this topic because it gets many people to focus on the lesser idea. The big idea is &#8220;messaging&#8221;.</p></blockquote>
<p>也就是说相比类和对象的概念来讲，他认为对象交互的消息模型是OOP更为本质的特征，因为消息关注的是对象间的接口和交互，在构建大的系统的时候重要的不是对象/模块的内部状态，而是它们的交互。根据消息模型，牛.吃(草) 的语义是发送一条消息给“牛”，消息的类型是“吃”，消息的内容是“草”。如果按照严格的消息模型，那么上面那段C++代码应解释为向一个NULL对象发送Hello消息，这显然是不应该顺利执行的。类似的代码如果是在Java或C#中则会抛出空引用异常，所以Java和C#的设计更符合消息模型。</p>
<p>不过，Java和C#中也并非完全符合消息模型，来看一个经典的封装问题：</p>
<pre data-enlighter-language="csharp" class="EnlighterJSRAW">//C#

public class Account {
    private int _amount;

    public void Transfer(Account acc, int delta) {
        acc._amount += delta;
        this._amount -= delta;
    }
    …
}</pre>
<p>上面定义了一个Account类，问题在于为什么在这个类的Transfer方法中可以直接访问另一个对象acc的私有成员_amount呢？这是不是有破坏封装的嫌疑呢？这个问题经典的答案是：并不破坏封装，封装是划分了基于类的静态的代码边界，使得类的private代码修改不影响外界，而不是对于动态对象的保护。这个解释当然是合理的，不过正如上面C++代码的解释属于C++对象模型范畴，这个解释则属于基于类的静态类型OOP语言的范畴。消息模型强调了对象内部状态的保护，只能通过消息改变其状态，而对象内部是否真的具有_amout这样一个私有成员对其他任何对象（即使同类对象）都是未知的。</p>
<p>如果要严格遵守消息模型实现对象内部状态的保护应该怎么做呢？我们来看一个例子，定义一个集合类，包括：1.集合对象的构造函数；2.In方法：判断元素是否存在；3.Join方法：对两个集合做交集；4.Union方法：对两个集合做并集。下面是一种Javascript实现：</p>
<pre data-enlighter-language="js" class="EnlighterJSRAW">//Javascript

//集合类Set的构造函数
function Set() {
    var _elements = arguments;
    //In方法：判断元素e是否在集合中
    this.In = function(e) {
        for (var i = 0; i &lt; _elements.length; ++i) {
            if (_elements[i] == e) return true;
        }
        return false;
    };
}

//Join方法：对两个集合求交集
Set.prototype.Join = function(s2) {
    var s1 = this;
    var s = new Set();
    s.In = function(e) { return s1.In(e) &amp;&amp; s2.In(e); }
    return s;
};

//Union方法：对两个集合求并集
Set.prototype.Union = function(s2) {
    var s1 = this;
    var s = new Set();
    s.In = function(e) { return s1.In(e) || s2.In(e); }
    return s;
};

var s1 = new Set(1, 2, 3, 4, 5);
var s2 = new Set(2, 3, 4, 5, 6);
var s3 = new Set(3, 4, 5, 6, 7);
assert(false == s1.Join(s2).Join(s3).In(2));
assert(true == s1.Join(s2).Uion(s3).In(7));</pre>
<p>如果是在静态类型OOP语言中，要实现集合类的Join或Union，我们多半会像上面Account的例子一样直接对s2内部的_elements进行操作，而上面这段Javascript定义的Set关于对象s2的访问完全是符合消息模型的基于接口的访问。要实现消息模型Javascript的prototype机制并非必须的，真正的关键在于函数式的高级函数和闭包特性。从这个例子我们也可以体会到函数式的优点不仅在于无副作用，函数的可组合性也是函数式编程强大的原因。</p>
<h4><strong>Method Missing</strong></h4>
<p>接下来我们还要进行深度历险，让我们思考一下如果发送一条对象不能识别的消息会怎样？这种情况在C++、Java、C#等静态类型语言中会得到一个方法未定义的编译错误，如果是在Javascript中则会产生运行时异常。比如，s1.count()会产生一个运行时异常：Object #&lt;Set&gt; has no method &#8216;count&#8217;。</p>
<p>在静态类型语言这个问题很少受到重视，但在动态类型语言中却大有文章，来看下面的例子：<br />
//Ruby</p>
<pre data-enlighter-language="ruby" class="EnlighterJSRAW">
builder = Builder::XmlMarkup.new
xml = builder.books {|b|
    b.book :isbn =&gt; &quot;14134&quot; do
        b.title &quot;Revelation Space&quot;
        b.author &quot;Alastair Reynolds&quot;
    end
    b.book :isbn =&gt; &quot;53534&quot; do
        b.title &quot;Accelerando&quot;
        b.author &quot;Charles Stross&quot;
    end
}</pre>
<p>上面这段很DSL的Ruby代码创建了这样一个XML文件对象：</p>
<pre data-enlighter-language="xml" class="EnlighterJSRAW">

&lt;books&gt;
    &lt;book isbn=&quot;14134&quot;&gt;
        &lt;title&gt;Revelation Space&lt;/title&gt;
        &lt;author&gt;Alastair Reynolds&lt;/author&gt;
    &lt;/book&gt;
    &lt;book isbn=&quot;53534&quot;&gt;
        &lt;title&gt;Accelerando&lt;/title&gt;
        &lt;author&gt;Charles Stross&lt;/author&gt;
    &lt;/book&gt;
&lt;/books&gt;

</pre>
<p>builder.books, b.book, b.title都是对象方法调用，由于XML的元素名是任意的，所以不可能事先定义这些方法，类似的代码如果是在Javascript中就是no method异常。那为什么上面的Ruby代码可以正确执行呢？其实只要理解了消息模型就很容易想明白，只需要定义一个通用的消息处理方法，所有未明确定义的消息都交给它来处理就行了，这就是所谓的Method Missing模式：</p>
<pre data-enlighter-language="ruby" class="EnlighterJSRAW">
class Foo
    def method_missing(method, *args, &amp;block)
        …
    end
end
</pre>
<p>Method Missing除了对实现DSL很重要外，还可用于产生更好地调试和错误信息，把参数嵌入到方法名中等场合。目前，Ruby、Python、Groovy几种语言对Method Missing都有很好的支持，甚至在C# 4.0中也可以利用动态特性实现。</p>
<h4>总结</h4>
<p>本文主要介绍了对象的消息模型的特征，并比较了C++对象模型，Java、C#等基于类的静态类型语言中的对象模型与严格消息模型的差异，最后探讨了Method Missing相关话题。</p>
<h4>参考</h4>
<ul>
<li><a href="http://book.douban.com/subject/1484262/" target="_blank">Inside the C++ Object Model</a></li>
<li><a href="http://book.douban.com/subject/4031906/" target="_blank">冒号课堂 &#8211; 编程范式与OOP思想</a></li>
<li><a href="http://c2.com/cgi/wiki?AlanKaysDefinitionOfObjectOriented" target="_blank">Alan Kays Definition Of Object Oriented</a></li>
<li><a href="http://fitzgeraldnick.com/weblog/39/" target="_blank">OOP The Good Parts: Message Passing, Duck Typing, Object Composition, and not Inheritance</a></li>
<li><a href="http://olabini.com/blog/2010/04/patterns-of-method-missing/">Patterns of Method Missing</a></li>
<li><a href="http://haacked.com/archive/2009/08/26/method-missing-csharp-4.aspx">Fun With Method Missing and C# 4</a></li>
</ul>
<p>（全文完）<!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" ><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/10337.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg" alt="数据即代码：元驱动编程" width="150" height="150" /></a><a href="https://coolshell.cn/articles/10337.html" class="wp_rp_title">数据即代码：元驱动编程</a></li><li ><a href="https://coolshell.cn/articles/18360.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg" alt="程序员练级攻略（2018)  与我的专栏" width="150" height="150" /></a><a href="https://coolshell.cn/articles/18360.html" class="wp_rp_title">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href="https://coolshell.cn/articles/10739.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2013/12/lua-150x150.gif" alt="Lua简明教程" width="150" height="150" /></a><a href="https://coolshell.cn/articles/10739.html" class="wp_rp_title">Lua简明教程</a></li><li ><a href="https://coolshell.cn/articles/10169.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg" alt="类型的本质和函数式实现" width="150" height="150" /></a><a href="https://coolshell.cn/articles/10169.html" class="wp_rp_title">类型的本质和函数式实现</a></li><li ><a href="https://coolshell.cn/articles/6731.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2012/03/closure-150x150.png" alt="理解Javascript的闭包" width="150" height="150" /></a><a href="https://coolshell.cn/articles/6731.html" class="wp_rp_title">理解Javascript的闭包</a></li><li ><a href="https://coolshell.cn/articles/6668.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2012/02/joo_1-150x150.png" alt="再谈javascript面向对象编程 " width="150" height="150" /></a><a href="https://coolshell.cn/articles/6668.html" class="wp_rp_title">再谈javascript面向对象编程 </a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/5202.html">对象的消息模型</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/5202.html/feed</wfw:commentRss>
			<slash:comments>42</slash:comments>
		
		
			</item>
		<item>
		<title>StackOverflow的404错误页</title>
		<link>https://coolshell.cn/articles/2529.html</link>
					<comments>https://coolshell.cn/articles/2529.html#comments</comments>
		
		<dc:creator><![CDATA[陈皓]]></dc:creator>
		<pubDate>Fri, 25 Jun 2010 00:35:41 +0000</pubDate>
				<category><![CDATA[杂项资源]]></category>
		<category><![CDATA[轶事趣闻]]></category>
		<category><![CDATA[404]]></category>
		<category><![CDATA[brainfuck]]></category>
		<category><![CDATA[C++]]></category>
		<category><![CDATA[Perl]]></category>
		<category><![CDATA[Polyglot]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[StackOverflow]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=2529</guid>

					<description><![CDATA[<p>不知道大家有没有注意到StakeOverflow的404错误页面？其显示了下面的这个图片： 这个是一个很有意思的图片，不知道你看懂了吗？看上去像Python，又...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/2529.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/2529.html">StackOverflow的404错误页</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script>不知道大家有没有注意到StakeOverflow的<a href="http://stackoverflow.com/404" target="_blank">404错误页面</a>？其显示了下面的这个图片：</p>
<p style="text-align: center;"><img decoding="async" src="http://sstatic.net/stackoverflow/img/polyglot-404.png" alt="" width="500" /></p>
<p style="text-align: left;">这个是一个很有意思的图片，不知道你看懂了吗？看上去像Python，又像 Ruby，还像 Perl，当然也有 C的影子，还有<a href="https://coolshell.cn/articles/1142.html" target="_blank">Brainfuck</a>。是的，这是一个杂交程序，杂交了Python，Ruby，Perl，C，还有Brainfuck（注意其中的#号），所有的语句都是输出“404”字符串。</p>
<p style="text-align: left;">关于这种杂交程序，本站以前也发布过《<a rel="bookmark" href="https://coolshell.cn/articles/1824.html" target="_blank">C语言和sh脚本的杂交代码</a>》，大家可以前往一看。这样的有趣的玩法叫“<a rel="nofollow" href="http://en.wikipedia.org/wiki/Polyglot_%28computing%29" target="_blank">Polyglot</a>”，也就是说，把N种语言写在一个文件中，然后，该文件在任何编译器下都可以运行，上述的那段代码在Python，Ruby，Perl，Brainfuck下都可以正常运行，也可以被C和的编译器编译通过，并被运行。</p>
<p style="text-align: left;">下面是这个图片的字符码，以供各位试试。</p>
<p style="text-align: left;"><span id="more-2529"></span></p>
<pre><code># define v putchar
#   define print(x) main(){v(4+v(v(52)-4));return 0;}/*
#&gt;+++++++4+[&gt;++++++&lt;-]&gt;++++.----.++++.*/
print(202*2);exit();
#define/*&gt;.@*/exit()</code></pre>
<p style="text-align: left;">欢迎你留下你的看法。</p>
<p style="text-align: left;">（全文完）</p>
<p><!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" ><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/1839.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2009/11/oscar-meyer-wienermobile-150x150.jpg" alt="编程语言汽车" width="150" height="150" /></a><a href="https://coolshell.cn/articles/1839.html" class="wp_rp_title">编程语言汽车</a></li><li ><a href="https://coolshell.cn/articles/1532.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg" alt="到处都是Unix的胎记" width="150" height="150" /></a><a href="https://coolshell.cn/articles/1532.html" class="wp_rp_title">到处都是Unix的胎记</a></li><li ><a href="https://coolshell.cn/articles/18360.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg" alt="程序员练级攻略（2018)  与我的专栏" width="150" height="150" /></a><a href="https://coolshell.cn/articles/18360.html" class="wp_rp_title">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href="https://coolshell.cn/articles/10337.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg" alt="数据即代码：元驱动编程" width="150" height="150" /></a><a href="https://coolshell.cn/articles/10337.html" class="wp_rp_title">数据即代码：元驱动编程</a></li><li ><a href="https://coolshell.cn/articles/10169.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg" alt="类型的本质和函数式实现" width="150" height="150" /></a><a href="https://coolshell.cn/articles/10169.html" class="wp_rp_title">类型的本质和函数式实现</a></li><li ><a href="https://coolshell.cn/articles/7886.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2012/07/muxnt-150x150.jpg" alt="代码执行的效率" width="150" height="150" /></a><a href="https://coolshell.cn/articles/7886.html" class="wp_rp_title">代码执行的效率</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/2529.html">StackOverflow的404错误页</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/2529.html/feed</wfw:commentRss>
			<slash:comments>27</slash:comments>
		
		
			</item>
		<item>
		<title>程序员眼中的编程语言</title>
		<link>https://coolshell.cn/articles/1992.html</link>
					<comments>https://coolshell.cn/articles/1992.html#comments</comments>
		
		<dc:creator><![CDATA[陈皓]]></dc:creator>
		<pubDate>Thu, 24 Dec 2009 06:31:25 +0000</pubDate>
				<category><![CDATA[编程语言]]></category>
		<category><![CDATA[轶事趣闻]]></category>
		<category><![CDATA[C++]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Programmer]]></category>
		<category><![CDATA[programming language]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[程序员]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=1992</guid>

					<description><![CDATA[<p>下图是一个搞笑的图片——程序员眼中的编程语言。 图片的横轴是编程语言。 纵轴是各语言的程序员、粉丝、信徒。 中间的各个小图片则是，粉丝眼中的编程语言的形象。 比...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/1992.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/1992.html">程序员眼中的编程语言</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script>下图是一个搞笑的图片——程序员眼中的编程语言。</p>
<ul>
<li>图片的横轴是编程语言。</li>
<li>纵轴是各语言的程序员、粉丝、信徒。</li>
<li>中间的各个小图片则是，粉丝眼中的编程语言的形象。</li>
</ul>
<p>比如说，</p>
<ul>
<li>第一行第一列，是Java程序员看Java语言的样子，一幢现代化的大厦。</li>
<li>第一行第二列，是Java程序员看C语言，一个年老过时的骨灰级老头。</li>
<li>当然，C程序员看Java语言也比较搞，见第二行第一列。呵呵。</li>
</ul>
<p>其它的大家自己看吧。还有另外一个关于操作系统的《<a rel="bookmark" href="https://coolshell.cn/articles/1998.html">粉丝眼中的操作系统</a>》</p>
<p style="text-align: center;"><img decoding="async" loading="lazy" class="alignnone size-full wp-image-1994" title="程序员眼中的编程语言" src="https://coolshell.cn/wp-content/uploads/2009/12/language-fanboys.jpg" alt="程序员眼中的编程语言" width="575" height="420" srcset="https://coolshell.cn/wp-content/uploads/2009/12/language-fanboys.jpg 575w, https://coolshell.cn/wp-content/uploads/2009/12/language-fanboys-300x219.jpg 300w, https://coolshell.cn/wp-content/uploads/2009/12/language-fanboys-370x270.jpg 370w" sizes="(max-width: 575px) 100vw, 575px" /></p>
<p> </p>
<p><a href="https://coolshell.cn/wp-content/uploads/2009/12/language-fanboys.jpg"></a><!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" ><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/18360.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg" alt="程序员练级攻略（2018)  与我的专栏" width="150" height="150" /></a><a href="https://coolshell.cn/articles/18360.html" class="wp_rp_title">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href="https://coolshell.cn/articles/8088.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg" alt="对技术的态度" width="150" height="150" /></a><a href="https://coolshell.cn/articles/8088.html" class="wp_rp_title">对技术的态度</a></li><li ><a href="https://coolshell.cn/articles/4990.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2011/07/programmer-150x150.png" alt="程序员技术练级攻略" width="150" height="150" /></a><a href="https://coolshell.cn/articles/4990.html" class="wp_rp_title">程序员技术练级攻略</a></li><li ><a href="https://coolshell.cn/articles/4758.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg" alt="如何写出无法维护的代码" width="150" height="150" /></a><a href="https://coolshell.cn/articles/4758.html" class="wp_rp_title">如何写出无法维护的代码</a></li><li ><a href="https://coolshell.cn/articles/4102.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg" alt="如何学好C语言" width="150" height="150" /></a><a href="https://coolshell.cn/articles/4102.html" class="wp_rp_title">如何学好C语言</a></li><li ><a href="https://coolshell.cn/articles/1391.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg" alt="编程真难啊" width="150" height="150" /></a><a href="https://coolshell.cn/articles/1391.html" class="wp_rp_title">编程真难啊</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/1992.html">程序员眼中的编程语言</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/1992.html/feed</wfw:commentRss>
			<slash:comments>33</slash:comments>
		
		
			</item>
		<item>
		<title>编程语言汽车</title>
		<link>https://coolshell.cn/articles/1839.html</link>
					<comments>https://coolshell.cn/articles/1839.html#comments</comments>
		
		<dc:creator><![CDATA[陈皓]]></dc:creator>
		<pubDate>Tue, 24 Nov 2009 10:24:22 +0000</pubDate>
				<category><![CDATA[编程语言]]></category>
		<category><![CDATA[轶事趣闻]]></category>
		<category><![CDATA[Basic]]></category>
		<category><![CDATA[C++]]></category>
		<category><![CDATA[Erlang]]></category>
		<category><![CDATA[Haskell]]></category>
		<category><![CDATA[Lisp]]></category>
		<category><![CDATA[Perl]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Ruby]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=1839</guid>

					<description><![CDATA[<p>以前酷壳发布过《操作系统航空公司》戏谑了一下如果操作系统是航空公司会怎么样的一种情况。现在，我们来YY一下编程语言，如果编程语言是汽车，又会怎么样？ Ada  ...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/1839.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/1839.html">编程语言汽车</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script><strong><img decoding="async" loading="lazy" class="alignright" title="Oscar Mayer Wienermobile" src="https://coolshell.cn/wp-content/uploads/2009/11/oscar-meyer-wienermobile.jpg" alt="Oscar Mayer Wienermobile" width="225" height="167" /></strong>以前酷壳发布过《<a rel="bookmark" href="https://coolshell.cn/articles/1272.html">操作系统航空公司</a>》戏谑了一下如果操作系统是航空公司会怎么样的一种情况。现在，我们来YY一下编程语言，如果编程语言是汽车，又会怎么样？</p>
<li><strong>Ada</strong>   这是一辆坦克。一个很厚重但很丑的坦克，从不会崩溃。如果你告诉别人你正在驾驶Ada，别人会狂笑不已。但是，你会开着一辆跑车去打战吗？[from Amit Dubey]</li>
<li><strong>汇编语言</strong>   只是一个祼露在外的引擎。你不得不自己去造车，并向其提供汽油，但你在驾车时要小心，因为他会像一只从地狱放出来的蝙蝠一样。其实，对于汇编语言，你自己才是车。[From &#8220;Subterfug&#8221; off digg.com:]</li>
<li><strong>Basic</strong>   是一辆很简单的车，对于一些短途的交通比如去一些超市商店，他是很有用的。以前这是一个对初学者很流行的车，然而，近来它蜕变成脚本，而更新的车型被抛光以应对长途旅程，但那也只是新瓶装旧酒。[from Przemyslaw Wrzos]</li>
<li><strong>C</strong>   是一辆赛车，它的速度是令人难以想象的快，可惜的是它每50公里就会损毁一次。</li>
<li><strong>Cobol</strong>   号称是一辆车，但是，没有哪个“有自尊的司机”会承认以前驾驶过它。</li>
<li><strong>C#</strong>   是一个竞争性的家庭旅行车。一旦你开始使用，你就别想再使用别的竞争者的产品了。</li>
<li><strong>C++</strong>   是一个加大马力的C赛车，其有一堆新增的功能，而且，它只会每250公里损毁一次。可是，一旦它有故障，没人会知道故障发生在哪里。</li>
<p><span id="more-1839"></span></p>
<li><strong>Eiffel</strong>   是一个车，其包括了一个法国口音的内建的驾驶讲师。他会帮你很快的识别你的错误，但是你不能和他争，不然，他会凌辱你后毫不迟疑地把你扔到窗外。[From Daniel Prager ]</li>
<li><strong>Erlang</strong>   是一个汽车车队，你想去哪它都会非常合作。你只需要用一只脚就可以开动好几辆车。但是，一旦你学会了如何在它给你设计的地形上驾驶，你就会很难在别的地形上驾驶了。另外，由于你一次驾驶好几辆车，所以，就算是其中几车损毁了也无关紧要。</li>
<li><strong>Forth</strong>   是一辆你通过一些工具可以自己造出来的车。你的这个车不需要像别的车。然后，一辆Forth 车只有倒档。[By &#8220;256byteram&#8221;, on a comment on Digg.com ]</li>
<li><strong>Fortran</strong>   是一个非常漂亮的老爷车。它可以走得很快，但条件是那是一条很直的路，而且路上只有你自己。我们相信，学习去驾驶一辆Fortran车，你就可能去学习别的车型。</li>
<li><strong>Java</strong>   也是一个家用旅行车，很容易驾驶，但不是很快，而且这是一个你无法伤害自己的车。</li>
<li><strong>Haskell</strong>  是一个令人难以想象的超完美设计的相当漂亮的车，有谣言说，这是一辆要可以行驶在极端怪异地形上的车。有一天，你尝试着要去开它，但你发现它并不是顺着路行驶，而是，它把自己和道路都复制了很多份，每一个道路的复制品上都有一辆车，而这些车的位置都比前一个要往前一些。按理来说，我们可以更便捷地驾驶它，但你却对数据不是很懂，所以，你不知道怎么做。<br />
[Monadic 版:]<strong>Haskell</strong>  并不是一个真正的车。这是一个抽象机器，你需要给足你是怎么去驾驶汽车的流程描述。你不得不把这些抽象机器放到某一个真实的机器中，这样它才能真正的行驶。你并不需要知道，那个真实的机器是怎么工作的。而且，我们还可以把多个抽象机器作成一个抽象机器，这样，当你把其放进真实机器中时，你就能去很多地方了。</li>
<li><strong>Lisp</strong>  看上去像一辆车，但你只需要调整，你可把它变成一个飞机或是一个潜水艇。[from Paul Tanimoto:] 首先，这看起来并不像一辆车，但是你会发现还是有人在开他四处走。在你决定去学习驾驶它后，你会意识到这是一辆你可以制造更多的车的车。你告诉你的朋友，但你的朋友们嘲笑你说这个车看起来太怪异了。但就算是这样，你还是始终在你的车库中放着一辆Lisp，并希望有一天你的朋友会开关他到街上。</li>
<li><strong>Mathematica</strong>   是一个设置精良的车，其从Lisp借鉴了很多但却没有得到应得的声望。它可以知道什么才是到达目的地最有效的道路，但是那需要运气。</li>
<li><strong>Matlab</strong>   是一辆设计给新手司机使用的车，它过可用作一些短途用途，而且，适合它的地形也不多，和是那些“数学车”适合的地形差不多。在这种地面上，驾驶它是非常舒服的，但是一旦你离开适合它的地形，就算是一小辆Matlab的车也会变得很难驾驶。而很多专业的司机都拒绝承认这是一辆车。</li>
<li><strong>Ocaml</strong>   是一个很性感的欧洲车。它并不像 <strong>C </strong>一样的快，但他永远不会被损毁。然后，这是法国式的，所有的控制装置都不在正常的位置。</li>
<li><strong>Perl</strong>   本来应该是一个很酷的车，但是它的驾驶员手册相当的难以理解。另外，即使你能搞懂如何驾驶Perl车，你也不能去驾驶别的车。</li>
<li>
<p style="TEXT-ALIGN: left"><strong>PHP</strong>   是一个 Oscar Mayer Wienermobile（见本文文章头上的图片），它是一个很怪异的车，但是还是有很多的人喜欢去驾驶它。 [from &#8220;CosmicJustice&#8221; off of digg.com]</p>
</li>
<li><strong>Prolog</strong>   是一个完全自动化的车：你只要告诉它目的地是什么样的，它就可以带着你去那。[附录 from Paul Graham:] 然而，说明目的地的工作量和你自己开车到那里的工作时是一样的。[另一个版本] <strong>Prolog</strong>   这个车有一个独一无二的GPS装置。它会去为你寻找你要到的目的地，如果到了路的尽头还没有找到，那么，他会回来然后再去试另一条路，直到找到你的目的地为止。</li>
<li><strong>Python</strong>   是一个相当不错的入门者的车。你没有驾照也可以驾驶它。除非，你真的想把它开得很快，或是在很BT的地形上驾驶。有了它，你可能不再需要别的车。</li>
<li><strong>Ruby</strong>   是一个把Perl, Python和Smalltalk三辆车混合起来的一辆拼装车。一个日本的技师找到了Perl, Python和Smalltalk一些碎片并把这些碎片拼成成了一辆车。很多司机认为这个拼装车比其它三个全部加起来都好。而其它一些司机却喃喃道，这个车提供了很多重复的功能，甚至是三重一样的功能，这些重复的功能在不固定的环境下却又有一些细小的不同，这些重复的功能让这个车更难驾驶。有谣言说Ruby这个车要重新设计。</li>
<li><strong>Smalltalk</strong>   只是一个小型车，其原来的目的只是为了让大家学习驾驶。但是，这个车设计的太好了，就算是很有经验的老手也很喜欢驾驶它。它开起来并不是很快，但是你可以把这个车的各个部件全部解开，并且换上你像要的部件，或是组装成你喜欢的样子。你可以给他发一个短信告诉它你要去哪，它会带着你去那，或是告诉你它听不懂你在说什么。很人性化的一辆车。</li>
<li><strong>Visual Basic</strong>   这是一辆驾驭你的车。 [from &#8220;yivkX360&#8221; on digg.com]</li>
<p> </p>
<p>文章：<a href="http://www.cs.caltech.edu/~mvanier/hacking/rants/cars.html" target="_blank">来源</a><!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" ><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/1532.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg" alt="到处都是Unix的胎记" width="150" height="150" /></a><a href="https://coolshell.cn/articles/1532.html" class="wp_rp_title">到处都是Unix的胎记</a></li><li ><a href="https://coolshell.cn/articles/2529.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg" alt="StackOverflow的404错误页" width="150" height="150" /></a><a href="https://coolshell.cn/articles/2529.html" class="wp_rp_title">StackOverflow的404错误页</a></li><li ><a href="https://coolshell.cn/articles/10337.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg" alt="数据即代码：元驱动编程" width="150" height="150" /></a><a href="https://coolshell.cn/articles/10337.html" class="wp_rp_title">数据即代码：元驱动编程</a></li><li ><a href="https://coolshell.cn/articles/7886.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2012/07/muxnt-150x150.jpg" alt="代码执行的效率" width="150" height="150" /></a><a href="https://coolshell.cn/articles/7886.html" class="wp_rp_title">代码执行的效率</a></li><li ><a href="https://coolshell.cn/articles/2053.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg" alt="最为奇怪的程序语言的特性" width="150" height="150" /></a><a href="https://coolshell.cn/articles/2053.html" class="wp_rp_title">最为奇怪的程序语言的特性</a></li><li ><a href="https://coolshell.cn/articles/1992.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2009/12/language-fanboys-150x150.jpg" alt="程序员眼中的编程语言" width="150" height="150" /></a><a href="https://coolshell.cn/articles/1992.html" class="wp_rp_title">程序员眼中的编程语言</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/1839.html">编程语言汽车</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/1839.html/feed</wfw:commentRss>
			<slash:comments>23</slash:comments>
		
		
			</item>
		<item>
		<title>到处都是Unix的胎记</title>
		<link>https://coolshell.cn/articles/1532.html</link>
					<comments>https://coolshell.cn/articles/1532.html#comments</comments>
		
		<dc:creator><![CDATA[陈皓]]></dc:creator>
		<pubDate>Sun, 11 Oct 2009 10:01:06 +0000</pubDate>
				<category><![CDATA[Unix/Linux]]></category>
		<category><![CDATA[C++]]></category>
		<category><![CDATA[fork]]></category>
		<category><![CDATA[Haskell]]></category>
		<category><![CDATA[Perl]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[socket]]></category>
		<category><![CDATA[Unix]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=1532</guid>

					<description><![CDATA[<p>一说起Unix编程，不必多说，最著名的系统调用就是fork，pipe，exec，kill或是socket了（fork(2), execve(2), pipe(2...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/1532.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/1532.html">到处都是Unix的胎记</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script>一说起Unix编程，不必多说，最著名的系统调用就是fork，pipe，exec，kill或是socket了（<a href="http://www.kernel.org/doc/man-pages/online/pages/man2/fork.2.html"><code>fork(2)</code></a>, <a href="http://www.kernel.org/doc/man-pages/online/pages/man2/execve.2.html"><code>execve(2)</code></a>, <a href="http://www.kernel.org/doc/man-pages/online/pages/man2/pipe.2.html"><code>pipe(2)</code></a>, <a href="http://www.kernel.org/doc/man-pages/online/pages/man2/socketpair.2.html"><code>socketpair(2)</code></a>, <a href="http://www.kernel.org/doc/man-pages/online/pages/man2/select.2.html"><code>select(2)</code></a>, <a href="http://www.kernel.org/doc/man-pages/online/pages/man2/kill.2.html"><code>kill(2)</code></a>, <a href="http://www.kernel.org/doc/man-pages/online/pages/man2/sigaction.2.html"><code>sigaction(2)</code></a>）这些系统调用都像是Unix编程的胎记或签名一样，表明着它来自于Unix。</p>
<p>下面这篇文章，将向大家展示Unix下最经典的socket的编程例子——使用fork + socket来创建一个TCP/IP的服务程序。这个编程模式很简单，首先是创建Socket，然后把其绑定在某个IP和Port上上侦听连接，接下来的一般做法是使用一个fork创建一个client服务进程再加上一个死循环用于处理和client的交互。这个模式是Unix下最经典的Socket编程例子。</p>
<p>下面，让我们看看用C，Ruby，Python，Perl，PHP和Haskell来实现这一例子，你会发现这些例子中的Unix的胎记。如果你想知道这些例子中的技术细节，那么，向你推荐两本经典书——《Unix高级环境编程》和《Unix网络编程》。</p>
<p><span id="more-1532"></span></p>
<h4>C语言</h4>
<p>我们先来看一下经典的C是怎么实现的。</p>
<pre class="EnlighterJSRAW" data-enlighter-language="c">/**
 * A simple preforking echo server in C.
 *
 * Building:
 *
 * $ gcc -Wall -o echo echo.c
 *
 * Usage:
 *
 * $ ./echo
 *
 *   ~ then in another terminal ... ~
 *
 * $ echo 'Hello, world!' | nc localhost 4242
 *
 */

#include &lt;unistd.h&gt; /* fork, close */
#include &lt;stdlib.h&gt; /* exit */
#include &lt;string.h&gt; /* strlen */
#include &lt;stdio.h&gt; /* perror, fdopen, fgets */
#include &lt;sys/socket.h&gt;
#include &lt;sys/wait.h&gt; /* waitpid */
#include &lt;netdb.h&gt; /* getaddrinfo */

#define die(msg) do { perror(msg); exit(EXIT_FAILURE); } while (0)

#define PORT "4242"
#define NUM_CHILDREN 3

#define MAXLEN 1024

int readline(int fd, char *buf, int maxlen); // forward declaration

int
main(int argc, char** argv)
{
    int i, n, sockfd, clientfd;
    int yes = 1; // used in setsockopt(2)
    struct addrinfo *ai;
    struct sockaddr_in *client;
    socklen_t client_t;
    pid_t cpid; // child pid
    char line[MAXLEN];
    char cpid_s[32];
    char welcome[32];

    /* Create a socket and get its file descriptor -- socket(2) */
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd == -1) {
    die("Couldn't create a socket");
    }

    /* Prevents those dreaded "Address already in use" errors */
    if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const void *)&amp;yes, sizeof(int)) == -1) {
    die("Couldn't setsockopt");
    }

    /* Fill the address info struct (host + port) -- getaddrinfo(3) */
    if (getaddrinfo(NULL, PORT, NULL, &amp;ai) != 0) {
    die("Couldn't get address");
    }

    /* Assign address to this socket's fd */
    if (bind(sockfd, ai-&gt;ai_addr, ai-&gt;ai_addrlen) != 0) {
    die("Couldn't bind socket to address");
    }

    /* Free the memory used by our address info struct */
    freeaddrinfo(ai);

    /* Mark this socket as able to accept incoming connections */
    if (listen(sockfd, 10) == -1) {
    die("Couldn't make socket listen");
    }

    /* Fork you some child processes. */
    for (i = 0; i &lt; NUM_CHILDREN; i++) {
    cpid = fork();
    if (cpid == -1) {
        die("Couldn't fork");
    }

    if (cpid == 0) { // We're in the child ...
        for (;;) { // Run forever ...
        /* Necessary initialization for accept(2) */
        client_t = sizeof client;

        /* Blocks! */
        clientfd = accept(sockfd, (struct sockaddr *)&amp;client, &amp;client_t);
        if (clientfd == -1) {
            die("Couldn't accept a connection");
        }

        /* Send a welcome message/prompt */
        bzero(cpid_s, 32);
        bzero(welcome, 32);
        sprintf(cpid_s, "%d", getpid());
        sprintf(welcome, "Child %s echo&gt; ", cpid_s);
        send(clientfd, welcome, strlen(welcome), 0);

        /* Read a line from the client socket ... */
        n = readline(clientfd, line, MAXLEN);
        if (n == -1) {
            die("Couldn't read line from connection");
        }

        /* ... and echo it back */
        send(clientfd, line, n, 0);

        /* Clean up the client socket */
        close(clientfd);
        }
    }
    }

    /* Sit back and wait for all child processes to exit */
    while (waitpid(-1, NULL, 0) &gt; 0);

    /* Close up our socket */
    close(sockfd);

    return 0;
}

/**
 * Simple utility function that reads a line from a file descriptor fd,
 * up to maxlen bytes -- ripped from Unix Network Programming, Stevens.
 */
int
readline(int fd, char *buf, int maxlen)
{
    int n, rc;
    char c;

    for (n = 1; n &lt; maxlen; n++) {
    if ((rc = read(fd, &amp;c, 1)) == 1) {
        *buf++ = c;
        if (c == '\n')
        break;
    } else if (rc == 0) {
        if (n == 1)
        return 0; // EOF, no data read
        else
        break; // EOF, read some data
    } else
        return -1; // error
    }

    *buf = '\0'; // null-terminate
    return n;
}
</pre>
<h4>Ruby</h4>
<p>下面是Ruby，你可以看到其中的fork</p>
<pre class="EnlighterJSRAW" data-enlighter-language="ruby">
# simple preforking echo server in Ruby
require 'socket'

# Create a socket, bind it to localhost:4242, and start listening.
# Runs once in the parent; all forked children inherit the socket's
# file descriptor.
acceptor = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
address = Socket.pack_sockaddr_in(4242, 'localhost')
acceptor.bind(address)
acceptor.listen(10)

# Close the socket when we exit the parent or any child process. This
# only closes the file descriptor in the calling process, it does not
# take the socket out of the listening state (until the last fd is
# closed).
#
# The trap is guaranteed to happen, and guaranteed to happen only
# once, right before the process exits for any reason (unless
# it's terminated with a SIGKILL).
trap('EXIT') { acceptor.close }

# Fork you some child processes. In the parent, the call to fork
# returns immediately with the pid of the child process; fork never
# returns in the child because we exit at the end of the block.
3.times do
  fork do
    # now we're in the child process; trap (Ctrl-C) interrupts and
    # exit immediately instead of dumping stack to stderr.
    trap('INT') { exit }

    puts "child #$$ accepting on shared socket (localhost:4242)"
    loop {
      # This is where the magic happens. accept(2) blocks until a
      # new connection is ready to be dequeued.
      socket, addr = acceptor.accept
      socket.write "child #$$ echo&gt; "
      socket.flush
      message = socket.gets
      socket.write message
      socket.close
      puts "child #$$ echo'd: '#{message.strip}'"
    }
    exit
  end
end

# Trap (Ctrl-C) interrupts, write a note, and exit immediately
# in parent. This trap is not inherited by the forks because it
# runs after forking has commenced.
trap('INT') { puts "\nbailing" ; exit }

# Sit back and wait for all child processes to exit.
Process.waitall

</pre>
<h4>Python</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="python">"""
Simple preforking echo server in Python.
"""

import os
import sys
import socket

# Create a socket, bind it to localhost:4242, and start
# listening. Runs once in the parent; all forked children
# inherit the socket's file descriptor.
acceptor = socket.socket()
acceptor.bind(('localhost', 4242))
acceptor.listen(10)

# Ryan's Ruby code here traps EXIT and closes the socket. This
# isn't required in Python; the socket will be closed when the
# socket object gets garbage collected.

# Fork you some child processes. In the parent, the call to
# fork returns immediately with the pid of the child process;
# fork never returns in the child because we exit at the end
# of the block.
for i in range(3):
    pid = os.fork()

    # os.fork() returns 0 in the child process and the child's
    # process id in the parent. So if pid == 0 then we're in
    # the child process.
    if pid == 0:
        # now we're in the child process; trap (Ctrl-C)
        # interrupts by catching KeyboardInterrupt) and exit
        # immediately instead of dumping stack to stderr.
        childpid = os.getpid()
        print "Child %s listening on localhost:4242" % childpid
        try:
            while 1:
                # This is where the magic happens. accept(2)
                # blocks until a new connection is ready to be
                # dequeued.
                conn, addr = acceptor.accept()

                # For easier use, turn the socket connection
                # into a file-like object.
                flo = conn.makefile()
                flo.write('Child %s echo&gt; ' % childpid)
                flo.flush()
                message = flo.readline()
                flo.write(message)
                flo.close()
                conn.close()
                print "Child %s echo'd: %r" % \
                          (childpid, message.strip())
        except KeyboardInterrupt:
            sys.exit()

# Sit back and wait for all child processes to exit.
#
# Trap interrupts, write a note, and exit immediately in
# parent. This trap is not inherited by the forks because it
# runs after forking has commenced.
try:
    os.waitpid(-1, 0)
except KeyboardInterrupt:
    print "\nbailing"
    sys.exit()
</pre>
<h4>Perl</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="perl">#!/usr/bin/perl
use 5.010;
use strict;

# simple preforking echo server in Perl
use Proc::Fork;
use IO::Socket::INET;

sub strip { s/\A\s+//, s/\s+\z// for my @r = @_; @r }

# Create a socket, bind it to localhost:4242, and start listening.
# Runs once in the parent; all forked children inherit the socket's
# file descriptor.
my $acceptor = IO::Socket::INET-&gt;new(
    LocalPort =&gt; 4242,
    Reuse     =&gt; 1,
    Listen    =&gt; 10,
) or die "Couln't start server: $!\n";

# Close the socket when we exit the parent or any child process. This
# only closes the file descriptor in the calling process, it does not
# take the socket out of the listening state (until the last fd is
# closed).
END { $acceptor-&gt;close }

# Fork you some child processes. The code after the run_fork block runs
# in all process, but because the child block ends in an exit call, only
# the parent executes the rest of the program. If a parent block were
# specified here, it would be invoked in the parent only, and passed the
# PID of the child process.
for ( 1 .. 3 ) {
    run_fork { child {
        while (1) {
            my $socket = $acceptor-&gt;accept;
            $socket-&gt;printflush( "child $$ echo&gt; " );
            my $message = $socket-&gt;getline;
            $socket-&gt;print( $message );
            $socket-&gt;close;
            say "child $$ echo'd: '${\strip $message}'";
        }
        exit;
    } }
}

# Trap (Ctrl-C) interrupts, write a note, and exit immediately
# in parent. This trap is not inherited by the forks because it
# runs after forking has commenced.
$SIG{ 'INT' } = sub { print "bailing\n"; exit };

# Sit back and wait for all child processes to exit.
1 while 0 &lt; waitpid -1, 0;
</pre>
<h4>PHP</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="perl">
&lt;?
/*
Simple preforking echo server in PHP.
Russell Beattie (russellbeattie.com)
*/

/* Allow the script to hang around waiting for connections. */
set_time_limit(0);

# Create a socket, bind it to localhost:4242, and start
# listening. Runs once in the parent; all forked children
# inherit the socket's file descriptor.
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_bind($socket,'localhost', 4242);
socket_listen($socket, 10);

pcntl_signal(SIGTERM, 'shutdown');
pcntl_signal(SIGINT, 'shutdown');

function shutdown($signal){
    global $socket;
    socket_close($socket);
    exit();
}
# Fork you some child processes. In the parent, the call to
# fork returns immediately with the pid of the child process;
# fork never returns in the child because we exit at the end
# of the block.
for($x = 1; $x &lt;= 3; $x++){
   
    $pid = pcntl_fork();
   
    # pcntl_fork() returns 0 in the child process and the child's
    # process id in the parent. So if $pid == 0 then we're in
    # the child process.
    if($pid == 0){

        $childpid = posix_getpid();
       
        echo "Child $childpid listening on localhost:4242 \n";

        while(true){
            # This is where the magic happens. accept(2)
            # blocks until a new connection is ready to be
            # dequeued.
            $conn = socket_accept($socket);

            $message = socket_read($conn,1000,PHP_NORMAL_READ);
           
            socket_write($conn, "Child $childpid echo&gt; $message");
       
            socket_close($conn);
       
            echo "Child $childpid echo'd: $message \n";
       
        }

    }
}
#
# Trap interrupts, write a note, and exit immediately in
# parent. This trap is not inherited by the forks because it
# runs after forking has commenced.
try{

    pcntl_waitpid(-1, $status);

} catch (Exception $e) {

    echo "bailing \n";
    exit();

}</pre>
<h4>Haskell</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="haskell">import Network
import Prelude hiding ((-))
import Control.Monad
import System.IO
import Control.Applicative
import System.Posix
import System.Exit
import System.Posix.Signals

main :: IO ()
main = with =&lt;&lt; (listenOn - PortNumber 4242) where

  with socket = do
    replicateM 3 - forkProcess work
    wait

    where
    work = do
      installHandler sigINT (Catch trap_int) Nothing
      pid &lt;- show &lt;$&gt; getProcessID
      puts - "child " ++ pid ++ " accepting on shared socket (localhost:4242)"
     
      forever - do
        (h, _, _) &lt;- accept socket

        let write   = hPutStr h
            flush   = hFlush h
            getline = hGetLine h
            close   = hClose h

        write - "child " ++ pid ++ " echo&gt; "
        flush
        message &lt;- getline
        write - message ++ "\n"
        puts - "child " ++ pid ++ " echo'd: '" ++ message ++ "'"
        close

    wait = forever - do
      ( const () &lt;$&gt; getAnyProcessStatus True True  ) <code data-enlighter-language="raw" class="EnlighterJSRAW">catch</code> const trap_exit

    trap_int = exitImmediately ExitSuccess

    trap_exit = do
      puts "\nbailing"
      sClose socket
      exitSuccess

    puts = putStrLn

  (-) = ($)
  infixr 0 -

</pre>
<p>如果你知道更多的，请你告诉我们。（全文完）<!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" ><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/1839.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2009/11/oscar-meyer-wienermobile-150x150.jpg" alt="编程语言汽车" width="150" height="150" /></a><a href="https://coolshell.cn/articles/1839.html" class="wp_rp_title">编程语言汽车</a></li><li ><a href="https://coolshell.cn/articles/2529.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg" alt="StackOverflow的404错误页" width="150" height="150" /></a><a href="https://coolshell.cn/articles/2529.html" class="wp_rp_title">StackOverflow的404错误页</a></li><li ><a href="https://coolshell.cn/articles/18360.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg" alt="程序员练级攻略（2018)  与我的专栏" width="150" height="150" /></a><a href="https://coolshell.cn/articles/18360.html" class="wp_rp_title">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href="https://coolshell.cn/articles/7965.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2012/07/fork01jpg-150x150.jpg" alt="一个fork的面试题" width="150" height="150" /></a><a href="https://coolshell.cn/articles/7965.html" class="wp_rp_title">一个fork的面试题</a></li><li ><a href="https://coolshell.cn/articles/7886.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2012/07/muxnt-150x150.jpg" alt="代码执行的效率" width="150" height="150" /></a><a href="https://coolshell.cn/articles/7886.html" class="wp_rp_title">代码执行的效率</a></li><li ><a href="https://coolshell.cn/articles/2053.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg" alt="最为奇怪的程序语言的特性" width="150" height="150" /></a><a href="https://coolshell.cn/articles/2053.html" class="wp_rp_title">最为奇怪的程序语言的特性</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/1532.html">到处都是Unix的胎记</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/1532.html/feed</wfw:commentRss>
			<slash:comments>19</slash:comments>
		
		
			</item>
		<item>
		<title>让Ruby增加30%的性能改进</title>
		<link>https://coolshell.cn/articles/766.html</link>
					<comments>https://coolshell.cn/articles/766.html#comments</comments>
		
		<dc:creator><![CDATA[陈皓]]></dc:creator>
		<pubDate>Tue, 05 May 2009 15:44:55 +0000</pubDate>
				<category><![CDATA[Ruby]]></category>
		<category><![CDATA[编程语言]]></category>
		<category><![CDATA[Performance]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=766</guid>

					<description><![CDATA[<p>一切都和 --enable-pthread 有关 问一下 Ruby 黑客怎么简单地增加一个线程的Ruby应用程序的性能。也许，这些黑客会告诉你，“小伙，每个人都...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/766.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/766.html">让Ruby增加30%的性能改进</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script></p>
<h4>一切都和 <code>--enable-pthread</code> 有关</h4>
<p>问一下 Ruby 黑客怎么简单地增加一个线程的Ruby应用程序的性能。也许，这些黑客会告诉你，“<strong>小伙，每个人都知道在编译Ruby的时候你需要使用<code>configure</code> 的 <code>--disable-pthread</code>参数</strong>”。没错，在<code>configure</code> <code>--disable-pthread</code> 可以让你得到大约 30% 性能提高。但是，这是为什么呢？</p>
<p>所有的这一些我们需要使用 <a href="http://timetobleed.com/hello-world/">strace</a> 工具，这个工具可以打出所有的真实的操作系统的调用。</p>
<p>下面，是一段我们测试的例程：</p>
<p><span id="more-766"></span></p>
<pre data-enlighter-language="ruby" class="EnlighterJSRAW">
def make_thread
  Thread.new {
    a = []
    10_000_000.times {
      a &lt;&lt; &quot;a&quot;
      a.pop
    }
  }
end

t = make_thread
t1 = make_thread

t.join
t1.join
</pre>
<p>如果我们使用 <code>strace</code> 工具去测试 <code>configure</code> <code>--enable-pthread</code> 版本的Ruby引擎，那么我们可以得到下面这样的结果：</p>
<pre data-enlighter-language="shell" class="EnlighterJSRAW">
22:46:16.706136 rt_sigprocmask(SIG_BLOCK, NULL, [], 8 ) = 0 &lt;0.000004&gt;
22:46:16.706177 rt_sigprocmask(SIG_BLOCK, NULL, [], 8 ) = 0 &lt;0.000004&gt;
22:46:16.706218 rt_sigprocmask(SIG_BLOCK, NULL, [], 8 ) = 0 &lt;0.000004&gt;
22:46:16.706259 rt_sigprocmask(SIG_BLOCK, NULL, [], 8 ) = 0 &lt;0.000005&gt;
22:46:16.706301 rt_sigprocmask(SIG_BLOCK, NULL, [], 8 ) = 0 &lt;0.000004&gt;
22:46:16.706342 rt_sigprocmask(SIG_BLOCK, NULL, [], 8 ) = 0 &lt;0.000004&gt;
22:46:16.706383 rt_sigprocmask(SIG_BLOCK, NULL, [], 8 ) = 0 &lt;0.000004&gt;
</pre>
<p>你会发现上面的sigprocmask 系统调用一页一页又一页地没完没了的。如果你用 <code>strace -c，你会发现</code>一共大约<strong>20,054,180</strong> 个<code>sigprocmask系统调用<span style="font-family: Georgia;">。但是，如果你是在</span></code><code>--disable-pthread</code> 的Ruby版本下运行，你会发现根本没有那么多的<code>sigprocmask</code> 系统调用（只有 <strong>3</strong> 次，简直就是<strong>天壤之别</strong>）</p>
<h4>查看一下源代码</h4>
<p>我们知道 <code>configure</code> 是一个脚本，其主要用来创建一个 <code>config.h</code> 文件，其中有一大堆宏定义 <code>define</code>s ，这些宏定义决定了使用什么样的函数。所以，让我们来比较一下版本 <code>./configure --enable-pthread</code> 和版本<code>./configure --disable-pthread的不同之处吧。</code></p>
<pre data-enlighter-language="shell" class="EnlighterJSRAW" data-enlighter-highlight="6,7">
$ diff config.h config.h.pthread
&gt; #define _REENTRANT 1
&gt; #define _THREAD_SAFE 1
&gt; #define HAVE_LIBPTHREAD 1
&gt; #define HAVE_NANOSLEEP 1
&gt; #define HAVE_GETCONTEXT 1
&gt; #define HAVE_SETCONTEXT 1
</pre>
<p>好的，现在我们再 <code>grep</code> 一下Ruby的源代码，我们可以看到只要<code>HAVE_[S/G]ETCONTEXT</code> 被设置了，Ruby 就会调用<code>setcontext()</code> 和<code>getcontext()</code> 这两个系统调用来存取context 的状态，以便异常处理时的切换（通过<code>EXEC_TAG）。</code></p>
<p>而如果 <code>HAVE_[S/G]ETCONTEXT</code> <strong>没有被定义</strong> <code>的情况下，</code>Ruby 会使用 <code>_setjmp/_longjmp这两个系统调用。</code></p>
<div><code>我们来看看 <code>_setjmp/_longjmp</code> 的man page:</code></div>
<blockquote><p>… The _longjmp() and _setjmp() functions shall be equivalent to longjmp() and setjmp(), respectively, with the additional restriction that _longjmp() and _setjmp() shall not manipulate the signal mask…</p></blockquote>
<p>还有<code>setcontext /getcontext的</code> man page:</p>
<blockquote><p>… uc_sigmask is the set of signals blocked in this context (see sigprocmask(2)) …</p></blockquote>
<p>我们可以看到 <code>getcontext</code> 调用每次都要调用<code>sigprocmask</code> 但是<code>_setjmp</code> 不会。</p>
<h4>补丁</h4>
<p>请点击 <strong><a href="http://github.com/ice799/matzruby/commit/0b9b69f9653782a33aee2b8937d405eae245b60c" target="_blank">这里</a></strong>获取补丁</p>
<p>这个补丁增加了一个configure 的参数 <code>--disable-ucontext</code> 其可以让你关闭使用 <code>setcontext或getcontext，你只需要像如下方式使用就好了。</code></p>
<pre data-enlighter-language="shell" class="EnlighterJSRAW">
./configure --disable-ucontext --enable-pthread
</pre>
<p>如果你以这种方式编译Ruby，那么，你的程序的性能在同等条件下可能会有30%左右的提升。</p>
<p>文章：<a href="http://timetobleed.com/fix-a-bug-in-rubys-configurein-and-get-a-30-performance-boost/" target="_blank">来源</a><!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" ><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/22242.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2022/05/etcd-150x150.png" alt="ETCD的内存问题" width="150" height="150" /></a><a href="https://coolshell.cn/articles/22242.html" class="wp_rp_title">ETCD的内存问题</a></li><li ><a href="https://coolshell.cn/articles/17381.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2016/07/PerfTest-150x150.png" alt="性能测试应该怎么做？" width="150" height="150" /></a><a href="https://coolshell.cn/articles/17381.html" class="wp_rp_title">性能测试应该怎么做？</a></li><li ><a href="https://coolshell.cn/articles/11454.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg" alt="从LongAdder看更高效的无锁实现" width="150" height="150" /></a><a href="https://coolshell.cn/articles/11454.html" class="wp_rp_title">从LongAdder看更高效的无锁实现</a></li><li ><a href="https://coolshell.cn/articles/10910.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2014/01/trade-off-150x150.jpg" alt="分布式系统的事务处理" width="150" height="150" /></a><a href="https://coolshell.cn/articles/10910.html" class="wp_rp_title">分布式系统的事务处理</a></li><li ><a href="https://coolshell.cn/articles/10337.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg" alt="数据即代码：元驱动编程" width="150" height="150" /></a><a href="https://coolshell.cn/articles/10337.html" class="wp_rp_title">数据即代码：元驱动编程</a></li><li ><a href="https://coolshell.cn/articles/9703.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2013/05/图1-3-150x150.jpg" alt="无锁HashMap的原理与实现" width="150" height="150" /></a><a href="https://coolshell.cn/articles/9703.html" class="wp_rp_title">无锁HashMap的原理与实现</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/766.html">让Ruby增加30%的性能改进</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/766.html/feed</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>免费电子书：Ruby Complete</title>
		<link>https://coolshell.cn/articles/591.html</link>
					<comments>https://coolshell.cn/articles/591.html#comments</comments>
		
		<dc:creator><![CDATA[陈皓]]></dc:creator>
		<pubDate>Mon, 20 Apr 2009 15:14:58 +0000</pubDate>
				<category><![CDATA[Ruby]]></category>
		<category><![CDATA[技术读物]]></category>
		<category><![CDATA[编程语言]]></category>
		<category><![CDATA[ebook]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=591</guid>

					<description><![CDATA[<p>这是一本免费的关于教你如何使用Ruby编程的电子书。作者：Huw Collingbourne， SapphireSteel Software 公司的Techno...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/591.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/591.html">免费电子书：Ruby Complete</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script>这是一本免费的关于教你如何使用Ruby编程的电子书。作者：Huw Collingbourne， SapphireSteel Software 公司的Technology Directory，他也是一个开发 Visual Studio下的Ruby Steel IDE的程序员。这本书给大家提供非常全面的教程，其涵养了几乎所有主要的Ruby编程的东西。</p>
<p>每一章的代码都可以被下载。如果你是一个 Ruby In Steel 的用户，那么，你可以在一个单一的Visual Studio solution 中载入这些代码，并可以在集成的 Ruby Console 上运行这些代码，并调试之。</p>
<p><span id="more-591"></span></p>
<p>下面这是这本书的一些特性：</p>
<ul>
<li>425 页。</li>
<li>20 章节。</li>
<li>超过 84,000 个词。</li>
<li>超过300 个可以运行的示例代码。</li>
<li>100% 的免费!</li>
</ul>
<div class="chapo"><!-- finde_surligneconditionnel--></div>
<p><!--intro/deck--><img decoding="async" loading="lazy" class="alignnone" title="book-of-ruby-complete" src="http://www.sapphiresteel.com/IMG/png/book-of-ruby-complete.png" alt="" width="685" height="587" /></p>
<p><a class="spip_in" href="http://www.sapphiresteel.com/IMG/zip/book-of-ruby.zip">下载这本书和其所有的源码</a> (<em>大小2.9MB </em>)<!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" ><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/10337.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg" alt="数据即代码：元驱动编程" width="150" height="150" /></a><a href="https://coolshell.cn/articles/10337.html" class="wp_rp_title">数据即代码：元驱动编程</a></li><li ><a href="https://coolshell.cn/articles/5709.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg" alt="API设计：用流畅接口构造内部DSL" width="150" height="150" /></a><a href="https://coolshell.cn/articles/5709.html" class="wp_rp_title">API设计：用流畅接口构造内部DSL</a></li><li ><a href="https://coolshell.cn/articles/5202.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg" alt="对象的消息模型" width="150" height="150" /></a><a href="https://coolshell.cn/articles/5202.html" class="wp_rp_title">对象的消息模型</a></li><li ><a href="https://coolshell.cn/articles/4710.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg" alt="Python 和 PyGame 的一些示例" width="150" height="150" /></a><a href="https://coolshell.cn/articles/4710.html" class="wp_rp_title">Python 和 PyGame 的一些示例</a></li><li ><a href="https://coolshell.cn/articles/4220.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg" alt="一些有意思的文章和资源" width="150" height="150" /></a><a href="https://coolshell.cn/articles/4220.html" class="wp_rp_title">一些有意思的文章和资源</a></li><li ><a href="https://coolshell.cn/articles/3903.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg" alt="一些有意思的贴子和工具" width="150" height="150" /></a><a href="https://coolshell.cn/articles/3903.html" class="wp_rp_title">一些有意思的贴子和工具</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/591.html">免费电子书：Ruby Complete</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/591.html/feed</wfw:commentRss>
			<slash:comments>4</slash:comments>
		
		
			</item>
		<item>
		<title>2009年脚本语言排名</title>
		<link>https://coolshell.cn/articles/325.html</link>
					<comments>https://coolshell.cn/articles/325.html#comments</comments>
		
		<dc:creator><![CDATA[陈皓]]></dc:creator>
		<pubDate>Wed, 01 Apr 2009 09:25:03 +0000</pubDate>
				<category><![CDATA[技术新闻]]></category>
		<category><![CDATA[技术读物]]></category>
		<category><![CDATA[编程语言]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Ruby]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=325</guid>

					<description><![CDATA[<p>EDC（Evan Data Corporation）发布了一份脚本语言的调查报告，这个调查报告调查了500个以上的开发者和IT专家，在这份调查表中，PHP, R...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/325.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/325.html">2009年脚本语言排名</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script><a href="https://coolshell.cn/wp-content/uploads/2009/04/overall.jpg"></a><a href="https://coolshell.cn/wp-content/uploads/2009/04/ease.jpg"></a><a href="https://coolshell.cn/wp-content/uploads/2009/04/extensibility.jpg"></a><a href="https://coolshell.cn/wp-content/uploads/2009/04/maintainability.jpg"></a><a href="https://coolshell.cn/wp-content/uploads/2009/04/availability.jpg"></a><a href="https://coolshell.cn/wp-content/uploads/2009/04/performance.jpg"></a><a href="https://coolshell.cn/wp-content/uploads/2009/04/quality.jpg"></a><a href="https://coolshell.cn/wp-content/uploads/2009/04/security.jpg"></a><a href="https://coolshell.cn/wp-content/uploads/2009/04/overall.jpg"></a><a href="https://coolshell.cn/wp-content/uploads/2009/04/overall.jpg"></a>EDC（Evan Data Corporation）发布了一份脚本语言的调查报告，这个调查报告调查了500个以上的开发者和IT专家，在这份调查表中，PHP, Ruby和Python成为了前三强。这个调查总共调查了这些脚本语言：Actionscript, Flex, Javascript, Microsoft F#, Microsoft Powershell, Perl, PHP, Python, Ruby, VB Script。主要评估以下这些方面：</p>
<li>易用性。Ease of Use <a href="https://coolshell.cn/wp-content/uploads/2009/04/overall.jpg"><img decoding="async" loading="lazy" class="alignright size-medium wp-image-330" title="overall" src="https://coolshell.cn/wp-content/uploads/2009/04/overall-300x185.jpg" alt="overall" width="300" height="185" srcset="https://coolshell.cn/wp-content/uploads/2009/04/overall-300x185.jpg 300w, https://coolshell.cn/wp-content/uploads/2009/04/overall-438x270.jpg 438w, https://coolshell.cn/wp-content/uploads/2009/04/overall.jpg 707w" sizes="(max-width: 300px) 100vw, 300px" /></a><a href="https://coolshell.cn/wp-content/uploads/2009/04/overall.jpg"></a></li>
<li>异常处理。Exception handling</li>
<li>扩展性。Extensibility</li>
<li>可维护性和易读性。Maintainability / Readability</li>
<li>跨平台。Cross-platform portability</li>
<li>社区。Community</li>
<li>实用性。Availability of tools</li>
<li>质量。Quality of tools</li>
<li>性能。Performance</li>
<li>内存管理。Memory management</li>
<li>客户端脚本。Client side scripting</li>
<li>安全性。Security</li>
<p><span id="more-325"></span></p>
<p>下面是一些关于这份调查表的图示：（关于整个报告，大家可以到这里下载：<a href="http://www.evansdata.com/reports/viewRelease_download.php?reportID=18">http://www.evansdata.com/reports/viewRelease_download.php?reportID=18</a>）</p>
<p>对于综合性的排名，报告中说到“<strong>排名向前的都是一些开源的语言，因为开源所发发展得非常快也很不错。而排名靠后的则是一些私有的，收费的语言</strong>”</p>
<p><a href="https://coolshell.cn/wp-content/uploads/2009/04/overall.jpg"><img decoding="async" loading="lazy" title="overall" src="https://coolshell.cn/wp-content/uploads/2009/04/overall.jpg" alt="overall" width="707" height="436" /></a></p>
<p> 下面是各个评估方面的排名。（我只从报告中抽取了几个方面）</p>
<p><strong>易用性</strong></p>
<p><a href="https://coolshell.cn/wp-content/uploads/2009/04/ease.jpg"><img decoding="async" loading="lazy" title="ease" src="https://coolshell.cn/wp-content/uploads/2009/04/ease.jpg" alt="ease" width="740" height="354" /></a></p>
<p><strong>扩展性</strong></p>
<p><a href="https://coolshell.cn/wp-content/uploads/2009/04/extensibility.jpg"><img decoding="async" loading="lazy" title="extensibility" src="https://coolshell.cn/wp-content/uploads/2009/04/extensibility.jpg" alt="extensibility" width="832" height="387" /></a></p>
<p> </p>
<p><strong>可维护性/易读性</strong></p>
<p><a href="https://coolshell.cn/wp-content/uploads/2009/04/maintainability.jpg"><img decoding="async" loading="lazy" title="maintainability" src="https://coolshell.cn/wp-content/uploads/2009/04/maintainability.jpg" alt="maintainability" width="840" height="377" /></a></p>
<p><strong>实用性</strong></p>
<p><a href="https://coolshell.cn/wp-content/uploads/2009/04/availability.jpg"><img decoding="async" loading="lazy" title="availability" src="https://coolshell.cn/wp-content/uploads/2009/04/availability.jpg" alt="availability" width="861" height="384" /></a></p>
<p><strong>性能</strong></p>
<p><a href="https://coolshell.cn/wp-content/uploads/2009/04/performance.jpg"><img decoding="async" loading="lazy" title="performance" src="https://coolshell.cn/wp-content/uploads/2009/04/performance.jpg" alt="performance" width="841" height="379" /></a></p>
<p> </p>
<p><strong>质量</strong></p>
<p><a href="https://coolshell.cn/wp-content/uploads/2009/04/quality.jpg"><img decoding="async" loading="lazy" title="quality" src="https://coolshell.cn/wp-content/uploads/2009/04/quality.jpg" alt="quality" width="864" height="388" /></a></p>
<p> </p>
<p><strong>安全</strong></p>
<p><a href="https://coolshell.cn/wp-content/uploads/2009/04/security.jpg"><img decoding="async" loading="lazy" title="security" src="https://coolshell.cn/wp-content/uploads/2009/04/security.jpg" alt="security" width="885" height="399" /></a><!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" ><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/1839.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2009/11/oscar-meyer-wienermobile-150x150.jpg" alt="编程语言汽车" width="150" height="150" /></a><a href="https://coolshell.cn/articles/1839.html" class="wp_rp_title">编程语言汽车</a></li><li ><a href="https://coolshell.cn/articles/1532.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg" alt="到处都是Unix的胎记" width="150" height="150" /></a><a href="https://coolshell.cn/articles/1532.html" class="wp_rp_title">到处都是Unix的胎记</a></li><li ><a href="https://coolshell.cn/articles/7886.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2012/07/muxnt-150x150.jpg" alt="代码执行的效率" width="150" height="150" /></a><a href="https://coolshell.cn/articles/7886.html" class="wp_rp_title">代码执行的效率</a></li><li ><a href="https://coolshell.cn/articles/2529.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg" alt="StackOverflow的404错误页" width="150" height="150" /></a><a href="https://coolshell.cn/articles/2529.html" class="wp_rp_title">StackOverflow的404错误页</a></li><li ><a href="https://coolshell.cn/articles/1992.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2009/12/language-fanboys-150x150.jpg" alt="程序员眼中的编程语言" width="150" height="150" /></a><a href="https://coolshell.cn/articles/1992.html" class="wp_rp_title">程序员眼中的编程语言</a></li><li ><a href="https://coolshell.cn/articles/18360.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg" alt="程序员练级攻略（2018)  与我的专栏" width="150" height="150" /></a><a href="https://coolshell.cn/articles/18360.html" class="wp_rp_title">程序员练级攻略（2018)  与我的专栏</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/325.html">2009年脚本语言排名</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/325.html/feed</wfw:commentRss>
			<slash:comments>9</slash:comments>
		
		
			</item>
	</channel>
</rss>
