<?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>Lisp | 酷 壳 - CoolShell</title>
	<atom:link href="https://coolshell.cn/tag/lisp/feed" rel="self" type="application/rss+xml" />
	<link>https://coolshell.cn</link>
	<description>享受编程和技术所带来的快乐 - Coding Your Ambition</description>
	<lastBuildDate>Fri, 09 Aug 2013 11:53:38 +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>Lisp的永恒之道</title>
		<link>https://coolshell.cn/articles/7526.html</link>
					<comments>https://coolshell.cn/articles/7526.html#comments</comments>
		
		<dc:creator><![CDATA[Todd]]></dc:creator>
		<pubDate>Mon, 04 Jun 2012 00:58:46 +0000</pubDate>
				<category><![CDATA[程序设计]]></category>
		<category><![CDATA[Lisp]]></category>
		<category><![CDATA[面向语言编程]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=7526</guid>

					<description><![CDATA[<p>【感谢 Todd投递本文 – 微博帐号：weidagang 】 Lisp之魅 长久以来，Lisp一直被许多人视为史上最非凡的编程语言。它不仅在50多年前诞生的时...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/7526.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/7526.html">Lisp的永恒之道</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>【<span style="color: #cc0000;">感谢 Todd投递本文 – 微博帐号：</span><a title="weidagang" href="http://weibo.com/weidagang" target="_blank">weidagang</a> 】</p>
<h4>Lisp之魅</h4>
<p>长久以来，Lisp一直被许多人视为史上最非凡的编程语言。它不仅在50多年前诞生的时候带来了诸多革命性的创新并极大地影响了后来编程语言的发展，即使在一大批现代语言不断涌现的今天，Lisp的诸多特性仍然未被超越。当各式各样的编程语言摆在面前，我们可以从运行效率、学习曲线、社区活跃度、厂商支持等多种不同的角度进行评判和选择，但我特别看中的一点在于语言能否有效地表达编程者的设计思想。学习C意味着学习如何用过程来表达设计思想，学习Java意味着学习如何用对象来表达设计思想，而虽然Lisp与函数式编程有很大的关系，但学习Lisp绝不仅仅是学习如何用函数表达设计思想。实际上，<strong>函数式编程并非Lisp的本质</strong>，在已经掌握了lambda、高阶函数、闭包、惰性求值等函数式编程概念之后，学习Lisp仍然大大加深了我对编程的理解。<strong>学习Lisp所收获的是如何“自由地”表达你的思想</strong>，这正是Lisp最大的魅力所在，也是这门古老的语言仍然具有很强的生命力的根本原因。</p>
<h4>Lisp之源</h4>
<p>Lisp意为表处理(List Processing)，源自设计者John McCarthy于1960年发表的一篇论文《符号表达式的递归函数及其机器计算》。McCarthy在这篇论文中向我们展示了用一种简单的数据结构S表达式(S-expression)来表示代码和数据，并在此基础上构建一种完整的语言。Lisp语言形式简单、内涵深刻，Paul Graham在《Lisp之根源》中将其对编程的贡献与欧几里德对几何的贡献相提并论。</p>
<h4>Lisp之形</h4>
<p>然而，与数学世界中简单易懂的欧氏几何形成鲜明对比，程序世界中的Lisp却一直是一种古老而又神秘的存在，真正理解其精妙的人还是少数。从表面上看，Lisp最明显的特征是它“古怪”的S表达式语法。S表达式是一个原子(atom)，或者若干S表达式组成的列表(list)，表达式之间用空格分开，放入一对括号中。“列表“这个术语可能会容易让人联想到数据结构中的链表之类的线形结构，实际上，Lisp的列表是一种可嵌套的树形结构。下面是一些S表达式的例子:</p>
<pre data-enlighter-language="c" class="EnlighterJSRAW">
foo

()

(a b (c d) e)

(+ (* 2 3) 5)

(defun factorial (N)
    (if (= N 1)
        1
        (* N (factorial (- N 1)))
    )
)
</pre>
<p><span id="more-7526"></span></p>
<p>据说，这个古怪的S表达式是McCarthy在发明Lisp时候所采用的一种临时语法，他实际上是准备为Lisp加上一种被称为M表达式(M-expression)的语法，然后再把M表达式编译为S表达式。用一个通俗的类比，S表达式相当于是JVM的字节码，而M表达式相当于Java语言，但是后来Lisp的使用者都熟悉并喜欢上了直接用S表达式编写程序，并且他们发现S表达式有许多独特的优点，所以M表达式的引入也就被无限期延迟了。</p>
<p>许多Lisp的入门文章都比较强调Lisp的函数式特性，而我认为这是一种误导。真正的Lisp之门不在函数式编程，而在S表达式本身，Lisp最大的奥秘就藏在S表达式后面。S表达式是Lisp的语法基础，语法是语义的载体，形式是实质的寄托。<strong>“S表达式”是程序的一种形，正如“七言”是诗的一种形，“微博”是信息的一种形</strong>。正是形的不同，让微博与博客有了质的差异，同样的道理，正是S表达式让Lisp与C、Java、SQL等语言有了天壤之别。</p>
<h4>Lisp之道</h4>
<p>一门语言能否有效地表达编程者的设计思想取决于其抽象机制的语义表达能力。根据抽象机制的不同，语言的抽象机制形成了面向过程、面向对象、函数式、并发式等不同的范式。当你采用某一种语言，基本上就表示你已经“面向XXX“了，你的思维方式和解决问题的手段就会依赖于语言所提供的抽象方式。比如，采用Java语言通常意味着采用面向对象分析设计；采用Erlang通常意味着按Actor模型对并发任务进行建模。</p>
<p>有经验的程序员都知道，无论是面向XXX编程，程序设计都有一条“抽象原则“：What与How解耦。但是，<strong>普通语言的问题就在于表达What的手段非常有限</strong>，无非是过程、类、接口、函数等几种方式，而诸多领域问题是无法直接抽象为函数或接口的。比如，你完全可以在C语言中定义若干函数来做到make file所做的事情，但C代码很难像make file那样声明式地体现出target、depends等语义，它们只会作为实现细节被淹没在一个个的C函数之中。采用OOP或是FP等其它范式也会遇到同样的困难，也就是说make file语言所代表的抽象维度与面向过程、OOP以及FP的抽象维度是正交的，使得各种范式无法直接表达出make file的语义。这就是普通语言的“刚性”特征，它要求我们必须以语言的抽象维度去分析和解决问题，把问题映射到语言的基本语法和语义。</p>
<p>更进一步，如果仔细探究这种刚性的根源，我们会发现正是由于普通语言<strong>语法和语义的紧耦合</strong>造成了这种刚性。比如，C语言中printf(&#8220;hello %s&#8221;, name)符合函数调用语法，它表达了函数调用语义，除此之外别无他义；Java中interface IRunnable { &#8230; }符合接口定义语法，它表达了接口定义语义，除此之外别无他义。如果你认为“语法和语义紧耦合“是理所当然的，看不出这有什么问题，那么理解Lisp就会让你对此产生更深的认识。</p>
<p>当你看到Lisp的(f a (b c))的时候，你会想到什么？会不会马上联想到函数求值或是宏扩展？就像在C语言里看到gcd(10, 15)马上想到函数调用，或者在Java里看到class A马上想到类定义一样。如果真是这样，那它就是你理解Lisp的一道障碍，因为你已经习惯了顺着语言去思考，总是在想这一句话机器怎么解释执行？那一句话又对应语言的哪个特性？理解Lisp要反过来，让语言顺着你，Lisp的(f a (b c))可以是任何语义，完全由你来定，它可以是函数定义、类定义、数据库查询、文件依赖关系，异步任务的执行关系，业务规则 &#8230;</p>
<p>下面我准备先通过几个具体的例子逐步展示Lisp的本质。需要说明的是，由于Lisp的S表达式和XML的语法形式都是一种树形结构，在语义表达方面二者并无本质的差别。所以，为了理解方便，下面我暂且用多数人更为熟悉的XML来写代码，请记住我们可以很轻易地把XML代码和Lisp代码相互转换。</p>
<p>首先，我们可以轻易地用XML来定义一个求两个数最大公约数的函数：</p>
<pre data-enlighter-language="xml" class="EnlighterJSRAW">
    &lt;func name=&#039;gcd&#039; return_type=&#039;int&#039;&gt;
        &lt;params&gt;
            &lt;a type=&#039;int&#039;/&gt;
            &lt;b type=&#039;int&#039;/&gt;
        &lt;/params&gt;
        &lt;body&gt;
            &lt;if&gt;
               &lt;equals&gt;
                   &lt;a/&gt;
                   &lt;int&gt;0&lt;/int&gt;
               &lt;/equals&gt;
            &lt;/if&gt;
            &lt;then&gt;
                &lt;return&gt;&lt;b/&gt;&lt;/return&gt;
            &lt;/then&gt;
            &lt;else&gt;
                &lt;return&gt;
                    &lt;gcd&gt;
                        &lt;modulo&gt;&lt;b/&gt;&lt;a/&gt;&lt;/modulo&gt;
                        &lt;a/&gt;
                    &lt;/gcd&gt;
                &lt;/return&gt;
            &lt;/else&gt;
        &lt;/body&gt;
    &lt;/func&gt;
</pre>
<p>其次，我们可以用它来定义类：</p>
<pre data-enlighter-language="xml" class="EnlighterJSRAW">
    &lt;class name=&quot;Computer&quot;&gt;
        &lt;field access=&quot;private&quot; type=&quot;MainBoard&quot; name=&quot;main-board&quot; /&gt;
        &lt;field access=&quot;private&quot; type=&quot;CPU&quot; name=&quot;cpu&quot; /&gt;
        &lt;field access=&quot;private&quot; type=&quot;Memory&quot; name=&quot;memory&quot; /&gt;

        &lt;method access=&quot;public&quot; return_type=&quot;boolean&quot; name=&quot;powerOn&quot; /&gt;
            &lt;params&gt;...&lt;/params&gt;
            &lt;body&gt;...&lt;/body&gt;
        &lt;/method&gt;

        &lt;method access=&quot;public&quot; return_type=&quot;boolean&quot; name=&quot;powerOff&quot; /&gt;
            &lt;params&gt;...&lt;/params&gt;
            &lt;body&gt;...&lt;/body&gt;
        &lt;/method&gt;
    &lt;/class&gt;
</pre>
<p>还可以轻易地用它来编写关系查询：</p>
<pre data-enlighter-language="xml" class="EnlighterJSRAW">
&lt;sql&gt;
    &lt;select&gt;
        &lt;column name=&quot;employees.id&quot; /&gt;
        &lt;column name=&quot;bonus.amount&quot; /&gt;
    &lt;/select&gt;
    &lt;from&gt;
        &lt;table name=&quot;employees&quot; /&gt;
        &lt;table name=&quot;bonus&quot; /&gt;
    &lt;/from&gt;
    &lt;where&gt;
        &lt;equals&gt;
            &lt;column name=&quot;employees.id&quot; /&gt;
            &lt;column name=&quot;bonus.employee_id&quot; /&gt;
        &lt;/equals&gt;
    &lt;/where&gt;
&lt;/sql&gt;
</pre>
<p>还可以用它来实现类似make file的自动化构建(语法取自ant)：</p>
<pre data-enlighter-language="xml" class="EnlighterJSRAW">
    &lt;project name=&quot;MyProject&quot; default=&quot;dist&quot; basedir=&quot;.&quot;&gt;
        &lt;property name=&quot;src&quot; location=&quot;src&quot;/&gt;
        &lt;property name=&quot;build&quot; location=&quot;build&quot;/&gt;
        &lt;property name=&quot;dist&quot;  location=&quot;dist&quot;/&gt;

        &lt;target name=&quot;init&quot;&gt;
            &lt;mkdir dir=&quot;${build}&quot;/&gt;
        &lt;/target&gt;

        &lt;target name=&quot;compile&quot; depends=&quot;init&quot; description=&quot;compile the source &quot; &gt;
            &lt;javac srcdir=&quot;${src}&quot; destdir=&quot;${build}&quot;/&gt;
        &lt;/target&gt;

        &lt;target name=&quot;dist&quot; depends=&quot;compile&quot; description=&quot;generate the distribution&quot; &gt;
            &lt;mkdir dir=&quot;${dist}/lib&quot;/&gt;
            &lt;jar jarfile=&quot;${dist}/lib/MyProject-${DSTAMP}.jar&quot; basedir=&quot;${build}&quot;/&gt;
        &lt;/target&gt;

        &lt;target name=&quot;clean&quot; description=&quot;clean up&quot; &gt;
            &lt;delete dir=&quot;${build}&quot;/&gt;
            &lt;delete dir=&quot;${dist}&quot;/&gt;
        &lt;/target&gt;
    &lt;/project&gt;
</pre>
<p>一口气举了这么多个例子，目的在于用XML这种树形结构来说明Lisp的S表达式所能够描述的语义。不知道你是否发现了S表达式和XML这种树形语法在语义构造方面有着特别的“柔性”？我们可以轻易地用它构造出函数、变量、条件判断语义；类、属性、方法语义；可以轻易地构造出关系模型的select、where语义；可以轻易地构造出make的target、depends语义，等等数不清的语义。在普通语言里，你可以定义一个函数、一个类，但你无法为C语言增加匿名函数特性，也没法给Java语言加上RAII语义，甚至连自己创造一个foreach循环都不行，而自定义语义意味着在Lisp之上<strong>你创造了一门语言</strong>！不管是面向过程，面向对象，函数式，还是关系模型，在Lisp里统统都变成了一种DSL，而Lisp本身也就成了一种定义语言的语言，即元语言(Meta Language)。</p>
<p>Lisp的柔性与S表达式有着密切的关系。Lisp并不限制你用S表达式来表达什么语义，同样的S表达式语法可以表达各种不同领域的语义，这就是<strong>语法和语义解耦</strong>。如果说普通语言的刚性源于“语法和语义紧耦合”，那么Lisp的柔性正是源于“语法和语义解耦”！“语法和语义解耦”使得Lisp可以随意地构造各种领域的DSL，而不强制用某一种范式或是领域视角去分析和解决问题。本质上，Lisp编程是一种超越了普通编程范式的范式，这就是<strong>Lisp之道：面向语言编程(LOP, Language Oriented Programming)</strong>。Wikipedia上是这样描述LOP的：</p>
<blockquote><p>Language oriented programming (LOP) is a style of computer programming in which, rather than solving problems in general-purpose programming languages, the programmer creates one or more domain-specific languages for the problem first, and solves the problem in those languages &#8230; The concept of Language Oriented Programming takes the approach to capture requirements in the user&#8217;s terms, and then to try to create an implementation language as isomorphic as possible to the user&#8217;s descriptions, so that the mapping between requirements and implementation is as direct as possible.</p></blockquote>
<p>LOP范式的基本思想是从问题出发，先创建一门描述领域模型的DSL，再用DSL去解决问题，它具有高度的声明性和抽象性。SQL、make file、CSS等DSL都可以被认为是LOP的具体实例，下面我们再通过两个常见的例子来理解LOP的优势。</p>
<p>例1：在股票交易系统中，交易协议定义若干二进制的消息格式，交易所和客户端需要对消息进行编码和解码。</p>
<p>消息格式是一种抽象的规范，本身不对语言做任何的限制，你可以用C，C++，Java，或者Python。普通的实现方式是按照消息格式规范，在相应的语言中定义消息结构，并编写相应的编解码函数。假设为一个消息定义结构和实现编解码函数的工作量为M，不同消息类型的数量为N，这种方式的工作量大致为M*N。也就是说每增加一种消息类型，就需要为该消息定义结构，实现编解码函数，引入bug的可能性当然也和M*N成正比。如果仔细观察不难发现，各个消息结构其实是高度类似的，编解码函数也大同小异，但是普通语言却找不到一种抽象机制能表达这种共性，比如，我们无法通过面向对象的方法定义一个基类把消息结构的共性抽象出来，然后让具体的消息去继承它，达到复用的目的。这正是由于普通语言的抽象维度限制所致，在普通语言中，你只能从函数、接口等维度对事物进行抽象，而恰好消息格式共性所在的维度与这些抽象维度并不匹配。</p>
<p>其实，不同消息类型的<strong>共性在于它们都具有相同的领域语义</strong>，比如：“某字段内容是另一个字段内容的md5码”就是一种消息格式的领域语义，这种领域语义是OOP的抽象机制无法描述的。LOP的思路是先创建一门消息定义DSL，比如，类似Google的Protocol Buffer，Android的AIDL。然后，通过DSL编写消息定义文件，直接声明式地描述消息的结构特征，比如，我们可以声明式地描述“某字段内容是另一个字段内容的md5码”。我们还需要为DSL开发编译器用于生成C、Java等通用语言的消息定义和编解码函数。</p>
<p>有了消息定义DSL和编译器之后，由于DSL编写消息定义是一种高度声明式的编程方法，每增加一种消息的只需要多编写一个消息定义文件而已，工作量几乎可以忽略不计。所有的工作量都集中在编译器的开发上，工作量是一个常数C，与消息的数量没有关系；质量保证方面也只需要关注编译器这一点，不会因为增加新的消息类型而引入bug。</p>
<p>例2：在图书管理系统中，需要支持在管理界面上对书籍、学生、班级等各种实体进行管理操作。</p>
<p>如果按传统的三层架构，一般需要在后端程序中为每一种实体定义一个类，并定义相应的方法实现CRUD操作，与之相应的，还需要在前端页面中为每一个实体编写相应的管理页面。这些实体类的CRUD操作都是大同小异的，但细节又各不相同，虽然我们很想复用某些共同的设计实现，但OOP所提供的封装、继承、多态等抽象机制不足以有效捕获实体之间的共性，大量的代码还是必须放在子类中来完成。比如，Student和Book实体类的实现非常相似，但是如果要通过OOP的方式去抽象它们的共性，得出的结果多半是Entity这样的大而空的基类，很难起到复用的效果。</p>
<p>其实，不同实体之间的共性还是在于它们具有相同的领域语义，比如：实体具有属性，属性具有类型，属性具有取值范围，属性具有可读取、可编辑等访问属性，实体之间有关联关系等。LOP方法正是直接面向这种领域语义的。采用LOP方法，我们并不需要为每一个实体类单独编写CRUD方法，也不需要单独编写管理页面，只需要定义一种DSL并实现其编译器；然后，用DSL声明式地编写实体描述文件，去描述实体的属性列表，属性的类型、取值范围，属性所支持的操作，属性之间的关系和约束条件等；最后，通过这个实体描述文件自动生成后端的实体类和前端管理页面。采用LOP，不论前后端采用何种技术，Java也好，C#也好，JSP也好，ASP.NET也好，都可以自动生成它们的代码。采用LOP的工作量和质量都集中在DSL的设计和编译器的开发，与实体的数量无关，也就是说，越是庞大的系统，实体类越多越是能体现LOP的优势。</p>
<p>通过上面两个小例子我们可以感受到，LOP是一种面向领域的，高度声明式的编程方式，它的抽象维度与领域模型的维度完全一致。LOP能让程序员从复杂的实现细节中解脱出来，把关注点集中在问题的本质上，从而提高编程的效率和质量。</p>
<p>接下来的问题是如果需要为某领域设计DSL，我们是应该发明一门类似SQL这样的专用DSL呢，还是用XML或S表达式去定义DSL呢？它们各有何优缺点呢？</p>
<p>我认为采用XML或S表达式定义DSL的优点主要有：1) SQL、make file、CSS等专用DSL都只能面向各自的领域，而一个实际的领域问题通常是跨越多个领域的，有时我们需要将不同领域融合在一起，但是由于普通语言的刚性，多语言融合通常会是一件非常困难的事情，而XML和S表达式语法结构的单一性和“代码及数据”的特点使得跨领域融合毫无障碍。2) 在为DSL开发编译器或解释器的方面，二者难度不同。对XML和S表达式定义的DSL进行语法分析非常简单，相比之下，对SQL这样的专用DSL进行语法分析，虽然可以借助Lex、Yacc、ANTLR等代码生成工具，但总的来讲复杂度还是要明显高一些。</p>
<p>当然，XML和S表达式的优点也正好是其缺点，由于XML和S表达式的语法形式是固定的，不能像专用DSL那样自由地设计语法。所以，一般来讲专用DSL的语法显得更加简洁。换句话说，XML和Lisp其实是在语法和语义间做了一个交换，用语法的限制换来了语义的灵活。</p>
<h4>Lisp之器</h4>
<p>接下来我们继续探讨DSL的解释执行问题。DSL代码的解释执行一般分为3种典型的方式：1) 通过专门的解释器解释执行；2) 编译生成其他语言的代码，再通过其他语言的解释器解释执行(或编译运行)；3) 自解释。比如，第1类的代表是SQL，上一节举的两个例子都属于第2类，而第3类自解释正是Lisp的特色。</p>
<p>为了理解自解释，我们可以先从内部DSL的解释执行说起。内部DSL是指嵌入在宿主语言中的DSL，比如，Google Test单元测试框架定义了一套基于流畅接口(Fluent Interface)的C++单元测试DSL。从语义构造的角度看，内部DSL直接借用宿主语言的语法定义了自己的领域语义，是一种语法和语义解耦；从解释执行的角度看，内部DSL是随宿主语言的解释器而自动解释的，不需要像外部DSL一样开发专门的解释器，因而实现的代价很低。当然，并不是说设计内部DSL不用关心任何的解释实现，实际上，还是需要熟悉宿主语言的特性，并利用该特性使得DSL能随着宿主语言的解释器得到解释执行。</p>
<p>Lisp拥有强大的自解释特性，这得益于独一无二的<strong>Lisp之器：宏 (macro)</strong>。宏使得Lisp编写的DSL可以被Lisp解释器直接解释执行，这在原理上与内部DSL是相通的，只是内部DSL一般是利用宿主语言的链式调用等特性，通常形式简陋，功能有限，而Lisp的宏则要强大和灵活得多。</p>
<p>C语言中也有宏的概念，不过Lisp的宏与C语言的宏完全不同，C语言的宏是简单的字符串替换。比如，下面的宏定义：</p>
<pre data-enlighter-language="c" class="EnlighterJSRAW">
#define square(x) (x*x)
</pre>
<p>square(1+1)的期望结果是4，而实际上它会被替换成(1+1*1+1)，结果是3。这个例子说明，C语言的宏只在预编译阶段进行简单的字符串替换，对程序语法结构缺乏理解，非常脆弱。Lisp的宏不是简单的字符串替换，而是一套完整的代码生成系统，它是在语法解析的基础上把Lisp代码从一种形式转换为另一种形式，本质上起到了普通语言编译器的作用。不同的是，普通编译器是把一种语言的代码转换为另一种语言的代码，比如，Java编译器把Java代码转换成Java字节码；而Lisp宏的输入和输出都是S表达式，它本质上是把一种DSL转换为另一种DSL。下面的例子是宏的一个典型用法。</p>
<p>例3：假设Lisp解释器已经具备解释执行面向过程DSL的能力，需要实现类似ant的自动化构建工具。</p>
<p>我们可以基于宏构建一门类ant的DSL，宏的作用是把类ant DSL通过宏展开变成面向过程的DSL，最后被Lisp解释器所解释执行。这样用Lisp编写的ant DSL就不需要被编译为其他语言，也不需要像XML的ant一样依赖于专门的解释器了。</p>
<p>当然，和开发专门的解释器/编译器相比，Lisp的宏也并非没有缺点，宏难以理解，开发和调试更加困难。到底是开发专门的解释器/编译器还是直接采用宏应该视具体情况而定。</p>
<h4>总结</h4>
<p>Lisp采用单一的S表达式语法表达不同的语义，实现了语法和语义解耦。这使得Lisp具有强大的语义构造能力，擅长于构造DSL实现面向语言编程，而宏使得Lisp具有自解释能力，让不同DSL之间的转换游刃有余。进入Lisp的世界应当从理解面向语言编程入门，这是Lisp之道，而函数式编程和宏皆为Lisp之器，以道驭器方为正途。</p>
<h4>后记</h4>
<p>本文是我学习Lisp的一个总结，也是写给有兴趣学习Lisp的程序员的入门资料。必须说明，我还是一个标准的Lisp初学者，几乎没有写过像样的Lisp程序，文中的错误和不足在所难免，希望读者批评指正，感谢！</p>
<h4>参考</h4>
<p><a href="http://www.paulgraham.com/rootsoflisp.html">The Roots of Lisp</a></p>
<p><a href="http://www.defmacro.org/ramblings/lisp.html">The Nature of Lisp</a></p>
<p><a href="http://lists.warhead.org.uk/pipermail/iwe/2005-July/000130.html">Why Lisp macros are cool, a Perl perspective</a></p>
<p><a href="http://en.wikipedia.org/wiki/Language-oriented_programming">Wikipedia: Language-oriented programming</a></p>
<p><a href="http://book.douban.com/subject/6859720/">《实用Common Lisp编程》</a></p>
<p><a href="http://book.douban.com/subject/4031906/">《冒号课堂 &#8211; 编程范式与OOP思想》</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/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/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/1202.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2009/07/Question-150x150.jpg" alt="面试题：赛马问题" width="150" height="150" /></a><a href="https://coolshell.cn/articles/1202.html" class="wp_rp_title">面试题：赛马问题</a></li><li ><a href="https://coolshell.cn/articles/10804.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2013/12/x-y.problem-150x150.jpg" alt="X-Y Problem" width="150" height="150" /></a><a href="https://coolshell.cn/articles/10804.html" class="wp_rp_title">X-Y Problem</a></li><li ><a href="https://coolshell.cn/articles/3766.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2011/02/feedback_cycle-150x150.jpg" alt="[转]TDD到底美还是不美？" width="150" height="150" /></a><a href="https://coolshell.cn/articles/3766.html" class="wp_rp_title">[转]TDD到底美还是不美？</a></li><li ><a href="https://coolshell.cn/articles/562.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2009/04/13-09_menu_menu-150x150.jpg" alt="30种时尚的CSS网站导航条" width="150" height="150" /></a><a href="https://coolshell.cn/articles/562.html" class="wp_rp_title">30种时尚的CSS网站导航条</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/7526.html">Lisp的永恒之道</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/7526.html/feed</wfw:commentRss>
			<slash:comments>93</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>
	</channel>
</rss>
