<?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>Programming | 酷 壳 - CoolShell</title>
	<atom:link href="https://coolshell.cn/tag/programming/feed" rel="self" type="application/rss+xml" />
	<link>https://coolshell.cn</link>
	<description>享受编程和技术所带来的快乐 - Coding Your Ambition</description>
	<lastBuildDate>Sat, 26 Dec 2020 08:20:49 +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>50年前的登月程序和程序员有多硬核</title>
		<link>https://coolshell.cn/articles/19612.html</link>
					<comments>https://coolshell.cn/articles/19612.html#comments</comments>
		
		<dc:creator><![CDATA[陈皓]]></dc:creator>
		<pubDate>Sun, 21 Jul 2019 11:00:30 +0000</pubDate>
				<category><![CDATA[业界新闻]]></category>
		<category><![CDATA[技术读物]]></category>
		<category><![CDATA[杂项资源]]></category>
		<category><![CDATA[Programmer]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[程序员]]></category>
		<guid isPermaLink="false">https://coolshell.cn/?p=19612</guid>

					<description><![CDATA[<p>2019年7月20日，是有纪念意义的一天，这天不是因为广大网民帮周杰伦在新浪微博上的超话刷到第一，而是阿波罗登月的50周年的纪念日。早在几年前，在Github上...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/19612.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/19612.html">50年前的登月程序和程序员有多硬核</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>2019年7月20日，是有纪念意义的一天，这天不是因为广大网民帮周杰伦在新浪微博上的超话刷到第一，而是阿波罗登月的50周年的纪念日。早在几年前，在Github上放出了当年Apollo飞船使用的源代码（当然是汇编的），但完全不明白为什么这几天会有一些中国的小朋友到这个github的issue里灌水……，人类历史上这么伟大的一件事，为什么不借这个机会学习一下呢？下面是一些阿波罗登月与程序员相关的小故事，顺着这些东西，你可以把你的周末和精力用得更有价值。</p>
<p><img decoding="async" loading="lazy" class=" size-full alignright" src="https://coolshell.cn/wp-content/uploads/2019/07/1920px-Margaret_Hamilton_-_restoration-e1563697198766.jpg" alt="" width="400" height="498" /></p>
<p>首先，要说的是Apollo 11导航的源代码，这些代码的设计负责人叫<a href="https://en.wikipedia.org/wiki/Margaret_Hamilton_(software_engineer)" target="_blank" rel="noopener noreferrer">Margaret Heafield Hamilton </a>，是一个女程序员，专业是数学和哲学，1960年得到一个MIT麻省理工大学的临时的软件开发职位，负责在PDP-1和LGP-30上运行天气预报的软件（注：在计算机历史上，PDP系统机器被称为Hack文化的重要推手，PDP-11推了Unix操作系统，而Unix操作系统则是黑客文化的重要产品。参看《<a href="https://coolshell.cn/articles/2322.html" target="_blank" rel="noopener noreferrer">Unix传奇</a>》）。然后，她又为美国空军编写探测知敌方飞行的软件，之后，于1965年的时候，她加入了MIT仪器实验室，并成为了这个实验室的主管，这个实验实就是Apollo计划的一部分，她负责编写全新的月球登录的导航软件，以及后来该软件在其他项目中的各个版本。</p>
<p><span id="more-19612"></span></p>
<p>上图是Hamilton站在她和她的麻省理工团队为阿波罗项目制作的导航软件源代码旁边，在Github上的开源的代码 &#8211; <a href="https://github.com/chrislgarry/Apollo-11" target="_blank" rel="noopener noreferrer">Apollo-11</a> （2016年开源）。我们可以看到，有两个重要的目录，一个目录叫“Comanche055”，一个目录叫“Luminary099”，前者是指挥舱用的（英文为 <a href="https://en.wikipedia.org/wiki/Apollo_command_and_service_module#Command_Module_(CM)" target="_blank" rel="noopener noreferrer">Command Module</a> ）后者为登月舱用的（英文为 <a href="https://en.wikipedia.org/wiki/Apollo_Lunar_Module" target="_blank" rel="noopener noreferrer">Lunar Module</a>），这里需要说明一下的是，指挥舱是把登录舱推到月球上，在返回的时候，登录舱是被抛弃掉的，而返回到地球的是指挥舱。如果你想看这两份源代码的纸版，你可以访问这两个链接：<a href="https://archive.org/details/Comanche55J2k60" target="_blank" rel="noopener noreferrer">Comanche 55 AGC Program Listing</a> 和 <a href="https://archive.org/details/Luminary99001J2k60" target="_blank" rel="noopener noreferrer">Luminary 99 REv.1 AGC Program Listing</a>。其中的55 和 90 是各自的build 版本号。</p>
<p>我们细看一下，这些文件的日期是，1969年7月14日，而Apollo 11登月的日期是1969年7月16日起程，7月19日经过月球背面，7月20日下午8点登月。代码写好，两天后就直接上生产，然后就登月，还是导航代码，这代码写的的健壮性得有多强。</p>
<p>如果你仔细比较一下这两个目录中的文件，你会发现有些文件是一样的，不但文件名一样，而且内容也一样。这说明这两个模块中的一些东西是相似的。</p>
<p><img decoding="async" loading="lazy" class="size-full aligncenter" src="https://coolshell.cn/wp-content/uploads/2019/07/source.code_.compare.png" alt="" width="800" height="402" /></p>
<p>这些代码应该是很难读了，因为当时写这些代码的时候，C语言都没有被发明，所以基本上来说都是汇编代码了，而且还可以发现，这些代码的源文件全是以agc后缀结尾的， 看来这还不是我们平时所了解的汇编，所谓的AGC代表了运行这些代码的计算机 &#8211;<a href="https://en.wikipedia.org/wiki/Apollo_Guidance_Computer" target="_blank" rel="noopener noreferrer"> Apollo Guideance Computer</a> 。沿着这个Wikipedia的链接，你可以看到AGC这个电脑的指令是什么样的，看懂那几条指令后，这些源代码也就能读懂了。当然，因为是写成汇编的，所以，读起来还是要费点神的。不过，其中有一个文件是 <code><a href="https://github.com/chrislgarry/Apollo-11/blob/master/Luminary099/LUNAR_LANDING_GUIDANCE_EQUATIONS.agc" target="_blank" rel="noopener noreferrer">LUNAR_LANDING_GUIDANCE_EQUATIONS.agc</a></code> 你会不会很好奇想去看看？</p>
<p>打开源文件，你还可以看到每个文件都有很多很多的注释，非常友好，但是也有一些注释比较有趣。这里有一组短视频带你读这些代码 &#8211; <a href="https://www.pluralsight.com/courses/moon-landing-apollo-11" target="_blank" rel="noopener noreferrer">Exploring the Apollo Guidance Computer(AGC) Code</a>，一供10个小视频，每个2分钟左右，如果你英文听边还行（我觉得很容易听懂），可以看看，了解一下AGC的工作方式，挺有趣意思的。</p>
<p>当时的AGC有32公斤，主频只有2MHz，2K的RAM，36K的ROM。嗯，当年就是这么一个小玩意，把人送上了月球，今天，一个聊天程序就占内存几GB……</p>
<p>下面是AGC在Apollo 1指挥舱里的样子（图片截自上面的视频），这个高质量的3D扫描来自<a href="https://www.3d.si.edu/explorer/apollo-11-command-module" target="_blank" rel="noopener noreferrer"> Simithsonian 3D: Apollo 11 Command Module</a> （我觉得美国人干这些事干就是很漂亮啊，这种高清的3D扫描太牛了，如果你仔细看，这个舱里还有宇航员在舱壁上的手写）</p>
<p><img decoding="async" loading="lazy" class="aligncenter size-full" src="https://coolshell.cn/wp-content/uploads/2019/07/AGC.DSKY_.png" alt="" width="800" height="402" /></p>
<p>这个AGC的操作界面又叫DSKY &#8211; Display 和 Keyboard的缩写，下图是一个 AGC 模拟器，其官方主页在 <a href="https://www.ibiblio.org/apollo/" target="_blank" rel="noopener noreferrer">https://www.ibiblio.org/apollo/</a>源代码在 <a href="https://github.com/virtualagc/virtualagc" target="_blank" rel="noopener noreferrer">Github/VirtualAGC</a>。在这个界面上我们可以看到：下面的键盘上左边有两个键，一个是动词Verb一个是名词Noun，Verb指定操作类型，Noun指定要由Verb命令修改的数据。右边的显示器下面有三个5位的数字，这三个数值显示表示航天器姿态的矢量，以及所需速度变化的显示矢量。是的，当年的导航就靠这三个数字和里面的程序了。</p>
<p><img decoding="async" loading="lazy" class="aligncenter size-full" src="https://coolshell.cn/wp-content/uploads/2019/07/DSKY.png" alt="" width="588" height="669" /></p>
<p>&nbsp;</p>
<p>如果你想了解AGC更多的细节，你可以看看 这篇 <a href="http://www.ibiblio.org/apollo/ForDummies.html" target="_blank" rel="noopener noreferrer">AGC for Dummies</a>。这篇文章讲述了AGC这个嵌入式系统的背景和操作指令。一份详细的<a href="http://www.ibiblio.org/apollo/assembly_language_manual.html" target="_blank" rel="noopener noreferrer">AGC 汇编语言手册</a>可以让你了解更多的细节。</p>
<p>另外，我在Youtube上找到了一个讲当时Apollo电脑的纪录片 &#8211; <a href="https://www.youtube.com/watch?v=9YA7X5we8ng" target="_blank" rel="noopener noreferrer">Navigation Computer</a>，太有趣了。比如：21分51秒开始讲存储用的 <a href="https://en.wikipedia.org/wiki/Core_rope_memory" target="_blank" rel="noopener noreferrer">Rope Memory</a> 绕线内存，Hamilton 也出来讲了一下在这种内存上编程，画面切到一个人用个比较长的金属针在一个像主板一样的东西上，左右穿梭，就像刺绣一样，但是绣的不是图案，而是程序……太硬核了，真正的通过“硬编织”的方式来写程序。</p>
<p><a href="https://www.youtube.com/watch?v=9YA7X5we8ng" target="_blank" rel="noopener noreferrer"><img decoding="async" loading="lazy" class="aligncenter size-full" src="https://coolshell.cn/wp-content/uploads/2019/07/rope.memory.png" alt="" width="800" height="497" /></a></p>
<p>看完上面这个纪录篇，我是非常之惊叹，惊叹于50年前的工程能力，惊叹于50年前这些人面对技术的的一丝不苟，对技术的尊重和严谨的这种精神和方法，一点都不比较今天差。</p>
<p>不过，最牛的还不是这个，我在Hamilton的Wikipedia词条上找到了他说的一个事件—— 当年Apollo登陆雷达开关放在了错误的位置，导致AGC收到了不少错误的信号。结果就是AGC既得执行着陆必须的计算，又要接受这些占用其15%时间的额外数据。但是AGC的程序居然可以用高优先级的任务打断低优先级的任务，于是，AGC自动剔除了低级别的任务以保证了重要的任务完成。Hamilton 原话说—— 如果当时的程序不能识别错误并从错误中恢复，我怀疑阿波罗不能成功登月。if the computer hadn&#8217;t recognized this problem and taken recovery action, I doubt if Apollo 11 would have been the successful moon landing it was。</p>
<p>看到这里，你有没有觉得——“这个女程序员的一小步，是整个人类的一大步”？</p>
<p>Hamilton 的牛逼之外还在于，她是第一个将“软件工程”提出来的人，在MIT，她想让软件开发就像其它工程一样，有相应的工程纪律，给于相关的尊重，于是她创造了Software Engineering这个词。2018年，<a href="https://www.computer.org/csdl/magazine/so/2018/05" target="_blank" rel="noopener noreferrer">IEEE在纪念软件工程50周年</a>的时候，他们把 Hamilton 请过去讲了一个叫 <a href="https://ieeexplore.ieee.org/document/8409915" target="_blank" rel="noopener noreferrer">What the Errors Tell Us</a> 的主题。她绝对可以称得上是程序员的Pioneer。</p>
<p>三年前，Apollo的源代码被开源时候，Twitter有个叫 Lin Clark 的人发了一条推：“我妈50年前的代码被放到Github上了”，虽然，她不是 Hamilton 的女儿，但她妈妈也是Apollo其中一个程序员，现在Lin Clark同样也是一个程序员，目前在 Mozilla工作，Staff Engineer，专长 <span class="lt-line-clamp__line">WebAssembly, Rust, 和 JavaScript</span> ，也是个非常厉害的程序，Youtube上各种演讲，也是一个跟他妈妈一样牛的人。</p>
<p>当她在Twitter上这么自豪地发了一条这样的推后，我不知道各位有什么想法？想不想你的后代在未来也会这样自豪的发条微博？<br />
<img decoding="async" loading="lazy" class="aligncenter size-full" src="https://coolshell.cn/wp-content/uploads/2019/07/Lin-Clark-e1563706128853.jpg" alt="" width="400" height="629" /></p>
<p>&nbsp;</p>
<p>最后，尤其是想对那些到Apollo源代码的issue里发spam垃圾信息的人说一下，你看看人家，再看看你们自己，你们是不是想让你们的孩子在登月100周年纪念的时候说——50年前我爹那个傻叉在Apollo的github的issue列表时写了些垃圾，还以为自己多机灵？！</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/12052.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg" alt="Leetcode 编程训练" width="150" height="150" /></a><a href="https://coolshell.cn/articles/12052.html" class="wp_rp_title">Leetcode 编程训练</a></li><li ><a href="https://coolshell.cn/articles/8387.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2012/10/Learnable_Programming-150x150.jpg" alt="Bret Victor &#8211; Learnable Programming" width="150" height="150" /></a><a href="https://coolshell.cn/articles/8387.html" class="wp_rp_title">Bret Victor &#8211; Learnable Programming</a></li><li ><a href="https://coolshell.cn/articles/22298.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png" alt="聊聊团队协同和协同工具" width="150" height="150" /></a><a href="https://coolshell.cn/articles/22298.html" class="wp_rp_title">聊聊团队协同和协同工具</a></li><li ><a href="https://coolshell.cn/articles/22173.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png" alt="“一把梭：REST API 全用 POST”" width="150" height="150" /></a><a href="https://coolshell.cn/articles/22173.html" class="wp_rp_title">“一把梭：REST API 全用 POST”</a></li><li ><a href="https://coolshell.cn/articles/22157.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg" alt="谈谈公司对员工的监控" width="150" height="150" /></a><a href="https://coolshell.cn/articles/22157.html" class="wp_rp_title">谈谈公司对员工的监控</a></li><li ><a href="https://coolshell.cn/articles/21589.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg" alt="如何做一个有质量的技术分享" width="150" height="150" /></a><a href="https://coolshell.cn/articles/21589.html" class="wp_rp_title">如何做一个有质量的技术分享</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/19612.html">50年前的登月程序和程序员有多硬核</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/19612.html/feed</wfw:commentRss>
			<slash:comments>70</slash:comments>
		
		
			</item>
		<item>
		<title>Go编程模式：修饰器</title>
		<link>https://coolshell.cn/articles/17929.html</link>
					<comments>https://coolshell.cn/articles/17929.html#comments</comments>
		
		<dc:creator><![CDATA[陈皓]]></dc:creator>
		<pubDate>Thu, 01 Jun 2017 08:48:15 +0000</pubDate>
				<category><![CDATA[Go 语言]]></category>
		<category><![CDATA[程序设计]]></category>
		<category><![CDATA[编程语言]]></category>
		<category><![CDATA[functional]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[golang]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[函数式]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=17929</guid>

					<description><![CDATA[<p>之前写过一篇《Python修饰器的函数式编程》，这种模式很容易的可以把一些函数装配到另外一些函数上，可以让你的代码更为的简单，也可以让一些“小功能型”的代码复用...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/17929.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/17929.html">Go编程模式：修饰器</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><img decoding="async" loading="lazy" class="alignright size-full wp-image-17945" src="https://coolshell.cn/wp-content/uploads/2017/06/go-hardhat.png" alt="" width="200" height="193" />之前写过一篇《<a href="https://coolshell.cn/articles/11265.html" target="_blank" rel="noopener noreferrer">Python修饰器的函数式编程</a>》，这种模式很容易的可以把一些函数装配到另外一些函数上，可以让你的代码更为的简单，也可以让一些“小功能型”的代码复用性更高，让代码中的函数可以像乐高玩具那样自由地拼装。所以，一直以来，我对修饰器decoration这种编程模式情有独钟，这里写一篇Go语言相关的文章。</p>
<section class="post-series"><h3 class="post-series-title">本文是全系列中第7 / 10篇：<a href="https://coolshell.cn/articles/series/go%e7%bc%96%e7%a8%8b%e6%a8%a1%e5%bc%8f">Go编程模式</a></h3><ul class="post-series-list"><li class="post-series-item"><span class="post-series-item-title"><a href="https://coolshell.cn/articles/21128.html">Go编程模式：切片，接口，时间和性能</a></span></li><li class="post-series-item"><span class="post-series-item-title"><a href="https://coolshell.cn/articles/21140.html">Go 编程模式：错误处理</a></span></li><li class="post-series-item"><span class="post-series-item-title"><a href="https://coolshell.cn/articles/21146.html">Go 编程模式：Functional Options</a></span></li><li class="post-series-item"><span class="post-series-item-title"><a href="https://coolshell.cn/articles/21214.html">Go编程模式：委托和反转控制</a></span></li><li class="post-series-item"><span class="post-series-item-title"><a href="https://coolshell.cn/articles/21164.html">Go编程模式：Map-Reduce</a></span></li><li class="post-series-item"><span class="post-series-item-title"><a href="https://coolshell.cn/articles/21179.html">Go 编程模式：Go Generation</a></span></li><li class="post-series-item-current post-series-item"><span class="post-series-item-title">Go编程模式：修饰器</span></li><li class="post-series-item"><span class="post-series-item-title"><a href="https://coolshell.cn/articles/21228.html">Go编程模式：Pipeline</a></span></li><li class="post-series-item"><span class="post-series-item-title"><a href="https://coolshell.cn/articles/21263.html">Go 编程模式：k8s Visitor 模式</a></span></li><li class="post-series-item"><span class="post-series-item-title"><a href="https://coolshell.cn/articles/21615.html">Go编程模式 ： 泛型编程</a></span></li></ul><nav class="post-series-nav"><span class="post-series-nav-prev">&laquo; <a href="https://coolshell.cn/articles/21179.html" rel="prev" title="Go 编程模式：Go Generation">上一篇文章</a></span><span class="post-series-nav-next"><a href="https://coolshell.cn/articles/21228.html" rel="next" title="Go编程模式：Pipeline">下一篇文章</a> &raquo;</span></nav></section>
<p>看过<a href="https://coolshell.cn/articles/11265.html" target="_blank" rel="noopener noreferrer">Python修饰器</a>那篇文章的同学，一定知道这是一种函数式编程的玩法——用一个高阶函数来包装一下。多唠叨一句，关于函数式编程，可以参看我之前写过一篇文章《<a href="https://coolshell.cn/articles/10822.html" target="_blank" rel="noopener noreferrer">函数式编程</a>》，这篇文章主要是，想通过从过程式编程的思维方式过渡到函数式编程的思维方式，从而带动更多的人玩函数式编程，所以，如果你想了解一下函数式编程，那么可以移步先阅读一下。所以，Go语言的修饰器编程模式，其实也就是函数式编程的模式。</p>
<p>不过，要提醒注意的是，Go 语言的“糖”不多，而且又是强类型的静态无虚拟机的语言，所以，无法做到像 Java 和 Python 那样的优雅的修饰器的代码。当然，也许是我才才疏学浅，如果你知道有更多的写法，请你一定告诉我。先谢过了。<br />
<span id="more-17929"></span></p>
<h4>简单示例</h4>
<p>我们先来看一个示例：</p>
<pre class="EnlighterJSRAW" data-enlighter-language="golang">package main

import "fmt"

func decorator(f func(s string)) func(s string) {

    return func(s string) {
        fmt.Println("Started")
        f(s)
        fmt.Println("Done")
    }
}

func Hello(s string) {
    fmt.Println(s)
}

func main() {
        decorator(Hello)("Hello, World!")
}</pre>
<p>我们可以看到，我们动用了一个高阶函数 <code>decorator()</code>，在调用的时候，先把 <code>Hello()</code> 函数传进去，然后其返回一个匿名函数，这个匿名函数中除了运行了自己的代码，也调用了被传入的 <code>Hello()</code> 函数。</p>
<p>这个玩法和 Python 的异曲同工，只不过，有些遗憾的是，Go 并不支持像 Python 那样的 <code>@decorator</code> 语法糖。所以，在调用上有些难看。当然，如果你要想让代码容易读一些，你可以这样：</p>
<pre class="EnlighterJSRAW" data-enlighter-language="golang">hello := decorator(Hello)
hello("Hello")</pre>
<p>我们再来看一个和计算运行时间的例子：</p>
<pre class="EnlighterJSRAW" data-enlighter-language="golang" data-enlighter-highlight="16-26">package main

import (
  "fmt"
  "reflect"
  "runtime"
  "time"
)

type SumFunc func(int64, int64) int64

func getFunctionName(i interface{}) string {
  return runtime.FuncForPC(reflect.ValueOf(i).Pointer()).Name()
}

func timedSumFunc(f SumFunc) SumFunc {
  return func(start, end int64) int64 {

    defer func(t time.Time) {
      fmt.Printf("--- Time Elapsed (%s): %v ---\n", 
          getFunctionName(f), time.Since(t))
    }(time.Now())

    return f(start, end)
  }
}

func Sum1(start, end int64) int64 {
  var sum int64
  sum = 0
  if start &gt; end {
    start, end = end, start
  }
  for i := start; i &lt;= end; i++ {
    sum += i
  }
  return sum
}

func Sum2(start, end int64) int64 {
  if start &gt; end {
    start, end = end, start
  }
  return (end - start + 1) * (end + start) / 2
}

func main() {

  sum1 := timedSumFunc(Sum1)
  sum2 := timedSumFunc(Sum2)

  fmt.Printf("%d, %d\n", sum1(-10000, 10000000), sum2(-10000, 10000000))
}</pre>
<p>关于上面的代码，有几个事说明一下：</p>
<p>1）有两个 Sum 函数，<code>Sum1()</code> 函数就是简单的做个循环，<code>Sum2()</code> 函数动用了数据公式。（注意：start 和 end 有可能有负数的情况）</p>
<p>2）代码中使用了 Go 语言的反射机器来获取函数名。</p>
<p>3）修饰器函数是 <code>timedSumFunc()</code></p>
<p>运行后输出：</p>
<pre class="EnlighterJSRAW" data-enlighter-language="shell">$ go run time.sum.go
--- Time Elapsed (main.Sum1): 3.557469ms ---
--- Time Elapsed (main.Sum2): 291ns ---
49999954995000, 49999954995000
</pre>
<h4>HTTP 相关的一个示例</h4>
<p>我们再来看一个处理 HTTP 请求的相关的例子。</p>
<p>先看一个简单的 HTTP Server 的代码。</p>
<pre class="EnlighterJSRAW" data-enlighter-language="golang" data-enlighter-highlight="10-16,24">package main

import (
    "fmt"
    "log"
    "net/http"
    "strings"
)

func WithServerHeader(h http.HandlerFunc) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        log.Println("---&gt;WithServerHeader()")
        w.Header().Set("Server", "HelloServer v0.0.1")
        h(w, r)
    }
}

func hello(w http.ResponseWriter, r *http.Request) {
    log.Printf("Recieved Request %s from %s\n", r.URL.Path, r.RemoteAddr)
    fmt.Fprintf(w, "Hello, World! "+r.URL.Path)
}

func main() {
    http.HandleFunc("/v1/hello", WithServerHeader(hello))
    err := http.ListenAndServe(":8080", nil)
    if err != nil {
        log.Fatal("ListenAndServe: ", err)
    }
}</pre>
<p>上面代码中使用到了修饰模式，<code>WithServerHeader()</code> 函数就是一个 Decorator，其传入一个 <code>http.HandlerFunc</code>，然后返回一个改写的版本。上面的例子还是比较简单，用 <code>WithServerHeader()</code> 就可以加入一个 Response 的 Header。</p>
<p>于是，这样的函数我们可以写出好些个。如下所示，有写 HTTP 响应头的，有写认证 Cookie 的，有检查认证Cookie的，有打日志的……</p>
<pre class="EnlighterJSRAW" data-enlighter-language="golang" data-enlighter-highlight="60-62">package main

import (
    "fmt"
    "log"
    "net/http"
    "strings"
)

func WithServerHeader(h http.HandlerFunc) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        log.Println("---&gt;WithServerHeader()")
        w.Header().Set("Server", "HelloServer v0.0.1")
        h(w, r)
    }
}

func WithAuthCookie(h http.HandlerFunc) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        log.Println("---&gt;WithAuthCookie()")
        cookie := &amp;http.Cookie{Name: "Auth", Value: "Pass", Path: "/"}
        http.SetCookie(w, cookie)
        h(w, r)
    }
}

func WithBasicAuth(h http.HandlerFunc) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        log.Println("---&gt;WithBasicAuth()")
        cookie, err := r.Cookie("Auth")
        if err != nil || cookie.Value != "Pass" {
            w.WriteHeader(http.StatusForbidden)
            return
        }
        h(w, r)
    }
}

func WithDebugLog(h http.HandlerFunc) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        log.Println("---&gt;WithDebugLog")
        r.ParseForm()
        log.Println(r.Form)
        log.Println("path", r.URL.Path)
        log.Println("scheme", r.URL.Scheme)
        log.Println(r.Form["url_long"])
        for k, v := range r.Form {
            log.Println("key:", k)
            log.Println("val:", strings.Join(v, ""))
        }
        h(w, r)
    }
}
func hello(w http.ResponseWriter, r *http.Request) {
    log.Printf("Recieved Request %s from %s\n", r.URL.Path, r.RemoteAddr)
    fmt.Fprintf(w, "Hello, World! "+r.URL.Path)
}

func main() {
    http.HandleFunc("/v1/hello", WithServerHeader(WithAuthCookie(hello)))
    http.HandleFunc("/v2/hello", WithServerHeader(WithBasicAuth(hello)))
    http.HandleFunc("/v3/hello", WithServerHeader(WithBasicAuth(WithDebugLog(hello))))
    err := http.ListenAndServe(":8080", nil)
    if err != nil {
        log.Fatal("ListenAndServe: ", err)
    }
}</pre>
<h4>多个修饰器的 Pipeline</h4>
<p>在使用上，需要对函数一层层的套起来，看上去好像不是很好看，如果需要 decorator 比较多的话，代码会比较难看了。嗯，我们可以重构一下。</p>
<p>重构时，我们需要先写一个工具函数——用来遍历并调用各个 decorator：</p>
<pre class="EnlighterJSRAW" data-enlighter-language="golang">type HttpHandlerDecorator func(http.HandlerFunc) http.HandlerFunc

func Handler(h http.HandlerFunc, decors ...HttpHandlerDecorator) http.HandlerFunc {
    for i := range decors {
        d := decors[len(decors)-1-i] // iterate in reverse
        h = d(h)
    }
    return h
}</pre>
<p>然后，我们就可以像下面这样使用了。</p>
<pre class="EnlighterJSRAW" data-enlighter-language="golang">http.HandleFunc("/v4/hello", Handler(hello,
                WithServerHeader, WithBasicAuth, WithDebugLog))</pre>
<p>这样的代码是不是更易读了一些？pipeline 的功能也就出来了。</p>
<h4>泛型的修饰器</h4>
<p>不过，对于 Go 的修饰器模式，还有一个小问题 —— 好像无法做到泛型，就像上面那个计算时间的函数一样，其代码耦合了需要被修饰的函数的接口类型，无法做到非常通用，如果这个事解决不了，那么，这个修饰器模式还是有点不好用的。</p>
<p>因为 Go 语言不像 Python 和 Java，Python是动态语言，而 Java 有语言虚拟机，所以他们可以干好些比较变态的事，然而 Go 语言是一个静态的语言，这意味着其类型需要在编译时就要搞定，否则无法编译。不过，Go 语言支持的最大的泛型是 <code>interface{}</code> 还有比较简单的 reflection 机制，在上面做做文章，应该还是可以搞定的。</p>
<p>废话不说，下面是我用 reflection 机制写的一个比较通用的修饰器（为了便于阅读，我删除了出错判断代码）</p>
<pre class="EnlighterJSRAW" data-enlighter-language="golang" data-enlighter-highlight="7,10">func Decorator(decoPtr, fn interface{}) (err error) {
    var decoratedFunc, targetFunc reflect.Value

    decoratedFunc = reflect.ValueOf(decoPtr).Elem()
    targetFunc = reflect.ValueOf(fn)

    v := reflect.MakeFunc(targetFunc.Type(),
            func(in []reflect.Value) (out []reflect.Value) {
                fmt.Println("before")
                out = targetFunc.Call(in)
                fmt.Println("after")
                return
            })

    decoratedFunc.Set(v)
    return
}</pre>
<p>上面的代码动用了 <code>reflect.MakeFunc()</code> 函数制出了一个新的函数其中的 <code>targetFunc.Call(in)</code> 调用了被修饰的函数。关于 Go 语言的反射机制，推荐官方文章 —— 《<a href="https://blog.golang.org/laws-of-reflection" target="_blank" rel="noopener noreferrer">The Laws of Reflection</a>》，在这里我不多说了。</p>
<p>上面这个 <code>Decorator()</code> 需要两个参数，</p>
<ul>
<li>第一个是出参 <code>decoPtr</code> ，就是完成修饰后的函数</li>
<li>第二个是入参 <code>fn</code> ，就是需要修饰的函数</li>
</ul>
<p>这样写是不是有些二？的确是的。不过，这是我个人在 Go 语言里所能写出来的最好的的代码了。如果你知道更多优雅的，请你一定告诉我！</p>
<p>好的，让我们来看一下使用效果。首先假设我们有两个需要修饰的函数：</p>
<pre class="EnlighterJSRAW" data-enlighter-language="golang">func foo(a, b, c int) int {
    fmt.Printf("%d, %d, %d \n", a, b, c)
    return a + b + c
}

func bar(a, b string) string {
    fmt.Printf("%s, %s \n", a, b)
    return a + b
}</pre>
<p>然后，我们可以这样做：</p>
<pre class="EnlighterJSRAW" data-enlighter-language="golang">type MyFoo func(int, int, int) int
var myfoo MyFoo
Decorator(&amp;myfoo, foo)
myfoo(1, 2, 3)
</pre>
<p>你会发现，使用 <code>Decorator()</code> 时，还需要先声明一个函数签名，感觉好傻啊。一点都不泛型，不是吗？</p>
<p>嗯。如果你不想声明函数签名，那么你也可以这样</p>
<pre class="EnlighterJSRAW" data-enlighter-language="golang">mybar := bar
Decorator(&amp;mybar, bar)
mybar("hello,", "world!")</pre>
<p>好吧，看上去不是那么的漂亮，但是 it works。看样子 Go 语言目前本身的特性无法做成像 Java 或 Python 那样，对此，我们只能多求 Go 语言多放糖了！</p>
<p>Again， 如果你有更好的写法，请你一定要告诉我。</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/21164.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2020/12/go.map_.reduce-150x150.png" alt="Go编程模式：Map-Reduce" width="150" height="150" /></a><a href="https://coolshell.cn/articles/21164.html" class="wp_rp_title">Go编程模式：Map-Reduce</a></li><li ><a href="https://coolshell.cn/articles/21146.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2020/12/go.options-150x150.png" alt="Go 编程模式：Functional Options" width="150" height="150" /></a><a href="https://coolshell.cn/articles/21146.html" class="wp_rp_title">Go 编程模式：Functional Options</a></li><li ><a href="https://coolshell.cn/articles/11265.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960-150x150.jpg" alt="Python修饰器的函数式编程" width="150" height="150" /></a><a href="https://coolshell.cn/articles/11265.html" class="wp_rp_title">Python修饰器的函数式编程</a></li><li ><a href="https://coolshell.cn/articles/21615.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2021/09/go-generics-150x150.png" alt="Go编程模式 ： 泛型编程" width="150" height="150" /></a><a href="https://coolshell.cn/articles/21615.html" class="wp_rp_title">Go编程模式 ： 泛型编程</a></li><li ><a href="https://coolshell.cn/articles/21263.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2020/12/go.k8s-150x150.png" alt="Go 编程模式：k8s Visitor 模式" width="150" height="150" /></a><a href="https://coolshell.cn/articles/21263.html" class="wp_rp_title">Go 编程模式：k8s Visitor 模式</a></li><li ><a href="https://coolshell.cn/articles/21228.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2020/12/go.line_.-150x150.png" alt="Go编程模式：Pipeline" width="150" height="150" /></a><a href="https://coolshell.cn/articles/21228.html" class="wp_rp_title">Go编程模式：Pipeline</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/17929.html">Go编程模式：修饰器</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/17929.html/feed</wfw:commentRss>
			<slash:comments>27</slash:comments>
		
		
			</item>
		<item>
		<title>如何重构“箭头型”代码</title>
		<link>https://coolshell.cn/articles/17757.html</link>
					<comments>https://coolshell.cn/articles/17757.html#comments</comments>
		
		<dc:creator><![CDATA[陈皓]]></dc:creator>
		<pubDate>Wed, 05 Apr 2017 10:07:14 +0000</pubDate>
				<category><![CDATA[C/C++语言]]></category>
		<category><![CDATA[程序设计]]></category>
		<category><![CDATA[编程语言]]></category>
		<category><![CDATA[Code Review]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Refactory]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=17757</guid>

					<description><![CDATA[<p>本文主要起因是，一次在微博上和朋友关于嵌套好几层的if-else语句的代码重构的讨论（微博原文），在微博上大家有各式各样的问题和想法。按道理来说这些都是编程的基...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/17757.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/17757.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>本文主要起因是，一次在微博上和朋友关于嵌套好几层的if-else语句的代码重构的讨论（<a href="http://weibo.com/1401880315/ECmCW0oy2" target="_blank" rel="noopener noreferrer">微博原文</a>），在微博上大家有各式各样的问题和想法。按道理来说这些都是编程的基本功，似乎不太值得写一篇文章，不过我觉得很多东西可以从一个简单的东西出发，到达本质，所以，我觉得有必要在这里写一篇的文章。不一定全对，只希望得到更多的讨论，因为有了更深入的讨论才能进步。</p>
<p>文章有点长，我在文章最后会给出相关的思考和总结陈词，你可以跳到结尾。</p>
<p>所谓箭头型代码，基本上来说就是下面这个图片所示的情况。</p>
<p><img decoding="async" loading="lazy" class="aligncenter wp-image-17758 size-full" src="https://coolshell.cn/wp-content/uploads/2017/04/IMG_7411.jpg" alt="" width="720" height="511" srcset="https://coolshell.cn/wp-content/uploads/2017/04/IMG_7411.jpg 720w, https://coolshell.cn/wp-content/uploads/2017/04/IMG_7411-300x213.jpg 300w, https://coolshell.cn/wp-content/uploads/2017/04/IMG_7411-380x270.jpg 380w" sizes="(max-width: 720px) 100vw, 720px" /></p>
<p>那么，这样“箭头型”的代码有什么问题呢？看上去也挺好看的，有对称美。但是……</p>
<p>关于箭头型代码的问题有如下几个：</p>
<p><span id="more-17757"></span></p>
<p>1）我的显示器不够宽，箭头型代码缩进太狠了，需要我来回拉水平滚动条，这让我在读代码的时候，相当的不舒服。</p>
<p>2）除了宽度外还有长度，有的代码的<code>if-else</code>里的<code>if-else</code>里的<code>if-else</code>的代码太多，读到中间你都不知道中间的代码是经过了什么样的层层检查才来到这里的。</p>
<p>总而言之，<strong>“箭头型代码”如果嵌套太多，代码太长的话，会相当容易让维护代码的人（包括自己）迷失在代码中，因为看到最内层的代码时，你已经不知道前面的那一层一层的条件判断是什么样的，代码是怎么运行到这里的，所以，箭头型代码是非常难以维护和Debug的</strong>。</p>
<h4>微博上的案例 与 Guard Clauses</h4>
<p>OK，我们先来看一下微博上的那个示例，代码量如果再大一点，嵌套再多一点，你很容易会在条件中迷失掉（下面这个示例只是那个“大箭头”下的一个小箭头）</p>
<pre class="EnlighterJSRAW" data-enlighter-language="cpp">
FOREACH(Ptr&lt;WfExpression&gt;, argument, node-&gt;arguments) {
    int index = manager-&gt;expressionResolvings.Keys().IndexOf(argument.Obj());
    if (index != -1) {
        auto type = manager-&gt;expressionResolvings.Values()[index].type;
        if (! types.Contains(type.Obj())) {
            types.Add(type.Obj());
            if (auto group = type-&gt;GetTypeDescriptor()-&gt;GetMethodGroupByName(L&quot;CastResult&quot;, true)) {
                int count = group-&gt;GetMethodCount();
                for (int i = 0; i &lt; count; i++) { auto method = group-&gt;GetMethod(i);
                    if (method-&gt;IsStatic()) {
                        if (method-&gt;GetParameterCount() == 1 &amp;&amp;
                            method-&gt;GetParameter(0)-&gt;GetType()-&gt;GetTypeDescriptor() == description::GetTypeDescriptor&lt;DescriptableObject&gt;() &amp;&amp;
                            method-&gt;GetReturn()-&gt;GetTypeDescriptor() != description::GetTypeDescriptor&lt;void&gt;() ) {
                            symbol-&gt;typeInfo = CopyTypeInfo(method-&gt;GetReturn());
                            break;
                        }
                    }
                }
            }
        }
    }
}
</pre>
<p>上面这段代码，可以把条件反过来写，然后就可以把箭头型的代码解掉了，重构的代码如下所示：</p>
<pre class="EnlighterJSRAW" data-enlighter-language="cpp">
FOREACH(Ptr&lt;WfExpression&gt;, argument, node-&gt;arguments) {
    int index = manager-&gt;expressionResolvings.Keys().IndexOf(argument.Obj());
    if (index == -1)  continue;
    
    auto type = manager-&gt;expressionResolvings.Values()[index].type;
    if ( types.Contains(type.Obj()))  continue;
    
    types.Add(type.Obj());

    auto group = type-&gt;GetTypeDescriptor()-&gt;GetMethodGroupByName(L&quot;CastResult&quot;, true);
    if  ( ! group ) continue;
 
    int count = group-&gt;GetMethodCount();
    for (int i = 0; i &lt; count; i++) { auto method = group-&gt;GetMethod(i);
        if (! method-&gt;IsStatic()) continue;
       
        if ( method-&gt;GetParameterCount() == 1 &amp;&amp;
               method-&gt;GetParameter(0)-&gt;GetType()-&gt;GetTypeDescriptor() == description::GetTypeDescriptor&lt;DescriptableObject&gt;() &amp;&amp;
               method-&gt;GetReturn()-&gt;GetTypeDescriptor() != description::GetTypeDescriptor&lt;void&gt;() ) {
            symbol-&gt;typeInfo = CopyTypeInfo(method-&gt;GetReturn());
            break;
        }
    }
}
</pre>
<p>这种代码的重构方式叫 <strong>Guard Clauses</strong></p>
<ul>
<li><a href="https://martinfowler.com/" target="_blank" rel="noopener noreferrer">Martin Fowler</a> 的 Refactoring 的网站上有相应的说明《<a href="https://refactoring.com/catalog/replaceNestedConditionalWithGuardClauses.html" target="_blank" rel="noopener noreferrer">Replace Nested Conditional with Guard Clauses</a>》。</li>
</ul>
<ul>
<li><a href="https://blog.codinghorror.com/" target="_blank" rel="noopener noreferrer">Coding Horror</a> 上也有一篇文章讲了这种重构的方式 —— 《<a href="https://blog.codinghorror.com/flattening-arrow-code/" target="_blank" rel="noopener noreferrer">Flattening Arrow Code</a>》</li>
</ul>
<ul>
<li><a href="http://stackoverflow.com/" target="_blank" rel="noopener noreferrer">StackOverflow</a> 上也有相关的问题说了这种方式 —— 《<a href="http://stackoverflow.com/questions/356121/refactor-nested-if-statement-for-clarity" target="_blank" rel="noopener noreferrer">Refactor nested IF statement for clarity</a>》</li>
</ul>
<p>这里的思路其实就是，<strong>让出错的代码先返回，前面把所有的错误判断全判断掉，然后就剩下的就是正常的代码了</strong>。</p>
<h4>抽取成函数</h4>
<p>微博上有些人说，continue 语句破坏了阅读代码的通畅，我觉得他们一定没有好好读这里面的代码，其实，我们可以看到，所有的 if 语句都是在判断是否出错的情况，所以，在维护代码的时候，你可以完全不理会这些 if 语句，因为都是出错处理的，而剩下的代码都是正常的功能代码，反而更容易阅读了。当然，一定有不是上面代码里的这种情况，那么，不用continue ，我们还能不能重构呢？</p>
<p>当然可以，抽成函数：</p>
<pre class="EnlighterJSRAW" data-enlighter-language="cpp">
bool CopyMethodTypeInfo(auto &amp;method, auto &amp;group, auto &amp;symbol) 
{
    if (! method-&gt;IsStatic()) {
        return true;
    }
    if ( method-&gt;GetParameterCount() == 1 &amp;&amp;
           method-&gt;GetParameter(0)-&gt;GetType()-&gt;GetTypeDescriptor() == description::GetTypeDescriptor&lt;DescriptableObject&gt;() &amp;&amp;
           method-&gt;GetReturn()-&gt;GetTypeDescriptor() != description::GetTypeDescriptor&lt;void&gt;() ) {
        symbol-&gt;typeInfo = CopyTypeInfo(method-&gt;GetReturn());
        return false;
    }
    return true;
}

void ExpressionResolvings(auto &amp;manager, auto &amp;argument, auto &amp;symbol) 
{
    int index = manager-&gt;expressionResolvings.Keys().IndexOf(argument.Obj());
    if (index == -1) return;
    
    auto type = manager-&gt;expressionResolvings.Values()[index].type;
    if ( types.Contains(type.Obj())) return;

    types.Add(type.Obj());
    auto group = type-&gt;GetTypeDescriptor()-&gt;GetMethodGroupByName(L&quot;CastResult&quot;, true);
    if  ( ! group ) return;

    int count = group-&gt;GetMethodCount();
    for (int i = 0; i &lt; count; i++) { auto method = group-&gt;GetMethod(i);
        if ( ! CopyMethodTypeInfo(method, group, symbol) ) break;
    }
}

...
...
FOREACH(Ptr&lt;WfExpression&gt;, argument, node-&gt;arguments) {
    ExpressionResolvings(manager, arguments, symbol)
}
...
...
</pre>
<p>你发出现，抽成函数后，代码比之前变得更容易读和更容易维护了。不是吗？</p>
<p>有人说：“如果代码不共享，就不要抽取成函数！”，持有这个观点的人太死读书了。函数是代码的封装或是抽象，并不一定用来作代码共享使用，函数用于屏蔽细节，让其它代码耦合于接口而不是细节实现，这会让我们的代码更为简单，简单的东西都能让人易读也易维护。这才是函数的作用。</p>
<h4>嵌套的 if 外的代码</h4>
<p>微博上还有人问，原来的代码如果在各个 if 语句后还有要执行的代码，那么应该如何重构。比如下面这样的代码。</p>
<pre class="EnlighterJSRAW" data-enlighter-language="cpp">
//原版
for(....) {
    do_before_cond1()
    if (cond1) {
        do_before_cond2();
        if (cond2) {
            do_before_cond3();
            if (cond3) {
                do_something();
            }
            do_after_cond3();
        }
        do_after_cond2();
    }
    do_after_cond1();
}</pre>
<p>上面这段代码中的那些 <code>do_after_condX()</code> 是无论条件成功与否都要执行的。所以，我们拉平后的代码如下所示：</p>
<pre class="EnlighterJSRAW" data-enlighter-language="cpp">
//重构第一版
for(....) {
    do_before_cond1();
    if ( !cond1 ) {
        do_after_cond1();
        continue
    } 
    do_after_cond1();

    do_before_cond2();
    if ( !cond2 ) { 
        do_after_cond2();
        continue;
    }
    do_after_cond2();

    do_before_cond3();
    if ( !cond3 ) {
        do_after_cond3();
        continue;
    }
    do_after_cond3();

    do_something();  
}</pre>
<p>你会发现，上面的 <code>do_after_condX</code> 出现了两份。<strong>如果 if 语句块中的代码改变了某些<code>do_after_condX</code>依赖的状态，那么这是最终版本。</strong></p>
<p>但是，如果它们之前没有依赖关系的话，根据 DRY 原则，我们就可以只保留一份，那么直接掉到 if 条件前就好了，如下所示：</p>
<pre class="EnlighterJSRAW" data-enlighter-language="cpp">
//重构第二版
for(....) {
    do_before_cond1();
    do_after_cond1();
    if ( !cond1 ) continue;
 
    do_before_cond2();
    do_after_cond2();
    if ( !cond2 ) continue;

    do_before_cond3();
    do_after_cond3();
    if ( !cond3 ) continue;

    do_something();  
}</pre>
<p>此时，你会说，我靠，居然，改变了执行的顺序，把条件放到 <code>do_after_condX()</code> 后面去了。这会不会有问题啊？</p>
<p>其实，你再分析一下之前的代码，你会发现，本来，cond1 是判断 do_before_cond1() 是否出错的，如果有成功了，才会往下执行。而 do_after_cond1() 是无论如何都要执行的。从逻辑上来说，do_after_cond1()其实和do_before_cond1()的执行结果无关，而 cond1 却和是否去执行 do_before_cond2() 相关了。如果我把断行变成下面这样，反而代码逻辑更清楚了。</p>
<pre class="EnlighterJSRAW" data-enlighter-language="cpp">
//重构第三版
for(....) {

    do_before_cond1();
    do_after_cond1();


    if ( !cond1 ) continue;  // &lt;-- cond1 成了是否做第二个语句块的条件
    do_before_cond2();
    do_after_cond2();

    if ( !cond2 ) continue; // &lt;-- cond2 成了是否做第三个语句块的条件
    do_before_cond3();
    do_after_cond3();

    if ( !cond3 ) continue; //&lt;-- cond3 成了是否做第四个语句块的条件
    do_something(); 
 
}
</pre>
<p>于是乎，在未来维护代码的时候，维护人一眼看上去就明白，代码在什么时候会执行到哪里。 这个时候，你会发现，把这些语句块抽成函数，代码会干净的更多，再重构一版：</p>
<pre class="EnlighterJSRAW" data-enlighter-language="cpp">
//重构第四版
bool do_func3() {
   do_before_cond2();
   do_after_cond2();
   return cond3;
}

bool do_func2() {
   do_before_cond2();
   do_after_cond2();
   return cond2;
}

bool do_func1() {
   do_before_cond1();
   do_after_cond1();
   return cond1;
}

// for-loop 你可以重构成这样
for (...) {
    bool cond = do_func1();
    if (cond) cond = do_func2();
    if (cond) cond = do_func3();
    if (cond) do_something();
}

// for-loop 也可以重构成这样
for (...) {
    if ( ! do_func1() ) continue;
    if ( ! do_func2() ) continue;
    if ( ! do_func3() ) continue;
    do_something();
}
</pre>
<p>上面，我给出了两个版本的for-loop，你喜欢哪个？我喜欢第二个。这个时候，因为for-loop里的代码非常简单，就算你不喜欢 continue ，这样的代码阅读成本已经很低了。</p>
<h4>状态检查嵌套</h4>
<p>接下来，我们再来看另一个示例。下面的代码的伪造了一个场景——把两个人拉到一个一对一的聊天室中，因为要检查双方的状态，所以，代码可能会写成了“箭头型”。</p>
<pre class="EnlighterJSRAW" data-enlighter-language="cpp">
int ConnectPeer2Peer(Conn *pA, Conn* pB, Manager *manager)
{
    if ( pA-&gt;isConnected() ) {
        manager-&gt;Prepare(pA);
        if ( pB-&gt;isConnected() ) {
            manager-&gt;Prepare(pB);
            if ( manager-&gt;ConnectTogther(pA, pB) ) {
                pA-&gt;Write(&quot;connected&quot;);
                pB-&gt;Write(&quot;connected&quot;);
                return S_OK;
            }else{
                return S_ERROR;
            }

        }else {
            pA-&gt;Write(&quot;Peer is not Ready, waiting...&quot;);
            return S_RETRY;
        }
    }else{
        if ( pB-&gt;isConnected() ) {
            manager-&gt;Prepare();
            pB-&gt;Write(&quot;Peer is not Ready, waiting...&quot;);
            return S_RETRY;
        }else{
            pA-&gt;Close();
            pB-&gt;Close();
            return S_ERROR;
        }
    }
    //Shouldn't be here!
    return S_ERROR;
}</pre>
<p>重构上面的代码，我们可以先分析一下上面的代码，说明了，上面的代码就是对 PeerA 和 PeerB 的两个状态 “连上”， “未连上” 做组合 “状态” （注：实际中的状态应该比这个还要复杂，可能还会有“断开”、“错误”……等等状态）， 于是，我们可以把代码写成下面这样，合并上面的嵌套条件，对于每一种组合都做出判断。这样一来，逻辑就会非常的干净和清楚。</p>
<pre class="EnlighterJSRAW" data-enlighter-language="cpp">
int ConnectPeer2Peer(Conn *pA, Conn* pB, Manager *manager)
{
    if ( pA-&gt;isConnected() ) {
        manager-&gt;Prepare(pA);
    }

    if ( pB-&gt;isConnected() ) {
        manager-&gt;Prepare(pB);
    }

    // pA = YES &amp;&amp; pB = NO
    if (pA-&gt;isConnected() &amp;&amp; ! pB-&gt;isConnected()  ) {
        pA-&gt;Write(&quot;Peer is not Ready, waiting&quot;);
        return S_RETRY;
    // pA = NO &amp;&amp; pB = YES
    }else if ( !pA-&gt;isConnected() &amp;&amp; pB-&gt;isConnected() ) {
        pB-&gt;Write(&quot;Peer is not Ready, waiting&quot;);
        return S_RETRY;
    // pA = YES &amp;&amp; pB = YES
    }else if (pA-&gt;isConnected() &amp;&amp; pB-&gt;isConnected()  ) {
        if ( ! manager-&gt;ConnectTogther(pA, pB) ) {
            return S_ERROR;
        }
        pA-&gt;Write(&quot;connected&quot;);
        pB-&gt;Write(&quot;connected&quot;);
        return S_OK;
    }

    // pA = NO, pB = NO
    pA-&gt;Close();
    pB-&gt;Close();
    return S_ERROR;
}</pre>
<h4>延伸思考</h4>
<p>对于 <code>if-else</code> 语句来说，一般来说，就是检查两件事：<strong>错误</strong> 和 <strong>状态</strong>。</p>
<h5>检查错误</h5>
<p>对于检查错误来说，使用 Guard Clauses 会是一种标准解，但我们还需要注意下面几件事：</p>
<p style="padding-left: 30px;">1）当然，出现错误的时候，还会出现需要释放资源的情况。你可以使用 <code>goto fail;</code> 这样的方式，但是最优雅的方式应该是C++面向对象式的 RAII 方式。</p>
<p style="padding-left: 30px;">2）以错误码返回是一种比较简单的方式，这种方式有很一些问题，比如，如果错误码太多，判断出错的代码会非常复杂，另外，正常的代码和错误的代码会混在一起，影响可读性。所以，在更为高组的语言中，使用 <code>try-catch</code> 异常捕捉的方式，会让代码更为易读一些。</p>
<h5>检查状态</h5>
<p>对于检查状态来说，实际中一定有更为复杂的情况，比如下面几种情况：</p>
<p style="padding-left: 30px;">1）像TCP协议中的两端的状态变化。</p>
<p style="padding-left: 30px;">2）像shell各个命令的命令选项的各种组合。</p>
<p style="padding-left: 30px;">3）像游戏中的状态变化（一棵非常复杂的状态树）。</p>
<p style="padding-left: 30px;">4）像语法分析那样的状态变化。</p>
<p>对于这些复杂的状态变化，其本上来说，你需要先定义一个状态机，或是一个子状态的组合状态的查询表，或是一个状态查询分析树。</p>
<p><strong>写代码时，代码的运行中的控制状态或业务状态是会让你的代码流程变得混乱的一个重要原因，重构“箭头型”代码的一个很重要的工作就是重新梳理和描述这些状态的变迁关系</strong>。</p>
<h4>总结</h4>
<p>好了，下面总结一下，把“箭头型”代码重构掉的几个手段如下：</p>
<p>1）<strong>使用 Guard Clauses </strong>。 尽可能的让出错的先返回， 这样后面就会得到干净的代码。</p>
<p>2）<strong>把条件中的语句块抽取成函数</strong>。 有人说：“如果代码不共享，就不要抽取成函数！”，持有这个观点的人太死读书了。函数是代码的封装或是抽象，并不一定用来作代码共享使用，函数用于屏蔽细节，让其它代码耦合于接口而不是细节实现，这会让我们的代码更为简单，简单的东西都能让人易读也易维护，<strong>写出让人易读易维护的代码才是重构代码的初衷</strong>！</p>
<p>3）<strong>对于出错处理，使用try-catch异常处理和<a href="http://stackoverflow.com/questions/2321511/what-is-meant-by-resource-acquisition-is-initialization-raii" target="_blank" rel="noopener noreferrer">RAII机制</a></strong>。返回码的出错处理有很多问题，比如：A) 返回码可以被忽略，B) 出错处理的代码和正常处理的代码混在一起，C) 造成函数接口污染，比如像atoi()这种错误码和返回值共用的糟糕的函数。</p>
<p>4）<strong>对于多个状态的判断和组合，如果复杂了，可以使用“组合状态表”，或是状态机加Observer的状态订阅的设计模式</strong>。这样的代码即解了耦，也干净简单，同样有很强的扩展性。</p>
<p>5） <strong>重构“箭头型”代码其实是在帮你重新梳理所有的代码和逻辑，这个过程非常值得为之付出</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" ><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/19612.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2019/07/1920px-Margaret_Hamilton_-_restoration-e1563697198766-1-150x150.jpg" alt="50年前的登月程序和程序员有多硬核" width="150" height="150" /></a><a href="https://coolshell.cn/articles/19612.html" class="wp_rp_title">50年前的登月程序和程序员有多硬核</a></li><li ><a href="https://coolshell.cn/articles/17929.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2017/06/go-hardhat-150x150.png" alt="Go编程模式：修饰器" width="150" height="150" /></a><a href="https://coolshell.cn/articles/17929.html" class="wp_rp_title">Go编程模式：修饰器</a></li><li ><a href="https://coolshell.cn/articles/12052.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg" alt="Leetcode 编程训练" width="150" height="150" /></a><a href="https://coolshell.cn/articles/12052.html" class="wp_rp_title">Leetcode 编程训练</a></li><li ><a href="https://coolshell.cn/articles/11656.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2014/06/software_development-150x150.png" alt="开发团队的效率" width="150" height="150" /></a><a href="https://coolshell.cn/articles/11656.html" class="wp_rp_title">开发团队的效率</a></li><li ><a href="https://coolshell.cn/articles/11432.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2014/04/code_review-150x150.jpg" alt="从Code Review 谈如何做技术" width="150" height="150" /></a><a href="https://coolshell.cn/articles/11432.html" class="wp_rp_title">从Code Review 谈如何做技术</a></li><li ><a href="https://coolshell.cn/articles/11265.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960-150x150.jpg" alt="Python修饰器的函数式编程" width="150" height="150" /></a><a href="https://coolshell.cn/articles/11265.html" class="wp_rp_title">Python修饰器的函数式编程</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/17757.html">如何重构“箭头型”代码</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/17757.html/feed</wfw:commentRss>
			<slash:comments>56</slash:comments>
		
		
			</item>
		<item>
		<title>Leetcode 编程训练</title>
		<link>https://coolshell.cn/articles/12052.html</link>
					<comments>https://coolshell.cn/articles/12052.html#comments</comments>
		
		<dc:creator><![CDATA[陈皓]]></dc:creator>
		<pubDate>Thu, 23 Oct 2014 02:51:54 +0000</pubDate>
				<category><![CDATA[C/C++语言]]></category>
		<category><![CDATA[杂项资源]]></category>
		<category><![CDATA[编程语言]]></category>
		<category><![CDATA[Algorithm]]></category>
		<category><![CDATA[C++]]></category>
		<category><![CDATA[Leetcode]]></category>
		<category><![CDATA[Programmer]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[程序员]]></category>
		<category><![CDATA[算法]]></category>
		<category><![CDATA[面试]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=12052</guid>

					<description><![CDATA[<p>Leetcode这个网站上的题都是一些经典的公司用来面试应聘者的面试题，很多人通过刷这些题来应聘一些喜欢面试算法的公司，比如：Google、微软、Faceboo...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/12052.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/12052.html">Leetcode 编程训练</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><img decoding="async" loading="lazy" class="alignright wp-image-12054 size-full" src="https://coolshell.cn/wp-content/uploads/2014/10/LeetCodeLogo-1.png" alt="LeetCodeLogo (1)" width="121" height="100" />Leetcode这个网站上的题都是一些经典的公司用来面试应聘者的面试题，很多人通过刷这些题来应聘一些喜欢面试算法的公司，比如：Google、微软、Facebook、Amazon之类的这些公司，基本上是应试教育的功利主义。</p>
<p>我做这些题目的不是为了要去应聘这些公司，而是为了锻炼一下自己的算法和编程能力。因为我开始工作的时候基本没有这样的训练算法和编程的网站，除了大学里的“算法和数据结构”里的好些最基础最基础的知识，基本上没有什么训练。所以，当我看到有人在做这些题的时候，我也蠢蠢欲动地想去刷一下。</p>
<p>于是，我花了3-4个月的业余时间，我把<a href="https://oj.leetcode.com/problems/" target="_blank">Leetcode的154道题</a>全部做完了。（这也是最近我没有太多的时间来写博客的原因，你可以看到我之前<a title="谜题的答案和活动的心得体会" href="https://coolshell.cn/articles/11847.html" target="_blank">做的那个活动</a>中有几个算法题来自于Leetcode）有人说我时间太多了，这里声明一下，我基本上都是利用了晚上10点以后的时间来做这些题的。</p>
<p>LeetCode的题大致分成两类：</p>
<p style="padding-left: 30px;"><strong>1）基础算法的知识</strong>。这些题里面有大量的算法题，解这些题都是有套路的，不是用递归（深度优先DFS，广度优先BFS），就是要用动态规划（Dynamic Programming），或是拆半查找（Binary Search），或是回溯（Back tracing），或是分治法（Divide and Conquer），还有大量的对树，数组、链表、字符串和hash表的操作。<strong>通过做这些题能让你对这些最基础的算法的思路有非常扎实的了解和训练</strong>。对我而言，Dynamic Programming 是我的短板，尤其是一些比较复杂的问题，在推导递推公式上总是有思维的缺陷（数学是我的硬伤），通过做了这些题后，我能感到我在DP的思路上有了很大的收获。</p>
<p style="padding-left: 30px;"><strong>2）编程题</strong>。比如：atoi，strstr，add two num，括号匹配，字符串乘法，通配符匹配，文件路径简化，Text Justification，反转单词等等，这些题的Edge Case, Corner Case有很多。这些题需要你想清楚了再干，只要你稍有疏忽，就会有几个case让你痛不欲生，而且一不小心就会让你的代码会写得又臭又长，无法阅读。<strong>通过做这些题，可以非常好的训练你对各种情况的考虑，以及你对程序代码组织的掌控（其实就是其中的状态变量）。</strong>还记得我在《<a title="函数式编程" href="https://coolshell.cn/articles/10822.html" target="_blank">函数式编程</a>》中说的，程序中的状态是你程序变得复杂难维护的直接原因。</p>
<p>我觉得每个程序员都应该花时间和精力做这些题，因为你会从这些题中得到很大的收益。做完这些题后你一定会明白下面几个道理：</p>
<p><span id="more-12052"></span></p>
<p style="padding-left: 30px;"><strong>1）想清楚了再干</strong>。这个观点我以前就在《<a title="多些时间能少写些代码" href="https://coolshell.cn/articles/5686.html" target="_blank">多些时间可以少些代码</a>》说过。如果你拿到题就上去直接写代码的话，你一定会被各种case打回来了。然后呢，你一着急，你就会进入那种我在《<a title="开发团队的效率" href="https://coolshell.cn/articles/11656.html" target="_blank">开发团队的效率</a>》中说的那种毫无效率case by case的开发模式，而你也进入了“平庸模式”。于是你就会出现下图那样的情况。</p>
<figure id="attachment_12053" aria-describedby="caption-attachment-12053" style="width: 440px" class="wp-caption aligncenter"><img decoding="async" loading="lazy" class="size-full wp-image-12053" src="https://coolshell.cn/wp-content/uploads/2014/10/bug_fixing.gif" alt="Case-by-Case Developement" width="440" height="231" /><figcaption id="caption-attachment-12053" class="wp-caption-text">Case-by-Case Development</figcaption></figure>
<p style="padding-left: 30px;"><strong>2) 编程是脑力劳动，急不得</strong>。这个事情在这做这些题的时候你就会发现，要么是脑子转不过来了，要么就是明明就差一点了，但程序怎么都调不对。如果你越着急的话，你就会发现你会离目标越远，而花的时间也会更多。另外，你会发现这些题基本上都是50行代码内就可以搞定的，但是为了这50行以内的代码，你要花好多时间和精力。coding  50行代码在我们的日常工作中分分钟就完成，而Leetcode里的50行代码却没那么简单，也许，用这个你就可以区别什么是码农，什么是程序员了。</p>
<p style="padding-left: 30px;"><strong>3）加班要不得。</strong>因为我总是在晚上10点以后做题，所以，基本上都是在加班状态中工作。这种状态过上两三天，你就会发现，整个大脑已经不转了，而且不但不转，还会犯很多低级错误，很多事情都想不清楚，一个晚上都在和程序的状态控制做搏斗，代码写得越来越乱，越来越没条理。于是这种时候，我都会休息几天，不做题了，然后再做题的时候，就觉得非常地清楚。可见加班 是编程最致命的敌人！</p>
<p>我把我的C++代码放到了Github上，大家也帮我review一下，看看有没有可以改善的。</p>
<p style="text-align: center;"><strong><a href="https://github.com/haoel/leetcode" target="_blank">https://github.com/haoel/leetcode</a></strong></p>
<p>好了，不多说了，<strong>我希望大家有时间都去练练LeetCode，无论是找工作还是对你的编程能力会有非常大的提高</strong>。</p>
<p>&nbsp;</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/8138.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2012/08/250px-Sheldon_Cooper-150x150.jpg" alt="为什么我反对纯算法面试题" width="150" height="150" /></a><a href="https://coolshell.cn/articles/8138.html" class="wp_rp_title">为什么我反对纯算法面试题</a></li><li ><a href="https://coolshell.cn/articles/19612.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2019/07/1920px-Margaret_Hamilton_-_restoration-e1563697198766-1-150x150.jpg" alt="50年前的登月程序和程序员有多硬核" width="150" height="150" /></a><a href="https://coolshell.cn/articles/19612.html" class="wp_rp_title">50年前的登月程序和程序员有多硬核</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/18024.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg" alt="API设计原则 &#8211; Qt官网的设计实践总结" width="150" height="150" /></a><a href="https://coolshell.cn/articles/18024.html" class="wp_rp_title">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href="https://coolshell.cn/articles/9543.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2013/04/weibo-150x150.jpg" alt="“C++的数组不支持多态”？" width="150" height="150" /></a><a href="https://coolshell.cn/articles/9543.html" class="wp_rp_title">“C++的数组不支持多态”？</a></li><li ><a href="https://coolshell.cn/articles/8790.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2012/12/choice-150x150.jpg" alt="程序算法与人生选择" width="150" height="150" /></a><a href="https://coolshell.cn/articles/8790.html" class="wp_rp_title">程序算法与人生选择</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/12052.html">Leetcode 编程训练</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/12052.html/feed</wfw:commentRss>
			<slash:comments>96</slash:comments>
		
		
			</item>
		<item>
		<title>开发团队的效率</title>
		<link>https://coolshell.cn/articles/11656.html</link>
					<comments>https://coolshell.cn/articles/11656.html#comments</comments>
		
		<dc:creator><![CDATA[陈皓]]></dc:creator>
		<pubDate>Mon, 09 Jun 2014 01:06:11 +0000</pubDate>
				<category><![CDATA[技术管理]]></category>
		<category><![CDATA[流程方法]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Project]]></category>
		<category><![CDATA[效率]]></category>
		<category><![CDATA[程序员]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=11656</guid>

					<description><![CDATA[<p>我之前写过一篇叫《加班与效率》的文章，从概念上说了一些我对“效率”的认识，但是那篇文章趋于概念化，对于一些没有经历过这样的环境的同学来说，可能会觉得太抽象了。很...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/11656.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/11656.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><img decoding="async" loading="lazy" class="alignright size-full wp-image-11700" src="https://coolshell.cn/wp-content/uploads/2014/06/software_development.png" alt="" width="230" height="231" srcset="https://coolshell.cn/wp-content/uploads/2014/06/software_development.png 230w, https://coolshell.cn/wp-content/uploads/2014/06/software_development-150x150.png 150w, https://coolshell.cn/wp-content/uploads/2014/06/software_development-200x200.png 200w" sizes="(max-width: 230px) 100vw, 230px" />我之前写过一篇叫《<a title="加班与效率" href="https://coolshell.cn/articles/10217.html" target="_blank">加班与效率</a>》的文章，从概念上说了一些我对“效率”的认识，但是那篇文章趋于概念化，对于一些没有经历过这样的环境的同学来说，可能会觉得太抽象了。很早以前就想写一篇更具体一点的，可执行的文章与《<a title="加班与效率" href="https://coolshell.cn/articles/10217.html" target="_blank">加班与效率</a>》这篇文章相辉映，并再把我两年前在杭州QCon上的那个<a href="http://vdisk.weibo.com/s/gN-sQ/1351485199" target="_blank">“<strong>鼓吹工程师文化”的《建一支强大的小团队》</strong></a>（新浪微盘）的观点再加强一下。</p>
<p><strong>但是我遇到了一些思维方式上的麻烦——我讲的总是从我的经历背景出发，没有从其它人的经历背景来讲</strong>。这就好像，我在酷壳里说了很多东西（比如：<a title="我们需要专职的QA吗？" href="https://coolshell.cn/articles/6994.html" target="_blank">专职的QA</a>，<a title="从Code Review 谈如何做技术" href="https://coolshell.cn/articles/11432.html" target="_blank">Code Review很重要</a>，<a title="编程能力与编程年龄" href="https://coolshell.cn/articles/10688.html" target="_blank">编程年龄</a>，<a title="来信， 创业 和 移动互联网" href="https://coolshell.cn/articles/5815.html" target="_blank">创业的</a>，<a title="《Rework》摘录及感想" href="https://coolshell.cn/articles/9156.html" target="_blank">Rework</a>的……），有好些人觉得是不可能甚至太理想，其实我说的那些东西都是实实在在存在的，也是我所经历过的。于是，不同的经历，不同的环境，不同的眼界，造成了——有些人不理解我说的，而我也不能理解他们所说的。</p>
<p>所以，过去的这段时间我一有机会就找一些人交流并观察一些身边的事情，并去试着跟从和理解那些我不能理解的东西。现在觉得差不多了，所以，写下了这篇文章。（但越是去理解对方，我就越坚持我的观点，所以这篇文章可能还是会出现鸡同鸭讲的情形，无所谓了）</p>
<p>本文不讨论任何业务上的效率问题，只讨论软件开发或是软件工程中的效率问题。虽然产品和业务上的效率问题是根本，但是因为本文不是拉仇恨的，我也不想混在一起谈，所以请原谅我在这里先说开发团队的，以后重新开篇文章专门谈产品和业务的。</p>
<p>我下面会罗列几个非常典型的开发方式——<strong>软件开发中的“锁”</strong>，<strong>接力棒式软件开发</strong>，<strong>保姆式软件开发</strong>，<strong>WatchDog软件开发</strong>，<strong>故障驱动式软件开发</strong>。</p>
<p><span id="more-11656"></span></p>
<h4>软件开发中的“锁”</h4>
<p>如果你搞过并发编程，你一定知道什么是“锁”，锁就是用来同步和互斥。我发现有好些开发部门里的各个开发团队间存在很多锁。比如：</p>
<ul>
<li><strong>技术能力上的锁</strong>。有一个项目需要在不同的地方做开发，这些模块用到不同的技术，比如：Java, C/C++, Python，Javascript，但是，这个团队里的每一个开发人员就只懂一门语言，于是，需要配合，需要任务排期，同步互斥锁就很多，于是，一个本来只需要2个人干3周的的工作变成了8个人干两个月。</li>
</ul>
<ul>
<li><strong>负责模块上的锁</strong>。同理，不同的人负责不同的模块，于是一个项目要动好多模块，那么你就需要把这些模块的人找过来，和上面一样。每个人都有自己的时间安排，人越多，锁越多。于是，一个来来只需要2个人干2两周的事，变成了7、8个人干一个多月。</li>
</ul>
<p>我上面并非瞎扯，这都是事实。我们可以看到，</p>
<ul>
<li><strong>时间锁、进度锁</strong>。这堆有不同技能或是负责不同模块的开发人员有锁，有锁你就要等，他们有自己的安排，所以，要协作起来，你就需要排期，去同步。而参与的人越多，你的锁就越多。你协调他们的时间就更复杂。</li>
</ul>
<ul>
<li><strong>沟通锁、利益锁</strong>。而且，最恐怖的事情是，他们之间的沟通成本巨大。他们会花大量的时间在讨论，一个功能是实现在你那边，还是我这边，每个人都有自己的利益和算盘。无形中增加了很多推诿、官僚和政治上的东西。</li>
</ul>
<p>有时候，我们会觉得分工和分模块是产生效率的前提，但是实际情况并不是这样。我们也可以看到，<strong>所谓的“分工”被彻彻底底的滥用了</strong>。他们把“分工”当成了永远只干一件事的借口。</p>
<h5>【解决方案】</h5>
<p><strong>一个程序员应该能够掌握多个语言，也能够负责多个模块甚至不同的职责。如果一个程序员觉得多学习一门语言，多掌握一个模块是件很困难的事，那么这个程序员本质上是不合格的</strong>。</p>
<h4>“接力棒式”软件开发</h4>
<p><strong>在有各种“工作锁”的软件开发团队里，一般都无法避免“接力棒式”的开发</strong>。也就是说，底层的C程序员干完了，交给上层的Java程序员，然后再交给更上层的前端程序员，最后再交给运维人员。这就是接力棒式的开发。</p>
<p>而且，更糟糕的是，如果在引入了软件流程下，这种“接力棒的方式”真是会把你搞崩溃的。比如下游团队开发一个月，交给QA测试一个月，再交给运维分步上线一个月，然后，上游团队拿到下游开发的API后开发一个月，再交给自己的QA测试一个月，然后再交给自己的运维上线一个月，于是，半年就这样过去了。<strong>这是一个由一个一个小瀑布叠出来的一个大瀑布</strong>。</p>
<p>哦，你会说，这个好办啊，上下游不会先商定好接口么？然后做并行开发么？是的，这是其中的一个优化方式，但是需要很好的接口设计。但是，在实际过程中，你会发现（这时我并非信口开河，我说的都是事实），</p>
<ul>
<li>如果这两个上下游团队在一起还好办，要是不在一起，那么，实际情况是，后面的团队会等到前面的团队提测了，才开始开发，本质上就是串行开发的。</li>
</ul>
<ul>
<li>如果有更多的团队呢？比如：A团队 -&gt; B团队 -&gt; C团队 -&gt;D团队呢。接口就变得非常地关键了。而在实际情况下，因为没有好的接口设计人员，所以，在开发过程经常性地修改接口，或者是因为接口不好用也只得忍着。</li>
</ul>
<h5>【解决方案】</h5>
<p>我以前写过一篇叫《<a title="IoC/DIP其实是一种管理思想" href="https://coolshell.cn/articles/9949.html" target="_blank" rel="bookmark">IoC/DIP其实是一种管理思想</a>》，对于这种接力棒的方式，应该反过来，<strong>如果业务应用团队是A团队，那B/C/D团队应该把自己的做成一个开发框架也好，服务化也好，让应用团队自己来接入</strong>。比如：前端做好一个前端开发框架，PE做好一个运维开发框架、各种工具，共享模块团队做好开发框架，让应用团队自己来接入，而不是帮他做。<strong>你会发现，在这么多团队各自P2P勾兑出来的很随意的接口的所带来的成本已经远超过一个统一标准的协议</strong>。</p>
<h4>“保姆式”软件开发</h4>
<p>所谓“保姆式”软件开发就是——我只管吃饭，不管做菜洗碗，就像——衣来伸手，饭来张口的“小皇帝”一样，身边有一堆太监或宫女，不然生活不能自理。这种情况经常见于开发和测试，开发和运维间的关系。很多公司，测试和运维都成了开发的保姆。</p>
<p>我就能看到，很多开发快速写完代码后基本上都不怎么测试就交给QA去测试了，QA一测，我草，各种问题，而只会做黑盒的QA并不能马上就能确定是代码的问题还是环境的问题，所以还要花大量时间排除不是环境问题，才给开发报BUG。很多问题，可能只需要做个Code Review，做个单测就可以发现了，硬要交给QA。运维也是一样的，开发出来的软件根本就没有考虑什么运维的东西，因为有运维人员，所以我才不考虑呢。</p>
<p><strong>这和我们带孩子的道理是一样的，对于孩子来说，如果父母帮孩子做得越多，孩子就越觉得理所应当，就越不会去做</strong>。</p>
<p><strong>“保姆式”开发一般会进化成“保安式”开发</strong>。</p>
<ul>
<li>因为你的团队开发人员的能力不行，设计不行，Code Reivew/UT不做，你就只能找堆QA看着他。</li>
<li>因为Dev/QA只管功能不管运维，所以，还得找堆运维人员看着他们。</li>
<li>因为你的技术人员不懂业务，不懂需求，需要再找个BA，找个产品经理来指挥他。</li>
<li>因为你的技术人员不会管理项目，所以，再搞个项目经理，找个敏捷教练、以及SQA来管着他。</li>
</ul>
<p><strong>就这样，你不行，我找人来看着你，看你的人不行，我再找人来看着看你的人……层层保姆，层层保安。</strong>于是，你就会发现，团队或部门里的人员越来越多，你整天都在开会，整天都在互相解释，互相争吵，会扯淡的人越来越多。那还有个屁的效率。</p>
<p style="text-align: center;"><img decoding="async" loading="lazy" class="aligncenter size-full wp-image-11702" src="https://coolshell.cn/wp-content/uploads/2014/06/worker.jpg" alt="" width="523" height="499" srcset="https://coolshell.cn/wp-content/uploads/2014/06/worker.jpg 523w, https://coolshell.cn/wp-content/uploads/2014/06/worker-300x286.jpg 300w" sizes="(max-width: 523px) 100vw, 523px" />网络上一个非常经典的图片，来源不详，程序员在挖坑，其它人站在当监工</p>
<h5>【解决方案】</h5>
<p style="padding-left: 30px;">1）<strong>不要招只会写代码的“码农”，要招懂“需求”，注重“软件工程”和“软件质量”和“软件维护”的“工程师”</strong>。</p>
<p style="padding-left: 30px;">2）<strong>最好的管理，不是找人来管人，而是自己管自己</strong>。</p>
<p style="padding-left: 30px;">3）<strong>组织和团队中支持性工作的人越少越好，最好不要</strong>。</p>
<p style="padding-left: 30px;">4）<strong>服务化。我服务于你并不代表我要帮你干活，而是代表——我要让你干活干得更爽</strong>。</p>
<p>我在<a href="http://weibo.com/1401880315/B6hC7elDb" target="_blank">微博</a>上说过下面的话，（大家可以体会一下保姆和服务的差别）</p>
<p style="padding-left: 30px;">运维要用“云服务”的思路去做。如果一个公司内的运维团队开发出一堆工具，让做应用开发团队可以很容易地申请机器、存储、网络、中间件、安全等资源，并很容易管理、监控和部署应用，并提供运维资询。而不是帮应用开发团队干活擦屁股当保姆。那么，这个公司就会不经意地做出一个云计算平台来了。</p>
<p>&nbsp;</p>
<h4>“WatchDog式”软件开发</h4>
<p>什么是WatchDog？就是说——<strong>为了解决某个系统的问题，我要用一个新的系统去看着它</strong>。</p>
<ul>
<li>我的系统架构太复杂，出了问题不好查找。咋办？那就搞个专门的特殊的监控系统吧……</li>
</ul>
<ul>
<li>我的系统配置太复杂，容易配错了。咋办？那就加一个配置校验系统吧……</li>
</ul>
<ul>
<li>我的系统配置和真实的情况有时候可能会不一性。咋办？那就加一个巡检系统吧……</li>
</ul>
<ul>
<li>我的系统测试环境和线上环境有时候会搞混了。咋办？那就为线上环境加一个权限控制系统吧……</li>
</ul>
<ul>
<li>我的系统有单点，那就加个负载均衡器吧，负载均衡器的单点呢？那就再加个等价路由器吧……</li>
</ul>
<p><strong>做加法谁不会？就不想去简化一样系统吗？就不能不拆东墙补西墙么？</strong>这些了系统加的越来越多，我看你以后怎么运维。</p>
<p>一开始没有想清楚就放到线上，然后，出了故障后，也无法重新设计和重新架构，只能以打补丁地方式往上打，这就好像一个本来就有缺陷的楼没有盖好，你要拆了重盖是不可能的，也只能不停地打补丁了。字是一只狗，越描越丑。</p>
<h5>【解决方案】</h5>
<p style="padding-left: 30px;"><strong>1）设计想好了再做，多评估几个设计没坏处，简化，简化，简化。</strong></p>
<p style="padding-left: 30px;"><strong>2）残酷无情地还债，就算是CEO来了，也无法阻止我还债的脚步。</strong></p>
<p>&nbsp;</p>
<h4>“故障驱动式”软件开发</h4>
<p>WatchDog式的软件开发通常来说都是“故障驱动式”软件开发的产物。这种开发方式其实就是在表明自己智力和能力的不足。以上线为目的，上了线再说，有什么问题出了再改。</p>
<p>上面的老大或是业务方基本上会说，没关系，我们不一开始并不需要一个完美的系统，你先上了再说，先解业务的渴，我们后面有时间再重构再完善。而有的技术人员也会用“架构和设计是逐步演化出来的”这句话来证明“故障驱动”开发是值得的。</p>
<p>我同意逐步迭代以及架构演化论，但是，我觉得<strong>“系统迭代说”和“架构演化论”被彻彻底底地成为那些能力有限甚至不学无术的人的超级借口</strong>。</p>
<p>你们有没有搞错啊？你们知道什么叫迭代，什么叫演化吗？你们知道，要定位一个线上的故障需要花多大的力气吗？（<a href="http://blog.aliyun.com/341" target="_blank">看看这篇文章</a>你就知道了）你们知道，随随便便去应付局部上你会快，但总体上来说你会慢。</p>
<p>虽然，我看到那些系统在一个又一个的故障后得到一点又一点的改善，但是我想说，为什么一开始不认真不严谨一点呢？我从来就没有见过一个精良的系统是靠一个一个的故障和失败案例给堆出来的，就算是Windows 95/98这样史上最烂的操作系统，如果没有设计精良Windows NT的补位，Windows也早玩完了（看看IE的下场就知道了）。</p>
<h5>【解决方案】</h5>
<p style="padding-left: 30px;"><strong>1）基础知识和理论知识非常重要</strong>。多多使用已有的成熟的方案是关键。</p>
<p style="padding-left: 30px;"><strong>2）对技术要有一颗严谨和敬畏的心。想清楚了再干，坚持高标准，Design for failure! </strong>很多事情都急不得。</p>
<p>&nbsp;</p>
<h4>其它开发方式</h4>
<p>其实，这样的事情还有很多。比如：</p>
<p><strong>1）配置管理上的问题</strong>。对于源代码的配置管理，其实并不是一件简单的事情。配置管理和软件和团队的组构的结构非常有关系。我看到过两种非常没有效率的配置管理，一种是以开项目分支的方式来做项目，同时开很多分支，分支开的时间还很长，导致merge回主干要花大量的时间去解决各种冲突，另一种是N多的团队都在一个代码库中做修改，导致出现很多复杂的问题，比如某团队的改动出现了一个bug，要么所有的团队的功能都得等这个bug被修复才能被发布，要么就是把所有的改动回滚到上一个版本，包括其它团队开发的功能。很明显，软件模块的结构，软件的架构，以及团队的组织形式都会严重影响开发效率。</p>
<p><strong>2）人肉式的软件开发</strong>。大多数的软件团队和主管都会用“人手不够”做为自己开发效率不够的借口，而大多数故障发生的时候，都会使用更重的“人肉流程”来弥补自己能力的不足。他们从来没有想过使用“技术”，使用更“聪明”的方式来解决问题。</p>
<p><strong>3）会议驱动式开发</strong>。人多了，团队多了，想法也就多了，沟通也就多了，于是需要不停得开会开会开会。</p>
<p>&nbsp;</p>
<h4>总结一下</h4>
<p>综上所述，我有如下总结：</p>
<p style="padding-left: 30px;">1）<strong>软件工程师分工分得越细这个团队就越没效率，团队间的服务化是关键的关键</strong>。不管是从语言上还是从软件模块上的人员分工，越细越糟糕。服务化不是我要帮你做事，而是我让你做起事来更容易。</p>
<p style="padding-left: 30px;">2）<strong>你总需要在一个环节上认真，这个环节越往前就越有效率，越往后你就越没效率</strong>。要么你设计和编码认真点，不然，你就得在测试上认真点。要是你设计、编码、测试都不认真，那你就得在运维上认真，就得在处理故障上认真。你总需要在一个地方认真。另外一篇文章你可以看一下——《<a title="多些时间能少写些代码" href="https://coolshell.cn/articles/5686.html" target="_blank">多些时间少写些代码</a>》</p>
<p style="padding-left: 30px;">3）<strong>“小而精的团队”+“条件和资源受限”是效率的根本</strong>。只有团队小，内耗才会小，只有条件或资源受限，才会逼着你去用最经济的手段做最有价值的事，才会逼着你喜欢简单和简化。</p>
<p style="padding-left: 30px;">4）<strong>技术债是不能欠的，要残酷无情地还债</strong>。很多事情，一开始不会有，那么就永远不会有。一旦一个事情烂了，后面只能跟着一起烂，烂得越多，就越没有人敢去还债。</p>
<p style="padding-left: 30px;">5）<strong>软件架构上要松耦合，团队组织上要紧耦合</strong>。</p>
<p style="padding-left: 30px;">6）<strong>工程师文化是关键，重视过程就是重视结果</strong>。只重视结果的KPI等同于“竭泽而渔”和“饮鸩止渴”。</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/19612.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2019/07/1920px-Margaret_Hamilton_-_restoration-e1563697198766-1-150x150.jpg" alt="50年前的登月程序和程序员有多硬核" width="150" height="150" /></a><a href="https://coolshell.cn/articles/19612.html" class="wp_rp_title">50年前的登月程序和程序员有多硬核</a></li><li ><a href="https://coolshell.cn/articles/12052.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg" alt="Leetcode 编程训练" width="150" height="150" /></a><a href="https://coolshell.cn/articles/12052.html" class="wp_rp_title">Leetcode 编程训练</a></li><li ><a href="https://coolshell.cn/articles/11170.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2014/03/42-150x150.jpg" alt="如何用最有创造力的方式输出42" width="150" height="150" /></a><a href="https://coolshell.cn/articles/11170.html" class="wp_rp_title">如何用最有创造力的方式输出42</a></li><li ><a href="https://coolshell.cn/articles/10217.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2013/07/Work-Overtime-150x150.jpg" alt="加班与效率" width="150" height="150" /></a><a href="https://coolshell.cn/articles/10217.html" class="wp_rp_title">加班与效率</a></li><li ><a href="https://coolshell.cn/articles/8387.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2012/10/Learnable_Programming-150x150.jpg" alt="Bret Victor &#8211; Learnable Programming" width="150" height="150" /></a><a href="https://coolshell.cn/articles/8387.html" class="wp_rp_title">Bret Victor &#8211; Learnable Programming</a></li><li ><a href="https://coolshell.cn/articles/4951.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/19.jpg" alt="软件公司的两种管理方式" width="150" height="150" /></a><a href="https://coolshell.cn/articles/4951.html" class="wp_rp_title">软件公司的两种管理方式</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/11656.html">开发团队的效率</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/11656.html/feed</wfw:commentRss>
			<slash:comments>153</slash:comments>
		
		
			</item>
		<item>
		<title>Python修饰器的函数式编程</title>
		<link>https://coolshell.cn/articles/11265.html</link>
					<comments>https://coolshell.cn/articles/11265.html#comments</comments>
		
		<dc:creator><![CDATA[陈皓]]></dc:creator>
		<pubDate>Thu, 20 Mar 2014 01:50:34 +0000</pubDate>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[程序设计]]></category>
		<category><![CDATA[编程语言]]></category>
		<category><![CDATA[Decorator]]></category>
		<category><![CDATA[functional]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[函数式]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=11265</guid>

					<description><![CDATA[<p>Python的修饰器的英文名叫Decorator，当你看到这个英文名的时候，你可能会把其跟Design Pattern里的Decorator搞混了，其实这是完全...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/11265.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/11265.html">Python修饰器的函数式编程</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><img decoding="async" loading="lazy" class="alignright wp-image-11300" src="https://coolshell.cn/wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960.jpg" alt="" width="280" height="233" srcset="https://coolshell.cn/wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960.jpg 350w, https://coolshell.cn/wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960-300x249.jpg 300w, https://coolshell.cn/wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960-325x270.jpg 325w" sizes="(max-width: 280px) 100vw, 280px" />Python的修饰器的英文名叫Decorator，当你看到这个英文名的时候，你可能会把其跟Design Pattern里的Decorator搞混了，其实这是完全不同的两个东西。虽然好像，他们要干的事都很相似——都是想要对一个已有的模块做一些“修饰工作”，所谓修饰工作就是想给现有的模块加上一些小装饰（一些小功能，这些小功能可能好多模块都会用到），但又不让这个小装饰（小功能）侵入到原有的模块中的代码里去。但是OO的Decorator简直就是一场恶梦，不信你就去看看wikipedia上的词条（<a href="http://en.wikipedia.org/wiki/Decorator_pattern" target="_blank" rel="noopener noreferrer">Decorator Pattern</a>）里的UML图和那些代码，这就是我在《 <a title="链接：从面向对象的设计模式看软件设计" href="https://coolshell.cn/articles/8961.html" rel="bookmark">从面向对象的设计模式看软件设计</a>》“餐后甜点”一节中说的，OO鼓励了——“厚重地胶合和复杂层次”，也是《 <a title="链接：如此理解面向对象编程" href="https://coolshell.cn/articles/8745.html" rel="bookmark">如此理解面向对象编程</a>》中所说的“OO的狂热者们非常害怕处理数据”，Decorator Pattern搞出来的代码简直就是OO的反面教程。</p>
<p>Python 的 Decorator在使用上和Java/C#的Annotation很相似，就是在方法名前面加一个@XXX注解来为这个方法装饰一些东西。但是，Java/C#的Annotation也很让人望而却步，太TMD的复杂了，你要玩它，你需要了解一堆Annotation的类库文档，让人感觉就是在学另外一门语言。</p>
<p>而Python使用了一种相对于Decorator Pattern和Annotation来说非常优雅的方法，这种方法不需要你去掌握什么复杂的OO模型或是Annotation的各种类库规定，完全就是语言层面的玩法：一种函数式编程的技巧。如果你看过本站的《<a href="https://coolshell.cn/articles/10822.html">函数式编程</a>》，你一定会为函数式编程的那种“描述你想干什么，而不是描述你要怎么去实现”的编程方式感到畅快。（如果你不了解函数式编程，那在读本文之前，还请你移步去看看《<a href="https://coolshell.cn/articles/10822.html">函数式编程</a>》） 好了，我们先来点感性认识，看一个Python修饰器的Hello World的代码。</p>
<p><span id="more-11265"></span></p>
<h4>Hello World</h4>
<p>下面是代码（文件名：hello.py）：</p>
<pre class="EnlighterJSRAW" data-enlighter-language="golang" data-enlighter-highlight="1-6,8">def hello(fn):
    def wrapper():
        print "hello, %s" % fn.__name__
        fn()
        print "goodby, %s" % fn.__name__
    return wrapper

@hello
def foo():
    print "i am foo"

foo()
</pre>
<p>当你运行代码，你会看到如下输出：</p>
<pre class="EnlighterJSRAW" data-enlighter-language="shell">[chenaho@chenhao-air]$ python hello.py
hello, foo
i am foo
goodby, foo</pre>
<p>你可以看到如下的东西：</p>
<p style="padding-left: 30px;">1）函数foo前面有个@hello的“注解”，hello就是我们前面定义的函数hello</p>
<p style="padding-left: 30px;">2）在hello函数中，其需要一个fn的参数（这就用来做回调的函数）</p>
<p style="padding-left: 30px;">3）hello函数中返回了一个inner函数wrapper，这个wrapper函数回调了传进来的fn，并在回调前后加了两条语句。</p>
<h4>Decorator 的本质</h4>
<p>对于Python的这个@注解语法糖- Syntactic Sugar 来说，当你在用某个@decorator来修饰某个函数func时，如下所示:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="python">@decorator
def func():
    pass
</pre>
<p>其解释器会解释成下面这样的语句：</p>
<pre class="EnlighterJSRAW" data-enlighter-language="python">func = decorator(func)</pre>
<p>尼玛，这不就是把一个函数当参数传到另一个函数中，然后再回调吗？是的，但是，我们需要注意，那里还有一个赋值语句，把decorator这个函数的返回值赋值回了原来的func。 根据《<a href="https://coolshell.cn/articles/10822.html" target="_blank" rel="noopener noreferrer">函数式编程</a>》中的<strong>first class functions</strong>中的定义的，你可以把函数当成变量来使用，所以，decorator必需得返回了一个函数出来给func，这就是所谓的<strong>higher order function </strong>高阶函数，不然，后面当func()调用的时候就会出错。 就我们上面那个hello.py里的例子来说，</p>
<pre class="EnlighterJSRAW" data-enlighter-language="python">@hello
def foo():
    print "i am foo"
</pre>
<p>被解释成了：</p>
<pre class="EnlighterJSRAW" data-enlighter-language="python"> foo = hello(foo)</pre>
<p><strong>是的，这是一条语句，而且还被执行了。</strong>你如果不信的话，你可以写这样的程序来试试看：</p>
<pre class="EnlighterJSRAW" data-enlighter-language="python">def fuck(fn):
    print "fuck %s!" % fn.__name__[::-1].upper()

@fuck
def wfg():
    pass
</pre>
<p>没了，就上面这段代码，没有调用wfg()的语句，你会发现， fuck函数被调用了，而且还很NB地输出了我们每个人的心声！</p>
<p>再回到我们hello.py的那个例子，我们可以看到，<strong>hello(foo)返回了wrapper()函数，所以，foo其实变成了wrapper的一个变量，而后面的foo()执行其实变成了wrapper()</strong>。</p>
<p>知道这点本质，当你看到有多个decorator或是带参数的decorator，你也就不会害怕了。</p>
<p>比如：多个decorator</p>
<pre class="EnlighterJSRAW" data-enlighter-language="python">@decorator_one
@decorator_two
def func():
    pass</pre>
<p>相当于：</p>
<pre class="EnlighterJSRAW" data-enlighter-language="python">func = decorator_one(decorator_two(func))</pre>
<p>比如：带参数的decorator：</p>
<pre class="EnlighterJSRAW" data-enlighter-language="python">@decorator(arg1, arg2)
def func():
    pass</pre>
<p>相当于：</p>
<pre class="EnlighterJSRAW" data-enlighter-language="python">func = decorator(arg1,arg2)(func)</pre>
<p>这意味着decorator(arg1, arg2)这个函数需要返回一个“真正的decorator”。</p>
<h4>带参数及多个Decrorator</h4>
<p>我们来看一个有点意义的例子（文件名：html.py）：</p>
<p>在上面这个例子中，我们可以看到：makeHtmlTag有两个参数。所以，<strong>为了让 <span style="color: #000080;">hello = makeHtmlTag(arg1, arg2)(hello)</span> 成功，makeHtmlTag 必需返回一个decorator</strong>（这就是为什么我们在makeHtmlTag中加入了real_decorator()的原因）<strong>，这样一来，我们就可以进入到 decorator 的逻辑中去了</strong>—— decorator得返回一个wrapper，wrapper里回调hello。<strong>看似那个makeHtmlTag() 写得层层叠叠，但是，已经了解了本质的我们觉得写得很自然</strong>。 你看，Python的Decorator就是这么简单，没有什么复杂的东西，你也不需要了解过多的东西，使用起来就是那么自然、体贴、干爽、透气，独有的速效凹道和完美的吸收轨迹，让你再也不用为每个月的那几天感到焦虑和不安，再加上贴心的护翼设计，量多也不用当心。对不起，我调皮了。 什么，你觉得上面那个带参数的Decorator的函数嵌套太多了，你受不了。好吧，没事，我们看看下面的方法。</p>
<h4>class式的 Decorator</h4>
<p>首先，先得说一下，decorator的class方式，还是看个示例：</p>
<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-highlight="3,7">class myDecorator(object):

    def __init__(self, fn):
        print "inside myDecorator.__init__()"
        self.fn = fn

    def __call__(self):
        self.fn()
        print "inside myDecorator.__call__()"

@myDecorator
def aFunction():
    print "inside aFunction()"

print "Finished decorating aFunction()"

aFunction()

# 输出：
# inside myDecorator.__init__()
# Finished decorating aFunction()
# inside aFunction()
# inside myDecorator.__call__()</pre>
<p>上面这个示例展示了，用类的方式声明一个decorator。我们可以看到这个类中有两个成员：<br />
1）一个是<strong>init</strong>()，这个方法是在我们给某个函数decorator时被调用，所以，需要有一个fn的参数，也就是被decorator的函数。<br />
2）一个是<strong>call</strong>()，这个方法是在我们调用被decorator函数时被调用的。<br />
上面输出可以看到整个程序的执行顺序。</p>
<p>这看上去要比“函数式”的方式更易读一些。</p>
<p>下面，我们来看看用类的方式来重写上面的html.py的代码（文件名：html.py）：</p>
<pre class="EnlighterJSRAW" data-enlighter-language="python">class makeHtmlTagClass(object):

    def __init__(self, tag, css_class=""):
        self._tag = tag
        self._css_class = " class='{0}'".format(css_class) \
                                       if css_class !="" else ""

    def __call__(self, fn):
        def wrapped(*args, **kwargs):
            return "&lt;" + self._tag + self._css_class+"&gt;"  \
                       + fn(*args, **kwargs) + "&lt;/" + self._tag + "&gt;"
        return wrapped

@makeHtmlTagClass(tag="b", css_class="bold_css")
@makeHtmlTagClass(tag="i", css_class="italic_css")
def hello(name):
    return "Hello, {}".format(name)

print hello("Hao Chen")
</pre>
<p>上面这段代码中，我们需要注意这几点：<br />
1）如果decorator有参数的话，<strong>init</strong>() 成员就不能传入fn了，而fn是在<strong>call</strong>的时候传入的。<br />
2）这段代码还展示了 wrapped(*args, **kwargs) 这种方式来传递被decorator函数的参数。（其中：args是一个参数列表，kwargs是参数dict，具体的细节，请参考Python的文档或是<a href="http://stackoverflow.com/questions/3394835/args-and-kwargs" target="_blank" rel="noopener noreferrer">StackOverflow的这个问题</a>，这里就不展开了）</p>
<h4>用Decorator设置函数的调用参数</h4>
<p>你有三种方法可以干这个事：</p>
<p>第一种，通过 **kwargs，这种方法decorator会在kwargs中注入参数。</p>
<pre class="EnlighterJSRAW" data-enlighter-language="python">def decorate_A(function):
    def wrap_function(*args, **kwargs):
        kwargs['str'] = 'Hello!'
        return function(*args, **kwargs)
    return wrap_function

@decorate_A
def print_message_A(*args, **kwargs):
    print(kwargs['str'])

print_message_A()</pre>
<p>第二种，约定好参数，直接修改参数</p>
<pre class="EnlighterJSRAW" data-enlighter-language="python">def decorate_B(function):
    def wrap_function(*args, **kwargs):
        str = 'Hello!'
        return function(str, *args, **kwargs)
    return wrap_function

@decorate_B
def print_message_B(str, *args, **kwargs):
    print(str)

print_message_B()</pre>
<p>第三种，通过 *args 注入</p>
<pre class="EnlighterJSRAW" data-enlighter-language="python">def decorate_C(function):
    def wrap_function(*args, **kwargs):
        str = 'Hello!'
        #args.insert(1, str)
        args = args +(str,)
        return function(*args, **kwargs)
    return wrap_function

class Printer:
    @decorate_C
    def print_message(self, str, *args, **kwargs):
        print(str)

p = Printer()
p.print_message()</pre>
<h4>Decorator的副作用</h4>
<p>到这里，我相信你应该了解了整个Python的decorator的原理了。</p>
<p>相信你也会发现，被decorator的函数其实已经是另外一个函数了，对于最前面那个hello.py的例子来说，如果你查询一下foo.<strong>name</strong>的话，你会发现其输出的是“wrapper”，而不是我们期望的“foo”，这会给我们的程序埋一些坑。所以，Python的functool包中提供了一个叫wrap的decorator来消除这样的副作用。下面是我们新版本的 hello.py。</p>
<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-highlight="1,3">from functools import wraps
def hello(fn):
    @wraps(fn)
    def wrapper():
        print "hello, %s" % fn.__name__
        fn()
        print "goodby, %s" % fn.__name__
    return wrapper

@hello
def foo():
    '''foo help doc'''
    print "i am foo"
    pass

foo()
print foo.__name__ #输出 foo
print foo.__doc__  #输出 foo help doc
</pre>
<p>当然，即使是你用了functools的wraps，也不能完全消除这样的副作用。</p>
<p>来看下面这个示例：</p>
<pre class="EnlighterJSRAW" data-enlighter-language="python">from inspect import getmembers, getargspec
from functools import wraps

def wraps_decorator(f):
    @wraps(f)
    def wraps_wrapper(*args, **kwargs):
        return f(*args, **kwargs)
    return wraps_wrapper

class SomeClass(object):
    @wraps_decorator
    def method(self, x, y):
        pass

obj = SomeClass()
for name, func in getmembers(obj, predicate=inspect.ismethod):
    print "Member Name: %s" % name
    print "Func Name: %s" % func.func_name
    print "Args: %s" % getargspec(func)[0]

# 输出：
# Member Name: method
# Func Name: method
# Args: []</pre>
<p>你会发现，即使是你你用了functools的wraps，你在用getargspec时，参数也不见了。</p>
<p>要修正这一问，我们还得用Python的反射来解决，下面是相关的代码：</p>
<pre class="EnlighterJSRAW" data-enlighter-language="python">def get_true_argspec(method):
    argspec = inspect.getargspec(method)
    args = argspec[0]
    if args and args[0] == 'self':
        return argspec
    if hasattr(method, '__func__'):
        method = method.__func__
    if not hasattr(method, 'func_closure') or method.func_closure is None:
        raise Exception("No closure for method.")

    method = method.func_closure[0].cell_contents
    return get_true_argspec(method)</pre>
<p>当然，我相信大多数人的程序都不会去getargspec。所以，用functools的wraps应该够用了。</p>
<h4>一些decorator的示例</h4>
<p>好了，现在我们来看一下各种decorator的例子：</p>
<h5>给函数调用做缓存</h5>
<p>这个例实在是太经典了，整个网上都用这个例子做decorator的经典范例，因为太经典了，所以，我这篇文章也不能免俗。</p>
<pre class="EnlighterJSRAW" data-enlighter-language="python">from functools import wraps
def memo(fn):
    cache = {}
    miss = object()

    @wraps(fn)
    def wrapper(*args):
        result = cache.get(args, miss)
        if result is miss:
            result = fn(*args)
            cache[args] = result
        return result

    return wrapper

@memo
def fib(n):
    if n &lt; 2:
        return n
    return fib(n - 1) + fib(n - 2)
</pre>
<p>上面这个例子中，是一个斐波拉契数例的递归算法。我们知道，这个递归是相当没有效率的，因为会重复调用。比如：我们要计算fib(5)，于是其分解成fib(4) + fib(3)，而fib(4)分解成fib(3)+fib(2)，fib(3)又分解成fib(2)+fib(1)…… 你可看到，基本上来说，fib(3), fib(2), fib(1)在整个递归过程中被调用了两次。</p>
<p>而我们用decorator，在调用函数前查询一下缓存，如果没有才调用了，有了就从缓存中返回值。一下子，这个递归从二叉树式的递归成了线性的递归。</p>
<h5>Profiler的例子</h5>
<p>这个例子没什么高深的，就是实用一些。</p>
<pre class="EnlighterJSRAW" data-enlighter-language="python">import cProfile, pstats, StringIO

def profiler(func):
    def wrapper(*args, **kwargs):
        datafn = func.__name__ + ".profile" # Name the data file
        prof = cProfile.Profile()
        retval = prof.runcall(func, *args, **kwargs)
        #prof.dump_stats(datafn)
        s = StringIO.StringIO()
        sortby = 'cumulative'
        ps = pstats.Stats(prof, stream=s).sort_stats(sortby)
        ps.print_stats()
        print s.getvalue()
        return retval

    return wrapper

</pre>
<h5>注册回调函数</h5>
<p>下面这个示例展示了通过URL的路由来调用相关注册的函数示例：</p>
<pre class="EnlighterJSRAW" data-enlighter-language="python">class MyApp():
    def __init__(self):
        self.func_map = {}

    def register(self, name):
        def func_wrapper(func):
            self.func_map[name] = func
            return func
        return func_wrapper

    def call_method(self, name=None):
        func = self.func_map.get(name, None)
        if func is None:
            raise Exception("No function registered against - " + str(name))
        return func()

app = MyApp()

@app.register('/')
def main_page_func():
    return "This is the main page."

@app.register('/next_page')
def next_page_func():
    return "This is the next page."

print app.call_method('/')
print app.call_method('/next_page')
</pre>
<p>注意：<br />
1）上面这个示例中，用类的实例来做decorator。<br />
2）decorator类中没有<strong>call</strong>()，但是wrapper返回了原函数。所以，原函数没有发生任何变化。</p>
<h5>给函数打日志</h5>
<p>下面这个示例演示了一个logger的decorator，这个decorator输出了函数名，参数，返回值，和运行时间。</p>
<pre class="EnlighterJSRAW" data-enlighter-language="python">from functools import wraps
def logger(fn):
    @wraps(fn)
    def wrapper(*args, **kwargs):
        ts = time.time()
        result = fn(*args, **kwargs)
        te = time.time()
        print "function      = {0}".format(fn.__name__)
        print "    arguments = {0} {1}".format(args, kwargs)
        print "    return    = {0}".format(result)
        print "    time      = %.6f sec" % (te-ts)
        return result
    return wrapper

@logger
def multipy(x, y):
    return x * y

@logger
def sum_num(n):
    s = 0
    for i in xrange(n+1):
        s += i
    return s

print multipy(2, 10)
print sum_num(100)
print sum_num(10000000)</pre>
<p>上面那个打日志还是有点粗糙，让我们看一个更好一点的（带log level参数的）：</p>
<pre class="EnlighterJSRAW" data-enlighter-language="python">import inspect
def get_line_number():
    return inspect.currentframe().f_back.f_back.f_lineno

def logger(loglevel):
    def log_decorator(fn):
        @wraps(fn)
        def wrapper(*args, **kwargs):
            ts = time.time()
            result = fn(*args, **kwargs)
            te = time.time()
            print "function   = " + fn.__name__,
            print "    arguments = {0} {1}".format(args, kwargs)
            print "    return    = {0}".format(result)
            print "    time      = %.6f sec" % (te-ts)
            if (loglevel == 'debug'):
                print "    called_from_line : " + str(get_line_number())
            return result
        return wrapper
    return log_decorator</pre>
<p>但是，上面这个带log level参数的有两具不好的地方，<br />
1） loglevel不是debug的时候，还是要计算函数调用的时间。<br />
2） 不同level的要写在一起，不易读。</p>
<p>我们再接着改进：</p>
<pre class="EnlighterJSRAW" data-enlighter-language="python">import inspect

def advance_logger(loglevel):

    def get_line_number():
        return inspect.currentframe().f_back.f_back.f_lineno

    def _basic_log(fn, result, *args, **kwargs):
        print "function   = " + fn.__name__,
        print "    arguments = {0} {1}".format(args, kwargs)
        print "    return    = {0}".format(result)

    def info_log_decorator(fn):
        @wraps(fn)
        def wrapper(*args, **kwargs):
            result = fn(*args, **kwargs)
            _basic_log(fn, result, args, kwargs)
        return wrapper

    def debug_log_decorator(fn):
        @wraps(fn)
        def wrapper(*args, **kwargs):
            ts = time.time()
            result = fn(*args, **kwargs)
            te = time.time()
            _basic_log(fn, result, args, kwargs)
            print "    time      = %.6f sec" % (te-ts)
            print "    called_from_line : " + str(get_line_number())
        return wrapper

    if loglevel is "debug":
        return debug_log_decorator
    else:
        return info_log_decorator
</pre>
<p>你可以看到两点，<br />
1）我们分了两个log level，一个是info的，一个是debug的，然后我们在外尾根据不同的参数返回不同的decorator。<br />
2）我们把info和debug中的相同的代码抽到了一个叫_basic_log的函数里，DRY原则。</p>
<h5>一个MySQL的Decorator</h5>
<p>下面这个decorator是我在工作中用到的代码，我简化了一下，把DB连接池的代码去掉了，这样能简单点，方便阅读。</p>
<pre class="EnlighterJSRAW" data-enlighter-language="python">import umysql
from functools import wraps

class Configuraion:
    def __init__(self, env):
        if env == "Prod":
            self.host    = "coolshell.cn"
            self.port    = 3306
            self.db      = "coolshell"
            self.user    = "coolshell"
            self.passwd  = "fuckgfw"
        elif env == "Test":
            self.host   = 'localhost'
            self.port   = 3300
            self.user   = 'coolshell'
            self.db     = 'coolshell'
            self.passwd = 'fuckgfw'

def mysql(sql):

    _conf = Configuraion(env="Prod")

    def on_sql_error(err):
        print err
        sys.exit(-1)

    def handle_sql_result(rs):
        if rs.rows &gt; 0:
            fieldnames = [f[0] for f in rs.fields]
            return [dict(zip(fieldnames, r)) for r in rs.rows]
        else:
            return []

    def decorator(fn):
        @wraps(fn)
        def wrapper(*args, **kwargs):
            mysqlconn = umysql.Connection()
            mysqlconn.settimeout(5)
            mysqlconn.connect(_conf.host, _conf.port, _conf.user, \
                              _conf.passwd, _conf.db, True, 'utf8')
            try:
                rs = mysqlconn.query(sql, {})
            except umysql.Error as e:
                on_sql_error(e)

            data = handle_sql_result(rs)
            kwargs["data"] = data
            result = fn(*args, **kwargs)
            mysqlconn.close()
            return result
        return wrapper

    return decorator

@mysql(sql = "select * from coolshell" )
def get_coolshell(data):
    ... ...
    ... ..
</pre>
<h5>线程异步</h5>
<p>下面量个非常简单的异步执行的decorator，注意，异步处理并不简单，下面只是一个示例。</p>
<pre class="EnlighterJSRAW" data-enlighter-language="python">from threading import Thread
from functools import wraps

def async(func):
    @wraps(func)
    def async_func(*args, **kwargs):
        func_hl = Thread(target = func, args = args, kwargs = kwargs)
        func_hl.start()
        return func_hl

    return async_func

if __name__ == '__main__':
    from time import sleep

    @async
    def print_somedata():
        print 'starting print_somedata'
        sleep(2)
        print 'print_somedata: 2 sec passed'
        sleep(2)
        print 'print_somedata: 2 sec passed'
        sleep(2)
        print 'finished print_somedata'

    def main():
        print_somedata()
        print 'back in main'
        print_somedata()
        print 'back in main'

    main()
</pre>
<h4>其它</h4>
<p>关于更多的示例，你可以参看： <a href="https://wiki.python.org/moin/PythonDecoratorLibrary" target="_blank" rel="noopener noreferrer">Python Decorator Library</a></p>
<p>关于Python Decroator的各种提案，可以参看：<a href="https://wiki.python.org/moin/PythonDecoratorProposals" target="_blank" rel="noopener noreferrer">Python Decorator Proposals</a></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/17929.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2017/06/go-hardhat-150x150.png" alt="Go编程模式：修饰器" width="150" height="150" /></a><a href="https://coolshell.cn/articles/17929.html" class="wp_rp_title">Go编程模式：修饰器</a></li><li ><a href="https://coolshell.cn/articles/10822.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2013/12/yoda-lambda-150x150.png" alt="函数式编程" width="150" height="150" /></a><a href="https://coolshell.cn/articles/10822.html" class="wp_rp_title">函数式编程</a></li><li ><a href="https://coolshell.cn/articles/21164.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2020/12/go.map_.reduce-150x150.png" alt="Go编程模式：Map-Reduce" width="150" height="150" /></a><a href="https://coolshell.cn/articles/21164.html" class="wp_rp_title">Go编程模式：Map-Reduce</a></li><li ><a href="https://coolshell.cn/articles/21146.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2020/12/go.options-150x150.png" alt="Go 编程模式：Functional Options" width="150" height="150" /></a><a href="https://coolshell.cn/articles/21146.html" class="wp_rp_title">Go 编程模式：Functional Options</a></li><li ><a href="https://coolshell.cn/articles/19612.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2019/07/1920px-Margaret_Hamilton_-_restoration-e1563697198766-1-150x150.jpg" alt="50年前的登月程序和程序员有多硬核" width="150" height="150" /></a><a href="https://coolshell.cn/articles/19612.html" class="wp_rp_title">50年前的登月程序和程序员有多硬核</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/11265.html">Python修饰器的函数式编程</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/11265.html/feed</wfw:commentRss>
			<slash:comments>84</slash:comments>
		
		
			</item>
		<item>
		<title>如何用最有创造力的方式输出42</title>
		<link>https://coolshell.cn/articles/11170.html</link>
					<comments>https://coolshell.cn/articles/11170.html#comments</comments>
		
		<dc:creator><![CDATA[陈皓]]></dc:creator>
		<pubDate>Thu, 06 Mar 2014 14:42:42 +0000</pubDate>
				<category><![CDATA[趣味问题]]></category>
		<category><![CDATA[轶事趣闻]]></category>
		<category><![CDATA[42]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[程序员]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=11170</guid>

					<description><![CDATA[<p>酷壳似乎好长时间没有像《编程真难啊》或是《老手是这样教新手编程的》或是像《如何写出无法维护的代码》这样“严肃正经”的文章了，所以，赶在大家还没有向我扔臭鸡蛋前奉...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/11170.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/11170.html">如何用最有创造力的方式输出42</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><img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2014/03/42-300x240.jpg" alt="" width="300" height="240" class="alignright size-medium wp-image-11216" srcset="https://coolshell.cn/wp-content/uploads/2014/03/42-300x240.jpg 300w, https://coolshell.cn/wp-content/uploads/2014/03/42-338x270.jpg 338w, https://coolshell.cn/wp-content/uploads/2014/03/42.jpg 750w" sizes="(max-width: 300px) 100vw, 300px" />酷壳似乎好长时间没有像《<a title="编程真难啊 - 80,069 人阅读" href="https://coolshell.cn/articles/1391.html">编程真难啊</a>》或是《<a title="老手是这样教新手编程的" href="https://coolshell.cn/articles/2420.html" target="_blank">老手是这样教新手编程的</a>》或是像《<a title="如何写出无法维护的代码" href="https://coolshell.cn/articles/4758.html" target="_blank">如何写出无法维护的代码</a>》这样“严肃正经”的文章了，所以，赶在大家还没有向我扔臭鸡蛋前奉献一篇。这篇文章来自CodeGolf.StackExchange上的《<a href="http://codegolf.stackexchange.com/questions/21835/most-creative-way-to-display-42">Most creative way to display 42</a>》—— 请以最有创造力的方式输出42。于是出现了下面的这些答案（注：精彩的总是留在最后面）</p>
<h4>人生和宇宙终级问题的答案：42</h4>
<p>这里，需要介绍一下为什么要输出42。这时因为42是我们人生，世界乃至整个宇宙的终级答案。这要从《银河系漫游指南》（英文名：The Hitchhiker&#8217;s Guide to the Galaxy）说起。这本书是著名英国科幻小说作家Douglas  Adams所著5本银河系漫游指南系列科幻喜剧系列小说中的第一本，改编自他本人为英国广播公司第四电台（BBC Radio 4）所写的广播剧剧本。该书1979年10月12日首次由麦克米伦出版公司（Pan Books）出版，次周成为英国图书销量榜冠军，前3个月内销售超过25万本。截至2005年，这本小说已被翻译成超过30种语言在全世界发行，并且被改编为电视剧、电影、舞台剧等多种艺术形式的作品。</p>
<p>这本小说中小说中充满尖锐的讽刺和隐喻，被西方科幻爱好者奉为“科幻圣经”。其中有两个关键词，一个是Don&#8217;t Panic，一个是42影响力很大，而其中关于42的故事简介是这样的：</p>
<p style="padding-left: 30px;">百万年前，老鼠其实是一种超智慧生物，它们建造了一部超级电脑深思Deep Thought，它们问超级电脑，生命、宇宙以及任何事情的终极答案（<i>Answer to Life, the Universe, and Everything</i>）什么，经过了750万年的计算，深思告诉老鼠的后人答案是<b>42</b>，深思解释它只能计算出答案是什么，但答案的原因必须由另一部更高智能的电脑才能解释，而该部电脑就是地球。经过了800万年，就在结果要出来的五分钟前，地球却因为挡在预定兴建的星际间高速公路的路线，被Vogons给毁灭，电脑没有给出最后的结果。</p>
<p><span id="more-11170"></span></p>
<p style="padding-left: 30px;">故事里面还说了这个42是6 乘于 9得来。当然，6乘9应该是54，但是因为地球上的电脑被搞坏了，导致主人翁答错了。至于后来有人说6 x 9 = 42是基于13进制，原作者说，完全没有这回事，他就是瞎搞的。</p>
<p>网上有很多人在猜测42的含义，比如<a href="http://www.douban.com/note/232036705/" target="_blank">douban的这篇文章</a>，但是原作者出来说这他就是随机想了一个，完全没有任何意义。</p>
<p>对于42来说，数字42和短语，“生命，宇宙以及一切的答案”（<i>Answer to Life, the Universe, and Everything</i>） 已达到在互联网上邪教的地位。在各种技术宅，极客，科学圈有着非同凡响的地位。</p>
<ul>
<li>您若在Google输入<a href="http://www.google.com/search?q=the+answer+to+life%2C+the+universe%2C+and+everything" target="_blank" rel="nofollow">the answer to life, the universe, and everything</a>，Google会直接回答42——而且还是用Google计算器算出来的。</li>
<li>若在<a title="Wolfram Alpha" href="http://zh.wikipedia.org/wiki/Wolfram_Alpha" target="_blank">Wolfram Alpha</a>中输入<a href="http://www.wolframalpha.com/input/?i=Answer+to+the+Ultimate+Question+of+Life%2C+the+Universe%2C+and+Everything" target="_blank" rel="nofollow">Answer to the Ultimate Question of Life, the Universe, and Everything</a>，Wolfram Alpha也会回答42</li>
<li>若在iPhone/iPad的Siri中问[What&#8217;s the meaning of life?]，Siri也会回答42</li>
<li><span><span>在</span></span><a title="OpenOffice.org" href="http://en.wikipedia.org/wiki/OpenOffice.org"><span>OpenOffice.org</span></a><span><span>软件，如果您在任何单元格输入spreadsheet=ANTWORT(&#8220;Das Leben, das Universum und der ganze Rest&#8221;) (注：德语的ANSWER(&#8220;life, the universe and everything&#8221;))，结果也会是42。</span></span></li>
</ul>
<p>另外，在美剧《Lost》里那个经典的数字序列： 4, 8, 15, 16, 23,42。经Lost的导演确认，最后那个42也是源自《银河系漫游指南》</p>
<p>好了，言归正传，下面让我们来看一下如何输出42的。</p>
<h4>Ruby</h4>
<p><code data-enlighter-language="ruby" class="EnlighterJSRAW"></code>puts (6 * 9).to_s(13)[/h4]</p>
<p>解释：6 x 9 = 42的表达式（基于13进制）</p>
<h4>Javascript</h4>
<p>[javascript]String.prototype.answer = function() {<br />
    alert(this.charCodeAt(+!&quot;The End of the Universe&quot;));<br />
};<br />
&#8216;*&#8217;.answer();[/javascript]</p>
<p>解释：+!&#8221;The End of the Universe&#8221;的值是0，&#8217;*&#8217;的ASCII码是42</p>
<p>[javascript]console.log(&quot;Douglas Adams&quot;.length + &quot;born on&quot;.length +<br />
    [1,1,0,3,1,9,5,2].reduce(<br />
        function(previousValue, currentValue, index, array){<br />
            return previousValue + currentValue;<br />
        }<br />
    )<br />
);</p>
<p> /* [1,1,0,3,1,9,5,2] =&gt; March 11, 1952 */[/javascript]</p>
<p>解释：Douglas Adams 是一位英国广播剧作家、和音乐家，尤其以《银河系漫游指南》系列作品出名。这部作品以广播剧起家，后来发展成包括五本书的“三部曲”，拍成电视连续剧。亚当斯逝世后还拍成电影。 除《银河系漫游指南》系列外亚当斯还参加了科幻电视连续剧《神秘博士》的拍摄工作，他写了其中的一些剧本。也的生日是 1952 年 3 月 11 日。</p>
<p>[javascript]alert((!![]+ -~[])*(!![]+ -~[])+&quot;&quot;+(!![]+ -~[]))[/javascript]</p>
<p>解释：[]是个空，![]就是true，~[]是-1, 于是，表达式就这样出来了。变态！</p>
<p>[javascript]var ________ = 0.023809523809523808, ____ = 1, ___ = 0, __ = 0, _ = 1;</p>
<p>       __ &#8211;           ___<br />
     /_  |0        //     \\<br />
    /_/   0     //          \\<br />
   /_/_  |0                //<br />
  /_/_   |0              //<br />
 /_/____ |_           //<br />
/________|0        //<br />
         |0     //______________[/javascript]</p>
<p>解释：这个其实是代码混乱的技巧之一，用下划线当变量。你可以参考《<a href="https://coolshell.cn/articles/933.html" target="_blank">如何加密/混乱C源代码</a>》和《<a href="https://coolshell.cn/articles/914.html" target="_blank">6个变态的C语言Hello World程序</a>》</p>
<h4>Shell</h4>
<p><code data-enlighter-language="shell" class="EnlighterJSRAW">echo &quot;what is the universe&quot;|tr &quot;a-z &quot; 0-7-0-729|sed &#039;s/9.//g;s/-/+/&#039;|bc</code></p>
<p>解释：其中，bc是一个计算器。tr是一个字符转换的命令，比如：<code>echo "good" | tr "good" "test"</code>输出 <code>tsst</code>。也就是说，g-t, o-e, o-s, d-t的映射，o被映了两次，所以，第二次会覆盖第一次。对于上面的<code>tr "a-z " 0-7-0-7-729</code>的意思是：abcdefg分别对应01234567，h对应-，ijklmno对应01234567，p对于2，剩下的包括空格都是9。如果你对tr和sed和bc不熟悉的话，可以man一下，关于sed你可以看一下我的《<a href="https://coolshell.cn/articles/9104.html" target="_blank">sed简明教程</a>》</p>
<pre data-enlighter-language="shell" class="EnlighterJSRAW">#!/bin/bash

#Vertical Version
echo $((2#100))
echo $((2#10))

#Horizontal Version
echo $((2#000100))$((2#00010))</pre>
<p>解释：2#100的意思就是说，#左边的数说明是“2进制”，右边的数是二进制数“100”，如16#ff就是16进制的ff，也就是十进制的255</p>
<p><code data-enlighter-language="shell" class="EnlighterJSRAW">echo &quot;obase=13;6*9&quot;|bc|figlet</code></p>
<p>上面的命令输出：</p>
<pre style="font-family: 'Consolas','Courier New', Courier, monospace;">
 _  _  ____
| || ||___ \
| || |_ __) |
|__   _/ __/
   |_||_____|</pre>
<p>解释：为了使用figlet命令，你还要去安装一个figlet（<a href="http://www.figlet.org/" target="_blank">http://www.figlet.org/</a>）这是一个让你画ASCII图的命令。</p>
<h4>Python</h4>
<p>Windows下，给你画个图：</p>
<div style="height: 300px; overflow: auto;">
<pre data-enlighter-language="python" class="EnlighterJSRAW">import win32api, win32con, win32gui
from time import time, sleep
import os

w = { 1:[(358, 263), (358, 262), (358, 261), (359, 261), (359, 262), (359, 264), (359, 266), (359, 270), (359, 282),
     (358, 289), (357, 308), (356, 319), (355, 341), (355, 351), (355, 360), (355, 378), (355, 388), (354, 397),
     (354, 406), (354, 422), (354, 428), (354, 436), (354, 438), (354, 439), (354, 440), (355, 440), (356, 439),
     (357, 439), (358, 438), (360, 438), (362, 437), (369, 437), (372, 437), (381, 437), (386, 437), (391, 437),
     (397, 436), (411, 436), (419, 435), (434, 435), (442, 435), (449, 434), (456, 434), (468, 434), (473, 435),
     (480, 436), (483, 436), (485, 436), (487, 437), (488, 437), (488, 438), (488, 439), (487, 440), (486, 440),
     (485, 440), (484, 440), (483, 439), (483, 437), (481, 431), (481, 427), (481, 420), (481, 413), (483, 396),
     (485, 387), (488, 367), (491, 356), (493, 345), (500, 321), (503, 310), (507, 299), (514, 280), (517, 272),
     (520, 266), (523, 260), (524, 258), (524, 259), (524, 261), (524, 265), (524, 269), (523, 275), (522, 289),
     (521, 297), (518, 315), (516, 324), (515, 334), (513, 345), (509, 368), (507, 382), (502, 411), (500, 426),
     (498, 440), (495, 453), (491, 478), (489, 491), (485, 517), (483, 530), (481, 542), (479, 552), (476, 570),
     (475, 577), (474, 588), (473, 592), (473, 595), (473, 597), (473, 600), (473, 601), (473, 602), (473, 601),
     (474, 599), (475, 597), (476, 594), (478, 587)],
  2:[(632, 305), (634, 306), (636, 309), (639, 314), (641, 319), (645, 330), (647, 337), (649, 353), (649, 362),
     (649, 372), (649, 384), (645, 409), (639, 436), (636, 448), (632, 459), (627, 470), (623, 479), (613, 497),
     (608, 503), (599, 512), (595, 514), (591, 514), (587, 513), (581, 504), (578, 498), (576, 483), (575, 476),
     (575, 469), (579, 454), (582, 447), (591, 436), (595, 432), (600, 430), (605, 429), (617, 432), (624, 437),
     (639, 448), (646, 455), (654, 461), (662, 469), (679, 484), (686, 491), (702, 504), (710, 509), (718, 512),
     (727, 514), (744, 515), (752, 515), (767, 512), (774, 510), (779, 508), (783, 505), (788, 499), (789, 495),
     (789, 486)] }

def d( x1, y1, x2, y2 ):
    win32api.SetCursorPos((x1, y1))
    win32api.mouse_event(win32con.MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0)
    win32api.SetCursorPos((x2, y2))
    win32api.mouse_event(win32con.MOUSEEVENTF_LEFTUP, 0, 0, 0, 0)
    sleep(0.01)

def p( l1 ):
    l2 = [&quot;&quot;]
    l2.extend(l1)
    l1.append(&quot;&quot;)
    l3 = zip(l2, l1)
    l3.pop(0)
    l3.pop(-1)
    for n in l3:
        d(n[0][0], n[0][1], n[1][0], n[1][2])

os.startfile(&quot;C:\Windows\system32\mspaint.exe&quot;)
sleep(0.5)
win32gui.ShowWindow(win32gui.GetForegroundWindow(), win32con.SW_MAXIMIZE)
sleep(0.5)

for n in w:
    p(w[n])</pre>
</div>
<p>输出：<img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2014/03/1j0va.png" alt="" width="300" height="240" class="aligncenter size-full wp-image-11172" srcset="https://coolshell.cn/wp-content/uploads/2014/03/1j0va.png 499w, https://coolshell.cn/wp-content/uploads/2014/03/1j0va-300x240.png 300w" sizes="(max-width: 300px) 100vw, 300px" /></p>
<p>lambda表达式 </p>
<p><code data-enlighter-language="python" class="EnlighterJSRAW">&gt;&gt;&gt; p = lambda x: x%2!=0 and True&lt;&gt;&gt; sum(p(i) for i in range(0,6))</code></p>
<p>解释：对python的lambda表达式或函数式编程不是很清楚的同学可以看一下《<a href="https://coolshell.cn/articles/10822.html" target="_blank">函数式编程</a>》</p>
<h4>Java</h4>
<pre data-enlighter-language="java" class="EnlighterJSRAW">import java.lang.*;
class answer_to_everything 
{
    void static main() 
    {
        String s = &quot;Hitchhiker&#039;s Guide to the Galaxy&quot;;
        String s2 = &quot;Don&#039;tPanic&quot;;
        String s3 = &quot;The Restaurant at the End of the Universe.&quot;;

        int arthur_dent = s.length();
        int ford_prefect = s2.length();
        int zooey_deschanel = s3.length();
        int vogon_poetry = arthur_dent + ford_prefect;

        System.out.println(&quot;         &quot; + vogon_poetry + &quot;       &quot; + zooey_deschanel + &quot; &quot; + zooey_deschanel); //in case you&#039;re confused, I&#039;m using Zooey to print the big &#039;2&#039;, and Vogons to print the big &#039;4&#039;.
        System.out.println(&quot;       &quot; + vogon_poetry + vogon_poetry + &quot;     &quot; + zooey_deschanel + &quot;     &quot; + zooey_deschanel);
        System.out.println(&quot;     &quot; + vogon_poetry + &quot;  &quot; + vogon_poetry + &quot;    &quot; + zooey_deschanel + &quot;       &quot; + zooey_deschanel);
        System.out.println(&quot;   &quot; + vogon_poetry + &quot;    &quot; + vogon_poetry + &quot;            &quot; + zooey_deschanel);
        System.out.println(&quot; &quot; + vogon_poetry + &quot;      &quot; + vogon_poetry + &quot;          &quot; + zooey_deschanel);
        System.out.println(vogon_poetry + &quot; &quot; + vogon_poetry + &quot; &quot; + vogon_poetry + &quot; DA &quot; + vogon_poetry + &quot;     &quot; + zooey_deschanel);
        System.out.println(&quot;         &quot; + vogon_poetry + &quot;     &quot; + zooey_deschanel);
        System.out.println(&quot;         &quot; + vogon_poetry + &quot;    &quot; + zooey_deschanel + &quot; &quot; + zooey_deschanel + &quot; &quot; + zooey_deschanel + &quot; &quot; + zooey_deschanel);
    }
}</pre>
<p>上面这段看上去平淡无奇，但其亮点是那三个string，这段代码输出：</p>
<pre style="font-family: 'Consolas','Courier New', Courier, monospace;">
         42       42 42
       4242     42     42
     42  42    42       42
   42    42            42
 42      42          42
42 42 42 DA 42     42
         42     42
         42    42 42 42 42</pre>
<p>别忘了Java也可以混乱代码：</p>
<pre data-enlighter-language="java" class="EnlighterJSRAW">
public        class         FourtyTwo{ public
static         void         main(String[]args)
{  new        javax                    .swing.
JFrame        () {{                    setSize
(42 /(        42/42                    +42/42)
*42/ (        42/42                    +42/42)
,42/(42/ 42+42/42)*         42/(42/42+42/42));
}public void paint(         java.awt .Graphics
  g){g.drawPolygon(         new int[]{42,42,42
              + 42+         42,42+
              42+42         ,42+42
              +42 +         42,42+
              42+42         +42,42
              + 42+         42,42+42+42,42+42,
              42+42         },new int[]{42,42+
              42+42         +42,42+42+42+42,42

+42+42+42+42+42,                  42+42+
42+42+42+42,42,42,               42+42+42
,42 +        42+42              ,42}, (42/
42+42        /42)*              (42/  42 +
42/42        + 42/             42 +    42 /
42+42        /42))            ;g.drawPolygon
( new        int[]           {42+42+42+42+42,
42+42        +42 +           42+42      , 42+
42+42        + 42+          42+42        + 42,
42+42        +42 +          42+42        +42 +
42,42+42+42+42+42,         42+42          + 42+
42+42,42+ 42+42+           42+42          +42 +

42+42,42+42+42+42+42+42+42+42,42+42+42+42+42+42,
42+42+42+42+42+42,42+42+42+42+42+42+42+42,42+42+
42+42+42+42+42+42},new int[]{42,42 +42,42+42,42+
42+42,42+42+42,42+42+42+42+42+42,42+42+42+42+42+
42,42+42+42+42+42,42+42+42+42+42,42+42+42+42,42+
42+42+42,42},(42/42+42/42+42/42)*((42/42+42/42)*
(42/42+42/ 42)));};}.setVisible(42*42*42!=42);}}</pre>
<h4>C/C++</h4>
<pre data-enlighter-language="c" class="EnlighterJSRAW">#include
int main()
{
    printf(&quot;%d&quot;, fprintf( fopen(&quot;/dev/null&quot;,&quot;w&quot;),
       &quot;so-popularity-contest\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b&quot;) );
}
</pre>
<p>解释：\b是backspace，fprintf的返回值是写成功数据的长度。</p>
<div style="height: 200px; overflow: auto;">
<pre data-enlighter-language="c" class="EnlighterJSRAW">#include&lt;iostream&gt;
using namespace std;
int main()
{
    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)32;
    cout&lt;&lt;(char)66&lt;&lt;(char)73&lt;&lt;(char)82;
    cout&lt;&lt;(char)84&lt;&lt;(char)72&lt;&lt;(char)32;
    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)32;
    cout&lt;&lt;(char)32&lt;&lt;(char)68&lt;&lt;(char)69;
    cout&lt;&lt;(char)65&lt;&lt;(char)84&lt;&lt;(char)72;
    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;&#039;\n&#039;;
    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)32;
    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)95;
    cout&lt;&lt;(char)95&lt;&lt;(char)95&lt;&lt;(char)32;
    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)32;
    cout&lt;&lt;(char)32&lt;&lt;(char)95&lt;&lt;(char)95;
    cout&lt;&lt;(char)95&lt;&lt;(char)95&lt;&lt;(char)95;
    cout&lt;&lt;(char)95&lt;&lt;(char)32&lt;&lt;&#039;\n&#039;;
    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)32;
    cout&lt;&lt;(char)32&lt;&lt;(char)47&lt;&lt;(char)32;
    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)124;
    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)32;
    cout&lt;&lt;(char)124&lt;&lt;(char)32&lt;&lt;(char)32;
    cout&lt;&lt;(char)95&lt;&lt;(char)95&lt;&lt;(char)32;
    cout&lt;&lt;(char)32&lt;&lt;(char)124&lt;&lt;&#039;\n&#039;;
    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)32;
    cout&lt;&lt;(char)47&lt;&lt;(char)32&lt;&lt;(char)47;
    cout&lt;&lt;(char)124&lt;&lt;(char)32&lt;&lt;(char)124;
    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)32;
    cout&lt;&lt;(char)124&lt;&lt;(char)95&lt;&lt;(char)124;
    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)124;
    cout&lt;&lt;(char)32&lt;&lt;(char)124&lt;&lt;&#039;\n&#039;;
    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)47;
    cout&lt;&lt;(char)32&lt;&lt;(char)47&lt;&lt;(char)32;
    cout&lt;&lt;(char)124&lt;&lt;(char)49&lt;&lt;(char)124;
    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)32;
    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)32;
    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)47;
    cout&lt;&lt;(char)50&lt;&lt;(char)124&lt;&lt;&#039;\n&#039;;
    cout&lt;&lt;(char)32&lt;&lt;(char)47&lt;&lt;(char)32;
    cout&lt;&lt;(char)47&lt;&lt;(char)32&lt;&lt;(char)32;
    cout&lt;&lt;(char)124&lt;&lt;(char)57&lt;&lt;(char)124;
    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)32;
    cout&lt;&lt;(char)84&lt;&lt;(char)79&lt;&lt;(char)32;
    cout&lt;&lt;(char)32&lt;&lt;(char)47&lt;&lt;(char)48;
    cout&lt;&lt;(char)47&lt;&lt;(char)32&lt;&lt;&#039;\n&#039;;
    cout&lt;&lt;(char)47&lt;&lt;(char)32&lt;&lt;(char)47;
    cout&lt;&lt;(char)95&lt;&lt;(char)95&lt;&lt;(char)95;
    cout&lt;&lt;(char)124&lt;&lt;(char)53&lt;&lt;(char)124;
    cout&lt;&lt;(char)95&lt;&lt;(char)95&lt;&lt;(char)32;
    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)32;
    cout&lt;&lt;(char)47&lt;&lt;(char)48&lt;&lt;(char)47;
    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;&#039;\n&#039;;
    cout&lt;&lt;(char)124&lt;&lt;(char)95&lt;&lt;(char)95;
    cout&lt;&lt;(char)95&lt;&lt;(char)95&lt;&lt;(char)95;
    cout&lt;&lt;(char)124&lt;&lt;(char)50&lt;&lt;(char)124;
    cout&lt;&lt;(char)95&lt;&lt;(char)95&lt;&lt;(char)124;
    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)47;
    cout&lt;&lt;(char)49&lt;&lt;(char)47&lt;&lt;(char)32;
    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;&#039;\n&#039;;
    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)32;
    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)32;
    cout&lt;&lt;(char)124&lt;&lt;(char)32&lt;&lt;(char)124;
    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)32;
    cout&lt;&lt;(char)32&lt;&lt;(char)47&lt;&lt;(char)32;
    cout&lt;&lt;(char)47&lt;&lt;(char)32&lt;&lt;(char)32;
    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;&#039;\n&#039;;
    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)32;
    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)32;
    cout&lt;&lt;(char)124&lt;&lt;(char)32&lt;&lt;(char)124;
    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)32;
    cout&lt;&lt;(char)47&lt;&lt;(char)32&lt;&lt;(char)47;
    cout&lt;&lt;(char)95&lt;&lt;(char)95&lt;&lt;(char)95;
    cout&lt;&lt;(char)95&lt;&lt;(char)32&lt;&lt;&#039;\n&#039;;
    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)32;
    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)32;
    cout&lt;&lt;(char)124&lt;&lt;(char)95&lt;&lt;(char)124;
    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)124;
    cout&lt;&lt;(char)95&lt;&lt;(char)95&lt;&lt;(char)95;
    cout&lt;&lt;(char)95&lt;&lt;(char)95&lt;&lt;(char)95;
    cout&lt;&lt;(char)95&lt;&lt;(char)124&lt;&lt;&#039;\n&#039;;
    return 0;
}  </pre>
</div>
<p>输出：</p>
<p><img decoding="async" loading="lazy" class="aligncenter size-full wp-image-11171" alt="" src="https://coolshell.cn/wp-content/uploads/2014/03/42.png" width="182" height="151" /></p>
<pre data-enlighter-language="c" class="EnlighterJSRAW">#include &lt;stdio.h&gt;

#define six  1+5
#define nine 8+1

int main()
{
    printf(&quot;what do you get when you multiply six by nine?\n&quot;);
    printf(&quot;%i x %i = %i\n&quot;, six, nine, six*nine);
}</pre>
<p>解释：6 x 9 = 42 ???，如果你知道宏只是做简单的字符串替换的话，你就知道six*nine被替换成了1+5*8+1这个表达式了。呵呵。</p>
<pre data-enlighter-language="c" class="EnlighterJSRAW">
        main(c     ,z,_){c==01?
       main(c+     1,0,c^c):c==2
      ?z=_[&quot;#&quot;     &quot;#$#%&amp;#%#x&#039;%%&quot;
     &quot;()&amp;(%%x&quot;             &quot;$%$(&quot;
    &quot;(&amp;(&quot;&quot;*%x&quot;             &quot;&#039;%%(&quot;
   &quot;(&amp;(&quot; &quot;+%x&quot;             &quot;&#039;#%(&quot;
  &quot;(&amp;(&quot;  &quot;%#x&quot;             ],z ?z
 ==&#039;x&#039;?main(4,_     ,c*5):main(c
 +1,z,0),main(c    ,z,_+1):00:c
 ==3?(_+-2)==3?    main(_-1,_,
         32):(     main(
         c+1,c     ,((2+
         c)*(z     -35)+
         _)[&quot;&quot;     &quot;six&quot;
         &quot;*ni&quot;     &quot;ne= {   }   &quot;
         &quot;  ;&quot;     &quot;      _   ( &quot;
         &quot;) [&quot;     &quot; 3 ]do {;&quot;]==
         32?32     :043),main(c,z
         ,_+1)     ):putchar(_);}</pre>
<p>解释：参看<a href="http://codegolf.stackexchange.com/questions/21835/most-creative-way-to-display-42/21950#21950" target="_blank">原文的这个答案</a>里的How-To一节。</p>
<h4>Brainfuck</h4>
<p>代码混乱自然少不了brainfuck语言：（更多的奇葩的编程语言请参考《<a href="https://coolshell.cn/articles/4458.html" target="_blank">那些BT雷人的编程语言</a>》）</p>
<pre style="font-family: 'Consolas','Courier New', Courier, monospace;"> 
         +++++          +++[>+>++>
        +++>++        ++>+++++>+++++
       +>+++++       ++>+        ++++
      +++ >+++       ++++        ++>+
     +++  ++++                   ++>+
    +++   ++++                  +++>
   +++    ++++                 ++++
  +>+     ++++               ++++
 +++      +>++             ++++
++++++++>+++++++++       ++++
++>+++++++++++++++     +<<<
          <<<<        <<<<
          <<<<       <-]>
          >>>>       >>----.++++<<<<<
          <<>>       >>>>++.--<<<<<<.</pre>
<p>不过，下面这个BrainFuck更无聊，所以顶在了最佳答案上：</p>
<pre style="font-family: 'Consolas','Courier New', Courier, monospace;">
           +++++[>++[>+>+        ++>++++>++++>++++>++++++
          >++++++>+++++++        ++>+++++++++<<<<<<<<<-]>>
         >+>+>+> >>>+[<]<        -]>>       >++>-->>+>>++>+
        >--<<<<  <<<.....         .>            ....<......
       ...>...   <<.>....                       >.>>>>>.<.
       <<<<..     ..<....                      >..>>>>>.<
      .<<<<.      >>>.<<.                     >>>>>.<.<
      <<<<<       <.>...>                    >>>.>>>.
     <<<.<        <<<..>>                  .>>>>>.<
    <.<<<         <<...>>                 >>>.<<<
   <..<.          ...>...               <<.>..>.
   >>.<.<<...>>...<<...>>...<         <....>>..
  .<<<.>.>>..>.<<.......<....        .....>...
                 <<.>...            .....>...
                 <......           .>>>.<<..
                 <<.>...          .....>...<......>.>>.<.<<<
                 .>......        ..>>...<<....>>.....>.<..>.
</pre>
<p>执行上面的代码，你会得到下面的输出：</p>
<pre style="font-family: 'Consolas','Courier New', Courier, monospace;">
      ++++         +++
    +[>++++    ++[>+<-][
   <]<  -]>   >++    +++
  +.-   ---   ---    ---
 --.+++++++         +++
        +++       .++
        +++      +.-
        ---    -----.--.</pre>
<p>再执行上面的代码，会输出：</p>
<pre>6*7=42</pre>
<p>如果6*9=42就完美了，就差一步啊……</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/">酷 壳 - 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/19612.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2019/07/1920px-Margaret_Hamilton_-_restoration-e1563697198766-1-150x150.jpg" alt="50年前的登月程序和程序员有多硬核" width="150" height="150" /></a><a href="https://coolshell.cn/articles/19612.html" class="wp_rp_title">50年前的登月程序和程序员有多硬核</a></li><li ><a href="https://coolshell.cn/articles/12052.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg" alt="Leetcode 编程训练" width="150" height="150" /></a><a href="https://coolshell.cn/articles/12052.html" class="wp_rp_title">Leetcode 编程训练</a></li><li ><a href="https://coolshell.cn/articles/11656.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2014/06/software_development-150x150.png" alt="开发团队的效率" width="150" height="150" /></a><a href="https://coolshell.cn/articles/11656.html" class="wp_rp_title">开发团队的效率</a></li><li ><a href="https://coolshell.cn/articles/8387.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2012/10/Learnable_Programming-150x150.jpg" alt="Bret Victor &#8211; Learnable Programming" width="150" height="150" /></a><a href="https://coolshell.cn/articles/8387.html" class="wp_rp_title">Bret Victor &#8211; Learnable Programming</a></li><li ><a href="https://coolshell.cn/articles/22298.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png" alt="聊聊团队协同和协同工具" width="150" height="150" /></a><a href="https://coolshell.cn/articles/22298.html" class="wp_rp_title">聊聊团队协同和协同工具</a></li><li ><a href="https://coolshell.cn/articles/22173.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png" alt="“一把梭：REST API 全用 POST”" width="150" height="150" /></a><a href="https://coolshell.cn/articles/22173.html" class="wp_rp_title">“一把梭：REST API 全用 POST”</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/11170.html">如何用最有创造力的方式输出42</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/11170.html/feed</wfw:commentRss>
			<slash:comments>29</slash:comments>
		
		
			</item>
		<item>
		<title>可视化编程</title>
		<link>https://coolshell.cn/articles/11094.html</link>
					<comments>https://coolshell.cn/articles/11094.html#comments</comments>
		
		<dc:creator><![CDATA[陈皓]]></dc:creator>
		<pubDate>Fri, 21 Feb 2014 16:27:10 +0000</pubDate>
				<category><![CDATA[杂项资源]]></category>
		<category><![CDATA[编程工具]]></category>
		<category><![CDATA[IDE]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Visual]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=11094</guid>

					<description><![CDATA[<p>本文来自《Visual Programming Languages &#8211; Snapshots》，作者Eric Hosick收集了一堆关于可视化编程的工...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/11094.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/11094.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>本文来自《<a href="http://blog.interfacevision.com/design/design-visual-progarmming-languages-snapshots/" target="_blank">Visual Programming Languages &#8211; Snapshots</a>》，作者<a href="http://twitter.com/erichosick" target="_blank">Eric Hosick</a>收集了一堆关于可视化编程的工具，好多我都听都没听说过，我一股脑的全转过来，给大家看看，算是开开眼界了。<span style="line-height: 1.5em;">本文也是参考了Wikipedia的 </span><a style="line-height: 1.5em;" href="http://en.wikipedia.org/wiki/Visual_programming_language">Visual Programming Language</a> 词条。</p>
<p>另外，在原文有很多评论，其中也有很多正文没有提到的，你可以前去围观一下。</p>
<h4 id="sketchpad">SketchPad</h4>
<p>Maybe the first. 1963.</p>
<p><a href="http://mydiesel22.blogspot.com/2011/05/vector-and-digital-graphics.html">图片来源</a>, <a href="http://en.wikipedia.org/wiki/Sketchpad">Wikipedia</a> 和 <a href="http://www.youtube.com/watch?v=495nCzxM9PI&amp;feature=player_embedded">官方网站</a></p>
<p style="text-align: center;"><img decoding="async" loading="lazy" class="aligncenter" alt="" src="http://blog.interfacevision.com/assets/img/posts/example_visual_language_sketchpad_01.jpg" width="576" height="407" /><img alt="" /></p>
<p><span id="more-11094"></span></p>
<h4 id="alice">Alice</h4>
<p><a href="http://www.alice.org/index.php">图片来源</a>, <a href="http://en.wikipedia.org/wiki/Alice_%28software%29">Wikipedia</a> 和 <a href="http://en.wikipedia.org/wiki/File:Alice-2-screenshot.jpg">官方网站</a></p>
<p><img decoding="async" class="aligncenter" alt="" src="http://blog.interfacevision.com/assets/img/posts/example_visual_language_alice_01.jpg" /><img alt="" /></p>
<h4 id="app_inventor_for_android">App Inventor For Android</h4>
<p><a href="http://beta.appinventor.mit.edu/learn/tutorials/whereismycar/whereismycar.html">图片来源</a>, <a href="http://en.wikipedia.org/wiki/App_Inventor_for_Android">Wikipedia</a> 和 <a href="http://appinventor.mit.edu/explore/">官方网站</a></p>
<p><img decoding="async" class="aligncenter" alt="" src="http://blog.interfacevision.com/assets/img/posts/example_visual_language_app_inventor_01.png" /><img alt="" /></p>
<h4 id="arcgis_model_builder">ArcGIS Model Builder</h4>
<p><a href="http://www.rockware.com/product/featuresLobby.php?id=193&amp;category=615">图片来源</a> 和 <a href="http://resources.arcgis.com/en/help/main/10.1/index.html#//002w00000001000000">官方网站</a></p>
<p><img decoding="async" class="aligncenter" alt="" src="http://blog.interfacevision.com/assets/img/posts/example_visual_language_app_arcgis_01.gif" /><img alt="" /></p>
<h4 id="automator">Automator</h4>
<p><a href="http://www.apple.com/remotedesktop/automation.html">图片来源</a>, <a href="http://en.wikipedia.org/wiki/Automator_%28software%29">Wikipedia</a> 和 <a href="http://www.apple.com/osx/apps/#automator">官方网站</a></p>
<p><img decoding="async" class="aligncenter" alt="" src="http://blog.interfacevision.com/assets/img/posts/example_visual_language_automator_01.jpg" /><img alt="" /></p>
<h4 id="blockly">Blockly</h4>
<p><a href="http://i.imgur.com/PfJO2.png">图片来源</a> 和 <a href="https://code.google.com/p/blockly/">官方网站</a></p>
<p><img decoding="async" class="aligncenter" alt="" src="http://blog.interfacevision.com/assets/img/posts/example_visual_language_blockly_01.png" /><img alt="" /></p>
<h4 id="bounce">Bounce</h4>
<p><a href="http://www.art.net/~hopkins/Don/lang/bounce/SpaceSeedCircuits.gif">图片来源</a> 和 <a href="http://www.art.net/~hopkins/Don/lang/bounce/bounce.html">官方网站</a></p>
<p><img decoding="async" class="aligncenter" alt="" src="http://blog.interfacevision.com/assets/img/posts/example_visual_language_bounce_01.gif" /><img alt="" /></p>
<h4 id="copper_thoughts">Copper Thoughts</h4>
<p><a href="http://www.copperthoughts.com/assets/request-fsm-instance.png">图片来源</a> 和 <a href="http://www.copperthoughts.com/">官方网站</a></p>
<p><img decoding="async" class="aligncenter" alt="" src="http://blog.interfacevision.com/assets/img/posts/example_visual_language_copper_01.png" /><img alt="" /></p>
<h4 id="drakon">DRAKON</h4>
<p><a href="http://en.wikipedia.org/wiki/DRAKON">图片来源</a>, <a href="http://en.wikipedia.org/wiki/DRAKON">Wikipedia</a> 和 <a href="http://drakon-editor.sourceforge.net/">官方网站</a></p>
<p style="text-align: center;"><img decoding="async" loading="lazy" class="aligncenter" alt="" src="http://blog.interfacevision.com/assets/img/posts/example_visual_language_drakon_01.png" width="720" height="712" /><img alt="" /></p>
<h4 id="etoys__squeak">Etoys / Squeak</h4>
<p><a href="http://en.wikipedia.org/wiki/File:Squeak-screenshot.png">图片来源</a>, <a href="http://en.wikipedia.org/wiki/Etoys_%28programming_language%29">Wikipedia</a> 和 <a href="http://www.squeakland.org/">官方网站</a></p>
<p style="text-align: center;"><img decoding="async" loading="lazy" class="aligncenter" alt="" src="http://blog.interfacevision.com/assets/img/posts/example_visual_language_etoysqueak_01.png" width="733" height="496" /><img alt="" /></p>
<h4 id="field">Field</h4>
<p><a href="http://openendedgroup.com/field/OverviewBanners2.html">图片来源</a> 和 <a href="http://openendedgroup.com/field/">官方网站</a></p>
<p style="text-align: center;"><img decoding="async" loading="lazy" class="aligncenter" alt="" src="http://blog.interfacevision.com/assets/img/posts/example_visual_language_field_01.png" width="860" height="442" /><img alt="" /></p>
<h4 id="fl_studio">FL Studio</h4>
<p><a href="http://freaksolid.wordpress.com/2013/05/20/fl-studio-11-patcher-dj-performance-presets/">图片来源</a>, <a href="http://en.wikipedia.org/wiki/Fl_studio">Wikipedia</a> 和 <a href="http://www.image-line.com/flstudio/">官方网站</a></p>
<p style="text-align: center;"><img decoding="async" loading="lazy" class="aligncenter" alt="" src="http://blog.interfacevision.com/assets/img/posts/example_visual_language_flstudiopatcher_01.jpg" width="1082" height="610" /><img alt="" /></p>
<h4 id="flow_hub_and_noflo">Flow Hub and NoFlo</h4>
<p>Flow-Based Programming.</p>
<p><a href="http://flowhub.io/">图片来源 1</a>, <a href="http://cdn.thegrid.io.s3.amazonaws.com/noflo/kickstarter/images/UI-03.jpg">图片来源 2</a> <a href="http://noflojs.org/">官方网站 1</a> 和 <a href="http://flowhub.io/">官方网站 2</a></p>
<p style="text-align: center;"><img decoding="async" loading="lazy" class="aligncenter" alt="" src="http://blog.interfacevision.com/assets/img/posts/example_visual_language_flohub_01.png" width="819" height="451" /><img alt="" /></p>
<p style="text-align: center;"><img decoding="async" loading="lazy" class="aligncenter" alt="" src="http://blog.interfacevision.com/assets/img/posts/example_visual_language_noflo_01.jpg" width="734" height="562" /><img alt="" /></p>
<h4 id="flowstone">FlowStone</h4>
<p><a href="http://en.wikipedia.org/wiki/File:FlowStone_Large_Screenshot.png">图片来源</a> 和 <a href="http://www.dsprobotics.com/flowstone.html">官方网站</a></p>
<p><img decoding="async" class="aligncenter" alt="" src="http://blog.interfacevision.com/assets/img/posts/example_visual_language_flowstone_01.png" /><img alt="" /></p>
<h4 id="godot_engine">GoDot Engine</h4>
<p><a href="http://www.godotengine.org/wp/wp-content/uploads/2014/01/editor2.jpg">图片来源</a> 和 <a href="http://www.godotengine.org/wp/">官方网站</a></p>
<p><img decoding="async" class="aligncenter" alt="" src="http://blog.interfacevision.com/assets/img/posts/example_visual_language_godot_01.jpg" /><img alt="" /></p>
<h4 id="google_web_designer">Google Web Designer</h4>
<p><a>图片来源</a>, <a href="http://en.wikipedia.org/wiki/Google_Web_Designer">Wikipedia</a> 和 <a href="https://www.google.com/webdesigner/">官方网站</a></p>
<p><img decoding="async" class="aligncenter" alt="" src="http://blog.interfacevision.com/assets/img/posts/example_visual_language_webdesigner_01.png" /><img alt="" /></p>
<h4 id="hopscotch">Hopscotch</h4>
<p><a href="https://www.gethopscotch.com/">图片来源</a>, <a href="http://en.wikipedia.org/wiki/Hopscotch_%28programming_language%29">Wikipedia</a> 和 <a href="https://www.gethopscotch.com/">官方网站</a></p>
<p><img decoding="async" class="aligncenter" alt="" src="http://blog.interfacevision.com/assets/img/posts/example_visual_language_hopscotch_01.png" /><img alt="" /></p>
<h4 id="hypercard">HyperCard</h4>
<p><a href="http://www.smackerel.net/black_white_02.html">图片来源</a>, <a href="http://en.wikipedia.org/wiki/HyperCard">Wikipedia</a> 和 <a href="http://hypercard.org/">官方网站???</a></p>
<p><img decoding="async" class="aligncenter" alt="" src="http://blog.interfacevision.com/assets/img/posts/example_visual_language_hypercard_01.gif" /><img alt="" /></p>
<h4 id="ifttt">IFTTT</h4>
<p><a href="https://ifttt.com/recipes">图片来源</a>, <a href="http://en.wikipedia.org/wiki/Ifttt">Wikipedia</a> 和 <a href="https://ifttt.com/wtf">官方网站</a></p>
<p><img decoding="async" class="aligncenter" alt="" src="http://blog.interfacevision.com/assets/img/posts/example_visual_language_ifttt_01.png" /><img alt="" /></p>
<h4 id="illumination_software_creator">Illumination Software Creator</h4>
<p><a href="http://lunduke.com/2010/06/16/illumination-software-creator-20-beta-2/">图片来源</a>, <a href="http://en.wikipedia.org/wiki/Illumination_Software_Creator">Wikipedia</a> 和 <a href="http://lunduke.com/2010/06/16/illumination-software-creator-20-beta-2/">官方网站</a></p>
<p><img decoding="async" class="aligncenter" alt="" src="http://blog.interfacevision.com/assets/img/posts/example_visual_language_illumination_01.png" /><img alt="" /></p>
<h4 id="intentional_technology">Intentional Technology</h4>
<p><a href="http://www.intentsoft.com/intentional-technology/">图片来源</a> 和 <a href="http://www.intentsoft.com/intentional-technology/">官方网站</a></p>
<p><img decoding="async" class="aligncenter" alt="" src="http://blog.interfacevision.com/assets/img/posts/example_visual_language_IntentionalTech_01.png" /><img alt="" /></p>
<h4 id="jeskola_buzz">Jeskola Buzz</h4>
<p><a href="http://blog.livedoor.jp/acid808/archives/cat_693944.html">图片来源</a>, <a href="http://en.wikipedia.org/wiki/Jeskola_Buzz">Wikipedia</a> 和 <a href="http://www.jeskola.net/buzz/">官方网站</a></p>
<p><img decoding="async" class="aligncenter" alt="" src="http://blog.interfacevision.com/assets/img/posts/example_visual_language_jeskolabuzz_01.jpg" /><img alt="" /></p>
<h4 id="kimono">Kimono</h4>
<p><a href="http://www.kimonolabs.com/">官方网站</a></p>
<p><img decoding="async" class="aligncenter" alt="" src="http://blog.interfacevision.com/assets/img/posts/example_visual_language_kimono_01.png" /><img alt="" /></p>
<h4 id="kodu_boku">Kodu (Boku)</h4>
<p><a href="http://www.interactiveclassroom.net/?p=508">图片来源</a>, <a href="http://en.wikipedia.org/wiki/Kodu">Wikipedia</a> 和 <a href="http://research.microsoft.com/en-us/projects/kodu/">官方网站</a></p>
<p><img decoding="async" class="aligncenter" alt="" src="http://blog.interfacevision.com/assets/img/posts/example_visual_language_kodu_01.png" /><img alt="" /></p>
<h4 id="labview">LabView</h4>
<p><a href="http://www.ni.com/newsletter/51735/en/">图片来源</a>, <a href="http://en.wikipedia.org/wiki/LabVIEW">Wikipedia</a> 和 <a href="http://www.ni.com/">官方网站</a></p>
<p><img decoding="async" class="aligncenter" alt="" src="http://blog.interfacevision.com/assets/img/posts/example_visual_language_labview_02.png" /><img alt="" /></p>
<h4 id="ladder_logic">Ladder Logic</h4>
<p><a href="http://en.wikipedia.org/wiki/Ladder_logic">图片来源</a>, <a href="http://en.wikipedia.org/wiki/Ladder_logic">Wikipedia</a> 和 <a>官方网站</a></p>
<p><img decoding="async" class="aligncenter" alt="" src="http://blog.interfacevision.com/assets/img/posts/example_visual_language_ladderlogic_01.png" /><img alt="" /></p>
<h4 id="lamdu">Lamdu</h4>
<p><a href="http://peaker.github.io/lamdu/">图片来源</a> 和 <a href="http://peaker.github.io/lamdu/">官方网站</a></p>
<p><img decoding="async" class="aligncenter" alt="" src="http://blog.interfacevision.com/assets/img/posts/example_visual_language_lamdu_01.png" /><img alt="" /></p>
<h4 id="lava">Lava</h4>
<p><a href="http://lavape.sourceforge.net/Derivation.htm">图片来源</a>, <a href="http://en.wikipedia.org/wiki/Lava_%28programming_language%29">Wikipedia</a> 和 <a href="http://lavape.sourceforge.net/">官方网站</a></p>
<p style="text-align: center;"><img decoding="async" loading="lazy" class="aligncenter" alt="" src="http://blog.interfacevision.com/assets/img/posts/example_visual_language_lava_01.png" width="660" height="639" /><img alt="" /></p>
<h4 id="learnable_programming">Learnable Programming</h4>
<p>More of a post on different ways to learn programming.</p>
<p><a href="http://worrydream.com/#!/LearnableProgramming">图片来源</a> 和 <a href="http://worrydream.com/#!/LearnableProgramming">官方网站</a></p>
<p><img decoding="async" class="aligncenter" alt="" src="http://blog.interfacevision.com/assets/img/posts/example_visual_language_learnable_01.png" /><img alt="" /></p>
<h4 id="light_table">Light Table</h4>
<p>Chris Granger’s development environment. <a href="https://plus.google.com/+JJoeDouglas/posts">图片来源</a>, <a href="http://en.wikipedia.org/wiki/Light_table_%28software%29">Wikipedia</a> 和 <a href="http://www.lighttable.com/">官方网站</a></p>
<p><img decoding="async" class="aligncenter" alt="" src="http://blog.interfacevision.com/assets/img/posts/example_visual_language_lighttable_01.jpg" /><img alt="" /></p>
<h4 id="lily">Lily</h4>
<p>Really cool and hard to describe. You need to visit their demo web page and watch their videos. <a href="http://blog.lilyapp.org/lily/demo/">图片来源</a>, <a href="http://en.wikipedia.org/wiki/Lily_%28software%29">Wikipedia</a> 和 <a href="http://blog.lilyapp.org/lily/demo/">官方网站</a></p>
<p><img decoding="async" class="aligncenter" alt="" src="http://blog.interfacevision.com/assets/img/posts/example_visual_language_lily_01.png" /><img alt="" /></p>
<h4 id="limnor_studio">Limnor Studio</h4>
<p><a href="http://www.limnor.com/studio_whatIsIt.html">图片来源</a>, <a href="http://en.wikipedia.org/wiki/Limnor">Wikipedia</a> 和 <a href="http://www.limnor.com/studio_whatIsIt.html">官方网站</a></p>
<p><img decoding="async" class="aligncenter" alt="" src="http://blog.interfacevision.com/assets/img/posts/example_visual_language_limnorstudio_01.png" /><img alt="" /></p>
<h4 id="little_big_planet">Little Big Planet</h4>
<p>Someone built an An <a href="http://www.youtube.com/watch?v=jWanvKdurU0">8-bit Mechanical Adder in LittleBigPlanet</a></p>
<p><a href="http://www.youtube.com/watch?v=jWanvKdurU0">图片来源</a>, <a href="http://en.wikipedia.org/wiki/LittleBigPlanet">Wikipedia</a> 和 <a href="http://littlebigplanet.playstation.com/">官方网站</a></p>
<p><img decoding="async" class="aligncenter" alt="" src="http://blog.interfacevision.com/assets/img/posts/example_visual_language_littlebig_01.png" /><img alt="" /></p>
<h4 id="minecraft">Minecraft</h4>
<p>Considering someone has created a <a href="http://www.youtube.com/watch?v=frcr9XYeTW4">fully programmable computer</a> using Minecraft.</p>
<p><a href="http://www.youtube.com/watch?v=frcr9XYeTW4">图片来源</a>, <a href="http://en.wikipedia.org/wiki/Minecraft">Wikipedia</a> 和 <a href="https://minecraft.net/">官方网站</a></p>
<p><img decoding="async" class="aligncenter" alt="" src="http://blog.interfacevision.com/assets/img/posts/example_visual_language_minecraft_01.png" /><img alt="" /></p>
<h4 id="minibloq">Minibloq</h4>
<p>This has a really cool looking interface. <a href="http://en.wikipedia.org/wiki/File:ToneWithVariables.png">图片来源</a>, <a href="http://en.wikipedia.org/wiki/Minibloq">Wikipedia</a> 和 <a href="http://blog.minibloq.org/">官方网站</a></p>
<p><img decoding="async" class="aligncenter" alt="" src="http://blog.interfacevision.com/assets/img/posts/example_visual_language_minibloq_01.png" /><img alt="" /></p>
<h4 id="morphic">Morphic</h4>
<p><a href="http://www.cc.gatech.edu/fac/mark.guzdial/squeak/startingmorphic.html">图片来源</a>, <a href="http://en.wikipedia.org/wiki/Morphic_%28software%29">Wikipedia</a> 和 <a href="http://www.dmoz.org/Computers/Software/Operating_Systems/Graphic_Subsystems/Morphic">官方网站</a></p>
<p><img decoding="async" class="aligncenter" alt="" src="http://blog.interfacevision.com/assets/img/posts/example_visual_language_morphic_01.gif" /><img alt="" /></p>
<h4 id="mozilla_appmaker">Mozilla Appmaker</h4>
<p>This was discussed quite a bit on <a href="https://news.ycombinator.com/item?id=6501731">Ycombinator</a>. <a href="http://2.bp.blogspot.com/-1xD81b5fPso/Uly-amqf9vI/AAAAAAAAC8I/n7ehLipb1CE/s1600/appmaker.png">图片来源</a> 和 <a href="https://appmaker.mozillalabs.com/">官方网站</a></p>
<p><img decoding="async" class="aligncenter" alt="" src="http://blog.interfacevision.com/assets/img/posts/example_visual_language_appmaker_01.png" /><img alt="" /></p>
<h4 id="mst_workshop">MST Workshop</h4>
<p><a href="http://home.comcast.net/~tpandolfi/site/?/photos/&amp;PHPSESSID=63621f2035fe55537d794ab0ac795934">图片来源</a>, <a href="http://en.wikipedia.org/wiki/MST_Workshop">Wikipedia</a> 和 <a href="http://home.comcast.net/~tpandolfi/site/?/home/">官方网站</a></p>
<p><img decoding="async" class="aligncenter" alt="" src="http://blog.interfacevision.com/assets/img/posts/example_visual_language_mst_01.jpg" /><img alt="" /></p>
<h4 id="neattools_visual_programming_environment">NeatTools Visual Programming Environment</h4>
<p><a href="http://www.sensyr.com/NeatTools.html">图片来源</a> 和 <a href="http://www.neattools.org/">官方网站</a></p>
<p><img decoding="async" class="aligncenter" alt="" src="http://blog.interfacevision.com/assets/img/posts/example_visual_language_NeatTools_01.png" /><img alt="" /></p>
<h4 id="nodebox">NodeBox</h4>
<p><a href="http://nodebox.net/node/">图片来源</a> 和 <a href="http://nodebox.net/">官方网站</a></p>
<p><img decoding="async" class="aligncenter" alt="" src="http://blog.interfacevision.com/assets/img/posts/example_visual_language_nodebox_01.png" /><img alt="" /></p>
<h4 id="nuke">Nuke</h4>
<p><a href="http://www.thefoundry.co.uk/products/nuke-product-family/nuke/">图片来源</a>, <a href="http://en.wikipedia.org/wiki/Nuke_%28software%29">Wikipedia</a> 和 <a href="http://www.thefoundry.co.uk/products/nuke-product-family/nuke/">官方网站</a></p>
<p><img decoding="async" class="aligncenter" alt="" src="http://blog.interfacevision.com/assets/img/posts/example_visual_language_nuke_01.png" /><img alt="" /></p>
<h4 id="nxtg">NXT-G</h4>
<p>Legos!!! <a href="http://www.brickshelf.com/cgi-bin/gallery.cgi?i=2051945">图片来源</a>, <a href="http://en.wikipedia.org/wiki/Lego_Mindstorms_NXT#NXT-G">Wikipedia</a> 和 <a href="http://www.legoengineering.com/program/nxt-g/">官方网站</a></p>
<p><img decoding="async" class="aligncenter" alt="" src="http://blog.interfacevision.com/assets/img/posts/example_visual_language_nxt-g_01.png" /><img alt="" /></p>
<h4 id="open_modelica">Open Modelica</h4>
<p><a href="http://www.marekgayer.com/en/projects/incfd/">图片来源</a> 和 <a href="https://www.openmodelica.org/">官方网站</a></p>
<p><img decoding="async" class="aligncenter" alt="" src="http://blog.interfacevision.com/assets/img/posts/example_visual_language_openmodelica_01.jpg" /><img alt="" /></p>
<h4 id="open_music">Open Music</h4>
<p><a href="http://en.wikipedia.org/wiki/File:Om_patch.gif">图片来源</a>, <a href="http://en.wikipedia.org/wiki/OpenMusic">Wikipedia</a> 和 <a href="http://repmus.ircam.fr/openmusic/home">官方网站</a></p>
<p><img decoding="async" class="aligncenter" alt="" src="http://blog.interfacevision.com/assets/img/posts/example_visual_language_openmusic_01.gif" /><img alt="" /></p>
<h4 id="openwire">OpenWire</h4>
<p><a href="http://www.mitov.com/products/openwire#screenshots">图片来源</a>, <a href="http://en.wikipedia.org/wiki/OpenWire_%28library%29">Wikipedia</a> 和 <a href="http://www.mitov.com/products/openwire#overview">官方网站</a></p>
<p><img decoding="async" class="aligncenter" alt="" src="http://blog.interfacevision.com/assets/img/posts/example_visual_language_openwire_01.png" /><img alt="" /></p>
<h4 id="origami">Origami</h4>
<p><a href="http://a.36krcnd.com/photo/2014/d2878df00bea4bfb782037f1683423e3.jpg">图片来源</a> 和 <a href="http://facebook.github.io/origami/">官方网站</a></p>
<p><img decoding="async" class="aligncenter" alt="" src="http://blog.interfacevision.com/assets/img/posts/example_visual_language_origami_01.jpg" /><img alt="" /></p>
<h4 id="piet">Piet</h4>
<p><a href="http://en.wikipedia.org/wiki/Piet_%28programming_language%29#Piet">图片来源</a>, <a href="http://en.wikipedia.org/wiki/Piet_%28programming_language%29#Piet">Wikipedia</a> 和 <a href="http://www.retas.de/thomas/computer/programs/useless/piet/Piet/index.html">官方网站</a></p>
<p><img decoding="async" class="aligncenter" alt="" src="http://blog.interfacevision.com/assets/img/posts/example_visual_language_piet_01.gif" /><img alt="" /></p>
<h4 id="programming_without_coding_technology">Programming Without Coding Technology</h4>
<p><a href="http://sourceforge.net/projects/doublesvsoop/?source=recommended">图片来源</a> 和 <a href="http://doublesvsoop.sourceforge.net/">官方网站</a></p>
<p><img decoding="async" class="aligncenter" alt="" src="http://blog.interfacevision.com/assets/img/posts/example_visual_language_pwct_01.png" /><img alt="" /></p>
<h4 id="prograph">Prograph</h4>
<p><a href="http://en.wikipedia.org/wiki/File:Prograph_database_operation.PNG">图片来源</a>, <a href="http://en.wikipedia.org/wiki/Prograph">Wikipedia</a> 和 <a href="http://c2.com/cgi/wiki?PrographLanguage">官方网站??</a></p>
<p><img decoding="async" class="aligncenter" alt="" src="http://blog.interfacevision.com/assets/img/posts/example_visual_language_prograph_01.png" /><img alt="" /></p>
<h4 id="pure_data">Pure Data</h4>
<p><a href="http://en.wikipedia.org/wiki/File:Pd_example_3.svg">图片来源</a>, <a href="http://en.wikipedia.org/wiki/Pure_Data">Wikipedia</a> 和 <a href="http://puredata.info/">官方网站</a></p>
<p><img decoding="async" class="aligncenter" alt="" src="http://blog.interfacevision.com/assets/img/posts/example_visual_language_puredata_01.png" /><img alt="" /></p>
<h4 id="quartz_composer">Quartz Composer</h4>
<p><a href="http://mastersofmedia.hum.uva.nl/2011/10/24/finally-it-comes-together/">图片来源</a>, <a href="http://en.wikipedia.org/wiki/Quartz_Composer">Wikipedia</a> 和 <a href="https://developer.apple.com/technologies/mac/graphics-and-animation.html">官方网站</a></p>
<p><img decoding="async" class="aligncenter" alt="" src="http://blog.interfacevision.com/assets/img/posts/example_visual_language_quartz_01.jpg" /><img alt="" /></p>
<h4 id="reaktor">Reaktor</h4>
<p><a href="http://media.soundonsound.com/sos/oct99/images/reaktor5.gif">图片来源</a>, <a href="http://en.wikipedia.org/wiki/Reaktor">Wikipedia</a> 和 <a href="http://www.native-instruments.com/en/products/komplete/synths-samplers/reaktor-5/">官方网站</a></p>
<p><img decoding="async" class="aligncenter" alt="" src="http://blog.interfacevision.com/assets/img/posts/example_visual_language_reaktor_01.gif" /><img alt="" /></p>
<h4 id="scheme_bricks">Scheme Bricks</h4>
<p><a href="http://www.pawfal.org/dave/blog/2010/05/scheme-bricks-for-graphics/">图片来源</a> 和 <a href="http://www.pawfal.org/dave/index.cgi?Projects/Scheme%20Bricks">官方网站</a></p>
<p><img decoding="async" class="aligncenter" alt="" src="http://blog.interfacevision.com/assets/img/posts/example_visual_language_schemebricks_01.png" /><img alt="" /></p>
<h4 id="scratch">Scratch</h4>
<p><a href="http://scratch.mit.edu/projects/11126006/#editor">图片来源 1</a>, <a href="http://scratch.mit.edu/projects/11126006/#editor">图片来源 2</a>, <a href="http://en.wikipedia.org/wiki/Scratch_%28programming_language%29">Wikipedia</a> 和 <a href="http://scratch.mit.edu/">官方网站</a></p>
<p><img decoding="async" class="aligncenter" alt="" src="http://blog.interfacevision.com/assets/img/posts/example_visual_language_scratch_01.png" /><img alt="" /></p>
<p><img decoding="async" class="aligncenter" alt="" src="http://blog.interfacevision.com/assets/img/posts/example_visual_language_scratch_02.png" /><img alt="" /></p>
<h4 id="self">Self</h4>
<p><a href="http://handbook.selflanguage.org/current/langref.html#objects">图片来源</a>, <a href="http://en.wikipedia.org/wiki/Self_%28programming_language%29">Wikipedia</a> 和 <a href="http://selflanguage.org/">官方网站</a></p>
<p><img decoding="async" class="aligncenter" alt="" src="http://blog.interfacevision.com/assets/img/posts/example_visual_language_self_01.png" /><img alt="" /></p>
<h4 id="sextante">Sextante</h4>
<p><a href="http://www.gvsig.com/files/images/screenshots/gvSIG_Sextante_02.png">图片来源</a> 和 <a href="http://sextantegis.com/">官方网站</a></p>
<p><img decoding="async" class="aligncenter" alt="" src="http://blog.interfacevision.com/assets/img/posts/example_visual_language_sextante_01.png" /><img alt="" /></p>
<h4 id="simulink">Simulink</h4>
<p><a href="http://www.mathworks.com/products/simulink/?s_cid=wiki_simulink_8">图片来源</a>, <a href="http://en.wikipedia.org/wiki/Simulink">Wikipedia</a> 和 <a href="http://www.mathworks.com/products/simulink/?s_cid=wiki_simulink_8">官方网站</a></p>
<p><img decoding="async" class="aligncenter" alt="" src="http://blog.interfacevision.com/assets/img/posts/example_visual_language_simlink_01.png" /><img alt="" /></p>
<h4 id="sikuli">Sikuli</h4>
<p><a href="http://hellotestworld.com/2012/04/27/sikuli-for-all-those-hard-to-reach-places/">图片来源</a>, <a href="http://en.wikipedia.org/wiki/Sikuli">Wikipedia</a> 和 <a href="http://www.sikuli.org">官方网站</a></p>
<p><img decoding="async" class="aligncenter" alt="" src="http://blog.interfacevision.com/assets/img/posts/example_visual_language_sikuli_01.png" /><img alt="" /></p>
<h4 id="sql_server_integration_services">SQL Server Integration Services</h4>
<p><a href="http://technet.microsoft.com/en-us/library/cc917721.aspx">图片来源</a>, <a href="http://en.wikipedia.org/wiki/SQL_Server_Integration_Services">Wikipedia</a> 和 <a href="http://www.microsoft.com/en-us/sqlserver/solutions-technologies/enterprise-information-management/integration-services.aspx">官方网站</a></p>
<p><img decoding="async" class="aligncenter" alt="" src="http://blog.interfacevision.com/assets/img/posts/example_visual_language_sqlintegration_01.png" /><img alt="" /></p>
<h4 id="story_code">Story Code</h4>
<p><a href="http://softconstructors.com/en/applications/stroycode/screenshots.html">图片来源</a> 和 <a href="http://softconstructors.com/en/applications/stroycode/">官方网站</a></p>
<p><img decoding="async" class="aligncenter" alt="" src="http://blog.interfacevision.com/assets/img/posts/example_visual_language_stroycode_01.png" /><img alt="" /></p>
<h4 id="textit">TextIt</h4>
<p><a href="https://textit.in/">图片来源</a> 和 <a href="https://textit.in/">官方网站</a></p>
<p><img decoding="async" class="aligncenter" alt="" src="http://blog.interfacevision.com/assets/img/posts/example_visual_language_textit_01.png" /><img alt="" /></p>
<h4 id="touch_develop">Touch Develop</h4>
<p>From Microsoft research.</p>
<p><a href="http://handheld.softpedia.com/progScreenshots/TouchDevelop-Screenshot-125731.html">图片来源</a> 和 <a href="https://www.touchdevelop.com/">官方网站</a></p>
<p><img decoding="async" class="aligncenter" alt="" src="http://blog.interfacevision.com/assets/img/posts/example_visual_language_touchdevelop_01.jpg" /><img alt="" /></p>
<h4 id="tydlig">Tydlig</h4>
<p><a href="http://tydligapp.com/images/screenshots/1-physics.png">图片来源</a> 和 <a href="http://tydligapp.com/">官方网站</a></p>
<p><img decoding="async" class="aligncenter" alt="" src="http://blog.interfacevision.com/assets/img/posts/example_visual_language_tydlig_01.png" /><img alt="" /></p>
<h4 id="udk">UDK</h4>
<p><a href="http://www.youtube.com/watch?v=0OR63rDN5p8">图片来源</a> 和 <a href="http://www.unrealengine.com/en/udk/">官方网站</a></p>
<p><img decoding="async" class="aligncenter" alt="" src="http://blog.interfacevision.com/assets/img/posts/example_visual_language_udk_01.png" /><img alt="" /></p>
<h4 id="visual_jforex">Visual JForex</h4>
<p><a href="http://i1.ytimg.com/vi/iz5numHchGU/maxresdefault.jpg">图片来源</a> 和 <a href="http://www.dukascopy.com/swiss/english/forex/Visual/features/">官方网站</a></p>
<p><img decoding="async" class="aligncenter" alt="" src="http://blog.interfacevision.com/assets/img/posts/example_visual_language_jforex_01.jpg" /><img alt="" /></p>
<h4 id="vuo">VUO</h4>
<p><a href="http://www.vjunion.se/2013/03/a-great-start-to-the-new-year/">图片来源</a> 和 <a href="http://vuo.org/">官方网站</a></p>
<p><img decoding="async" class="aligncenter" alt="" src="http://blog.interfacevision.com/assets/img/posts/example_visual_language_vuo_01.png" /><img alt="" /></p>
<h4 id="vvvv">VVVV</h4>
<p><a href="http://vvvv.org/contribution/vvvv.packs.image">图片来源 1</a>, <a href="http://kristiansmusicproductionblog.com/wp-content/uploads/vvvv.png">图片来源 2</a>, <a href="http://en.wikipedia.org/wiki/Vvvv">Wikipedia</a> 和 <a href="http://www.vvvv.org/">官方网站</a></p>
<p><img decoding="async" class="aligncenter" alt="" src="http://blog.interfacevision.com/assets/img/posts/example_visual_language_vvvv_01.png" /><img alt="" /></p>
<p><img decoding="async" class="aligncenter" alt="" src="http://blog.interfacevision.com/assets/img/posts/example_visual_language_vvvv_02.png" /><img alt="" /></p>
<h4 id="windows_workflow_foundation">Windows Workflow Foundation</h4>
<p><a href="http://fryerblog.com/post/2179029238/a-windows-workflow-foundation-example">图片来源</a>, <a href="http://en.wikipedia.org/wiki/Windows_Workflow_Foundation">Wikipedia</a> 和 <a href="http://msdn.microsoft.com/en-us/vstudio/jj684582.aspx">官方网站</a></p>
<p><img decoding="async" class="aligncenter" alt="" src="http://blog.interfacevision.com/assets/img/posts/example_visual_language_winworkflow_01.png" /><img alt="" /></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/19612.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2019/07/1920px-Margaret_Hamilton_-_restoration-e1563697198766-1-150x150.jpg" alt="50年前的登月程序和程序员有多硬核" width="150" height="150" /></a><a href="https://coolshell.cn/articles/19612.html" class="wp_rp_title">50年前的登月程序和程序员有多硬核</a></li><li ><a href="https://coolshell.cn/articles/17929.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2017/06/go-hardhat-150x150.png" alt="Go编程模式：修饰器" width="150" height="150" /></a><a href="https://coolshell.cn/articles/17929.html" class="wp_rp_title">Go编程模式：修饰器</a></li><li ><a href="https://coolshell.cn/articles/17757.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2017/04/IMG_7411-150x150.jpg" alt="如何重构“箭头型”代码" width="150" height="150" /></a><a href="https://coolshell.cn/articles/17757.html" class="wp_rp_title">如何重构“箭头型”代码</a></li><li ><a href="https://coolshell.cn/articles/12052.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg" alt="Leetcode 编程训练" width="150" height="150" /></a><a href="https://coolshell.cn/articles/12052.html" class="wp_rp_title">Leetcode 编程训练</a></li><li ><a href="https://coolshell.cn/articles/11656.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2014/06/software_development-150x150.png" alt="开发团队的效率" width="150" height="150" /></a><a href="https://coolshell.cn/articles/11656.html" class="wp_rp_title">开发团队的效率</a></li><li ><a href="https://coolshell.cn/articles/11265.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960-150x150.jpg" alt="Python修饰器的函数式编程" width="150" height="150" /></a><a href="https://coolshell.cn/articles/11265.html" class="wp_rp_title">Python修饰器的函数式编程</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/11094.html">可视化编程</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/11094.html/feed</wfw:commentRss>
			<slash:comments>48</slash:comments>
		
		
			</item>
		<item>
		<title>Bret Victor &#8211; Learnable Programming</title>
		<link>https://coolshell.cn/articles/8387.html</link>
					<comments>https://coolshell.cn/articles/8387.html#comments</comments>
		
		<dc:creator><![CDATA[陈皓]]></dc:creator>
		<pubDate>Sun, 14 Oct 2012 08:37:04 +0000</pubDate>
				<category><![CDATA[杂项资源]]></category>
		<category><![CDATA[程序设计]]></category>
		<category><![CDATA[编程语言]]></category>
		<category><![CDATA[Bret Victor]]></category>
		<category><![CDATA[Programmer]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[程序员]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=8387</guid>

					<description><![CDATA[<p>大家是否还记得之前酷壳向大家介绍的苹果设计师Bret Victor一种可视编程的视频《Bret Victor – Inventing on Principle》...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/8387.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/8387.html">Bret Victor – Learnable Programming</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="http://worrydream.com/" target="_blank">Bret Victor</a>一种可视编程的视频《<a href="https://coolshell.cn/articles/6775.html" rel="bookmark">Bret Victor – Inventing on Principle</a>》，最近，他写了一篇文章——<a href="http://worrydream.com/LearnableProgramming/" target="_blank"> Learnable Programming</a>，写这篇文章的原因是因为“可汗学院(Khan Academy)”近期上线的一个<a href="http://www.khanacademy.org/cs" target="_blank">在线编程环境</a>，根据他的演讲提供了一堆基于Javascript的“实时编程”的环境，因为这个环境是<a href="http://ejohn.org/blog/introducing-khan-cs" target="_blank">引用了他的想法</a>，所以，他有必要出来喷两句。</p>
<p>这篇文章的开头就是一个问题——“<em>How do we get people to understand programming?</em>”，我们怎么让人们懂得编程？</p>
<p><img decoding="async" loading="lazy" class="aligncenter size-full wp-image-8389" title="Learnable_Programming" src="https://coolshell.cn/wp-content/uploads/2012/10/Learnable_Programming.jpg" alt="" width="650" height="207" srcset="https://coolshell.cn/wp-content/uploads/2012/10/Learnable_Programming.jpg 650w, https://coolshell.cn/wp-content/uploads/2012/10/Learnable_Programming-300x96.jpg 300w, https://coolshell.cn/wp-content/uploads/2012/10/Learnable_Programming-604x192.jpg 604w" sizes="(max-width: 650px) 100vw, 650px" /></p>
<p>然后，他说了两条——</p>
<ul>
<li><strong>编程是一种思考，而不是一种死记硬背的技能！</strong>你学会了“for循环”并不是说你就学会了编程，这就好像你知道有铅笔这个东西，但是你对绘画还是什么不懂。（对于这一条，正好这两天我在微博上和人辩论“<a href="http://weibo.com/1401880315/yFQkJn8bC" target="_blank">基础算法面试题是否好</a>”（还有<a href="http://weibo.com/1401880315/yFOeyy00M" target="_blank">微博一</a>，<a href="http://weibo.com/1401880315/z06Y0qMGf" target="_blank">微博二</a>），而且我以前也写过一篇《<a title="为什么我反对纯算法面试题" href="https://coolshell.cn/articles/8138.html" target="_blank">为什么我反对纯算法面试</a>》，这里借用Bret的话再加强一下我的观点——“<strong>我们一方面在骂中国的应试教育毁了学生，另一方面我们又在把我们的面试变成“考八股文”式的考试！  你会qsort有什么用？你只不过是会用一支高级铅笔而已罢了。</strong>”）</li>
</ul>
<ul>
<li><strong>人只有看得见，才能理解。</strong>如果一个程序员不能看到他的程序在干什么，那么她就不能理解程序。（对于这一条，让我想到了Donald Knuth的话——“An algorithm must be seen to be believe!”）</li>
</ul>
<p>所以，Bret 觉得编程软件的目标是——</p>
<p><span id="more-8387"></span></p>
<ul>
<li>支持并激发强大的思考。 To support and encourage powerful ways of thinking.</li>
<li>让程序员可以看得见程序的运行过程。To enable programmers to see and understand the execution of their programs</li>
</ul>
<p>他说，可汗学院的“实时编程环境”并没有达到上面的任何一个目标。他还说用Javascript这样设计得很垃圾的语言根本不能支持强大的思考，而且还忽略了近十年来的成果，可汗学院这些东西完全是毫无价值的。</p>
<p>Bret认为，Alan Perlis的名言——“要学会编程，你必需得同时变成机器和程序”是错误的，这句被广为流传的错误名言，让我们把编程变成很难，并且掩盖了编程的艺术。人并不是一台机器，我们也不应该强迫自己变成那样。</p>
<p>接下来，他说明了一个编程系统应该有两个部分——</p>
<ul>
<li><strong>编程的“环境”，是其中一部分需要安装在电脑上的。</strong></li>
</ul>
<ul>
<li><strong>编程的“语言”，是另一部分需要安装在程序员大脑里的。</strong></li>
</ul>
<p>他随笔给出来了一些Design Principles——</p>
<p>对于“<strong>编程环境</strong>”，应该能让学习者干下面的事：</p>
<ul>
<li><strong>阅读程序词汇 read the vocabulary</strong> <em>&#8212; </em>这些单词意味着什么？是不是显而易见不用思考的？是不是很自然地被上下文解释了？</li>
</ul>
<ul>
<li><strong>跟进流程 follow the flow</strong> <em>&#8212; </em>在什么时候会发生什么？流程的时间过程是不是看得见摸得着的？流程的粒度是否有意义？</li>
</ul>
<ul>
<li><strong>看见状态 see the state</strong> <em>&#8212; </em>电脑在想些什么？你能不能看到电脑里的数据？并可以看到不同状态的比较？没有任何状态会隐藏？</li>
</ul>
<ul>
<li><strong>通过交互来创造代码 create by reacting</strong> <em>&#8212; </em>从粗糙开始，然后开始雕琢程序。交互是否实时显示在屏幕上？有多少组件我可以用来做实时交互？</li>
</ul>
<ul>
<li><strong>通过抽像来创造代码 create by abstracting</strong> <em>&#8212; </em>从一些hard code开始，然后开始抽象成变量<em>，</em>抽象成公式，抽象成函数。从一个开始作模板，然后做多个不同的东西。</li>
</ul>
<p>对于“<strong>编程语言</strong>” 来说，它应该提供下面的事：</p>
<ul>
<li><strong>同一性和比方 identity and metaphor</strong> <em>&#8212; </em>我怎么把电脑的世界和我的世界联系起来?<em> </em>推荐了一本书《<em><a href="http://books.google.com/books?id=HhIEAgUfGHwC&amp;printsec=frontcover">&#8220;Mindstorms&#8221;</a></em>》</li>
</ul>
<ul>
<li><strong>分解 decomposition</strong> <em>&#8212; </em>怎么把我的想法分解成碎片？<em>how do I break down my thoughts into mind-sized pieces?</em></li>
<li><strong>重组 recomposition</strong> <em>&#8212; </em>怎么把这些碎片重组起来？<em> how do I glue pieces together?</em></li>
<li><strong>可读性 readability</strong> <em>&#8212; </em>这一大堆程序单词是什么意思？<em>what do these words mean?</em></li>
</ul>
<p>然后，他说“The Features are not the point”，<strong>我们很多时候会关注编程环境和编程语言提供的功能，这就好像我们在看一本书有哪些单词一样，有哪些单词不重要，重要的是我这些单词组合起来传达了一个什么信息</strong>？<strong>一个设计的好的系统并不是一堆功能，一个设计得好的编程环境是激发特定的思考方式</strong>。所有的功能都是非常小心翼翼地组合起来为之服务。（不好意思，我又要插一句。我觉得这和我在《<a title="抄袭，腾讯 和 产品" href="https://coolshell.cn/articles/7617.html" target="_blank">抄袭，腾讯和产品</a>》一文中，我所理解的“什么是真正的产品”有点类似——真正的产品不是功能的组合，而是要表达的价值和对某一特定问题端到端的解决方案）</p>
<p>接下来，Bret用大量的示例告诉了大家上面所说的那几条是具体是什么。大家一定要去读一读！（我把这些东西总结果在上面的那些条目中了）</p>
<p>最后，Bret说了一下，他被问过很多次——这些漂亮的想法怎么应用到现实世界中？他说这个问题问的是对的，但是这些问题问的就好像是——“怎么能让一匹马从内燃机引擎受益”一样，其假设的改变是错误的。他回答到，更准确的是——“<strong>Programming has to work like this</strong>”，所以他说，他的这些东西不是一种“Training”，也不是一种“银弹”，只不过是拿开了眼罩。</p>
<p><strong>更新：</strong>一楼回复的朋友给了一个中译版的链接：<a href="http://chengyichao.info/learnable-programming/">http://chengyichao.info/learnable-programming/</a></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/19612.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2019/07/1920px-Margaret_Hamilton_-_restoration-e1563697198766-1-150x150.jpg" alt="50年前的登月程序和程序员有多硬核" width="150" height="150" /></a><a href="https://coolshell.cn/articles/19612.html" class="wp_rp_title">50年前的登月程序和程序员有多硬核</a></li><li ><a href="https://coolshell.cn/articles/12052.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg" alt="Leetcode 编程训练" width="150" height="150" /></a><a href="https://coolshell.cn/articles/12052.html" class="wp_rp_title">Leetcode 编程训练</a></li><li ><a href="https://coolshell.cn/articles/6775.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg" alt="Bret Victor &#8211; Inventing on Principle" width="150" height="150" /></a><a href="https://coolshell.cn/articles/6775.html" class="wp_rp_title">Bret Victor &#8211; Inventing on Principle</a></li><li ><a href="https://coolshell.cn/articles/22298.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png" alt="聊聊团队协同和协同工具" width="150" height="150" /></a><a href="https://coolshell.cn/articles/22298.html" class="wp_rp_title">聊聊团队协同和协同工具</a></li><li ><a href="https://coolshell.cn/articles/22173.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png" alt="“一把梭：REST API 全用 POST”" width="150" height="150" /></a><a href="https://coolshell.cn/articles/22173.html" class="wp_rp_title">“一把梭：REST API 全用 POST”</a></li><li ><a href="https://coolshell.cn/articles/22157.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg" alt="谈谈公司对员工的监控" width="150" height="150" /></a><a href="https://coolshell.cn/articles/22157.html" class="wp_rp_title">谈谈公司对员工的监控</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/8387.html">Bret Victor – Learnable Programming</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/8387.html/feed</wfw:commentRss>
			<slash:comments>33</slash:comments>
		
		
			</item>
		<item>
		<title>狗日的开源软件许可证</title>
		<link>https://coolshell.cn/articles/4657.html</link>
					<comments>https://coolshell.cn/articles/4657.html#comments</comments>
		
		<dc:creator><![CDATA[陈皓]]></dc:creator>
		<pubDate>Wed, 04 May 2011 00:25:17 +0000</pubDate>
				<category><![CDATA[杂项资源]]></category>
		<category><![CDATA[编程工具]]></category>
		<category><![CDATA[轶事趣闻]]></category>
		<category><![CDATA[BSD]]></category>
		<category><![CDATA[DBAD]]></category>
		<category><![CDATA[GNU]]></category>
		<category><![CDATA[GPL]]></category>
		<category><![CDATA[MIT]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[WTFPL]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=4657</guid>

					<description><![CDATA[<p>你知道这个世上有多少种开源软件的许可证吗？GPL，BSD，MIT，Apache？GNU上有个网页，上面记录了几乎所有的开源软件的许可证，真TMD的多，有开源的，...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/4657.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/4657.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>你知道这个世上有多少种开源软件的许可证吗？GPL，BSD，MIT，Apache？GNU上有个网页，上面<a href="http://www.gnu.org/licenses/license-list.html" target="_blank">记录了几乎所有的开源软件的许可证</a>，真TMD的多，有开源的，有商用的，有软件的，有文档的，多得你都不想看了，天杀的，程序员们还真能鼓捣啊。不过，主流的也就几种——<a href="http://www.gnu.org/licenses/gpl.html" target="_blank">GPL</a><a href="https://coolshell.cn/wp-content/uploads/2011/05/OSS-License.jpg"></a>、<a href="http://en.wikipedia.org/wiki/BSD_licenses">BSD</a>、<a href="http://en.wikipedia.org/wiki/MIT_License">MIT</a>、<a href="http://www.mozilla.org/MPL/">Mozilla</a>、<a href="http://www.apache.org/licenses/LICENSE-2.0">Apache</a>等等。</p>
<p>那么，你知道怎么区别他们吧？怎么选择他们吗？这里有一张比较复杂的图，在调侃这些纷繁的许可证（我不翻译了，这个图属于是发泄不满）</p>
<p style="text-align: center;"><a href="https://coolshell.cn/wp-content/uploads/2011/05/OSS-License.jpg"><img decoding="async" loading="lazy" title="OSS License" src="https://coolshell.cn/wp-content/uploads/2011/05/OSS-License.jpg" alt="" width="567" height="730" /></a></p>
<p>下面是另一个图，这个图<a href="http://pbagwl.com/post/5078147450/description-of-popular-software-licenses" target="_blank">来自这里</a><a href="https://coolshell.cn/wp-content/uploads/2011/05/Infographic-of-popular-software-licenses.jpg"></a>，这个图并不恶搞，但其非常简单地说明了如何选择一个开源的许可证：</p>
<p><span id="more-4657"></span></p>
<p style="text-align: center;"><a href="https://coolshell.cn/wp-content/uploads/2011/05/Infographic-of-popular-software-licenses.jpg"><img decoding="async" loading="lazy" title="Infographic of popular software licenses" src="https://coolshell.cn/wp-content/uploads/2011/05/Infographic-of-popular-software-licenses.jpg" alt="" width="560" height="446" /></a></p>
<p style="text-align: left;">最后，正如<a title="BT雷人的程序语言（大全）" href="https://coolshell.cn/articles/4458.html" target="_blank">那些BT雷人的程序语言</a>一样，我想介绍两个比较独特的开源软件许可证给你，以辉映本文的标题——</p>
<h4 style="text-align: left;">1、WTFPL</h4>
<p style="text-align: left;"><a href="http://sam.zoy.org/wtfpl/COPYING" target="_blank">WTFPL</a>全称 What The Fuck Public License，这个许可证单从名字上就那么NB了，其许可证如下，相当的短，完全的自由，你的开源软件有自信用这个许可证吗？</p>
<pre>            DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
                    Version 2, December 2004

 Copyright (C) 2004 Sam Hocevar &lt;sam@hocevar.net&gt;

 Everyone is permitted to copy and distribute verbatim or modified
 copies of this license document, and changing it is allowed as long
 as the name is changed.

            DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. You just DO WHAT THE FUCK YOU WANT TO.</pre>
<p style="text-align: left;">最后那句——You just DO WHAT THE FUCK YOU WANT TO 真是铿锵有力，怎么说怎么痛快，很有一种在看美国大片的感觉。这是我喜欢这个许可证的原因之一，即不限制你控制版权，也不限制你放弃版权！</p>
<h4 style="text-align: left;">2、DBAD</h4>
<p style="text-align: left;"><a href="https://github.com/SFEley/candy/blob/2f964916961a2dcccbb374cd389520ac2ac62226/LICENSE.markdown" target="_blank">DBAD</a>全称 Don&#8217;t Be A Dick，dick是什么我就不解释了，你自己查字典吧。这个许可证中定义了什么是dick，</p>
<blockquote>
<p style="text-align: left;">A person who <em>does not</em> respect the time and energy that have been invested in the Project, ……. A Dick is nearly always selfish, but not necessarily with deliberate intent; some Dicks are merely thoughtless. ……</p>
</blockquote>
<p style="text-align: left;">也就是项目中扯淡的人。这个许可证最NB的地方在于其不限制软件的版权，而是限制了软件开发中的人的行为。我真是太喜欢这个许可证了。（请参看其第四节Limitation ）</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/3723.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg" alt="（麻省理工免费课程）计算机科学和编程导论" width="150" height="150" /></a><a href="https://coolshell.cn/articles/3723.html" class="wp_rp_title">（麻省理工免费课程）计算机科学和编程导论</a></li><li ><a href="https://coolshell.cn/articles/19996.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2019/11/ken.dennis-300x186-1-150x150.jpeg" alt="Unix 50 年：Ken Thompson 的密码" width="150" height="150" /></a><a href="https://coolshell.cn/articles/19996.html" class="wp_rp_title">Unix 50 年：Ken Thompson 的密码</a></li><li ><a href="https://coolshell.cn/articles/19612.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2019/07/1920px-Margaret_Hamilton_-_restoration-e1563697198766-1-150x150.jpg" alt="50年前的登月程序和程序员有多硬核" width="150" height="150" /></a><a href="https://coolshell.cn/articles/19612.html" class="wp_rp_title">50年前的登月程序和程序员有多硬核</a></li><li ><a href="https://coolshell.cn/articles/17929.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2017/06/go-hardhat-150x150.png" alt="Go编程模式：修饰器" width="150" height="150" /></a><a href="https://coolshell.cn/articles/17929.html" class="wp_rp_title">Go编程模式：修饰器</a></li><li ><a href="https://coolshell.cn/articles/17757.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2017/04/IMG_7411-150x150.jpg" alt="如何重构“箭头型”代码" width="150" height="150" /></a><a href="https://coolshell.cn/articles/17757.html" class="wp_rp_title">如何重构“箭头型”代码</a></li><li ><a href="https://coolshell.cn/articles/12052.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg" alt="Leetcode 编程训练" width="150" height="150" /></a><a href="https://coolshell.cn/articles/12052.html" class="wp_rp_title">Leetcode 编程训练</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/4657.html">狗日的开源软件许可证</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/4657.html/feed</wfw:commentRss>
			<slash:comments>23</slash:comments>
		
		
			</item>
		<item>
		<title>（麻省理工免费课程）计算机科学和编程导论</title>
		<link>https://coolshell.cn/articles/3723.html</link>
					<comments>https://coolshell.cn/articles/3723.html#comments</comments>
		
		<dc:creator><![CDATA[陈皓]]></dc:creator>
		<pubDate>Mon, 28 Feb 2011 00:25:07 +0000</pubDate>
				<category><![CDATA[技术读物]]></category>
		<category><![CDATA[杂项资源]]></category>
		<category><![CDATA[程序设计]]></category>
		<category><![CDATA[编程语言]]></category>
		<category><![CDATA[Course]]></category>
		<category><![CDATA[ebook]]></category>
		<category><![CDATA[MIT]]></category>
		<category><![CDATA[Programming]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=3723</guid>

					<description><![CDATA[<p>以前本站推荐过麻省理工的C/C++的课程，今天在他们的网站看到上有一组关于计算机科学和编程导论的免费公开课（视频是Youtube的），我看了几个课程，我觉得讲得...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/3723.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/3723.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>以前本站推荐过<a href="https://coolshell.cn/articles/2474.html" target="_blank">麻省理工的C/C++的课程</a>，今天在他们的网站看到上有一组关于<a href="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/" target="_blank">计算机科学和编程导论的免费公开课</a>（视频是Youtube的），我看了几个课程，我觉得讲得很系统啊，而且有一点一通百通的感觉。虽然是理论课，但是可以感到我国的教育还是有很大差距的。这个组课程推荐给大家（需要翻墙），视频都有字幕，计算机科学系毕业的同学应该会很容易听懂。强烈推荐。（网友Aslan指出已经有人搬运到优酷上了，<a href="http://www.youku.com/playlist_show/id_3940564_ascending_1_mode_pic_page_1.html" target="_blank">链接在这里</a>，遗憾的是没有字幕，另外，不知道为什么会说是Python学习）</p>
<table width="100%">
<tbody>
<tr>
<td width="120"><a title="1: Introduction and Goals; Data Types, Operators, and Variables" href="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-1">&nbsp;</p>
<p></a><a title="1: Introduction and Goals; Data Types, Operators, and Variables" href="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-1"><img decoding="async" src="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-1/lec01.jpg" alt="" /></a></td>
<td>
<p class="mediatitle">1: Introduction and Goals; Data Types, Operators, and Variables</p>
<p><a class="bullet medialink" href="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-1">Youtube（英文字幕）</a></p>
<p><a href="http://v.youku.com/v_playlist/f4862914o1p0.html" target="_blank">优酷（无字幕）</a></td>
</tr>
</tbody>
</table>
<table width="100%">
<tbody>
<tr>
<td width="120"><a title="2: Branching, Conditionals, and Iteration" href="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-2">&nbsp;</p>
<p></a><a title="2: Branching, Conditionals, and Iteration" href="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-2"><img decoding="async" src="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-2/lec02.jpg" alt="" /></a></td>
<td>
<p class="mediatitle">2: Branching, Conditionals, and Iteration</p>
<p><a class="bullet medialink" href="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-2">Youtube（英文字幕）</a></p>
<p><a href="http://v.youku.com/v_playlist/f4862914o1p1.html" target="_blank">优酷（无字幕）</a></td>
</tr>
</tbody>
</table>
<table width="100%">
<tbody>
<tr>
<td width="120"><a title="3: Common Code Patterns: Iterative Programs" href="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-3">&nbsp;</p>
<p></a><a title="3: Common Code Patterns: Iterative Programs" href="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-3"><img decoding="async" src="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-3/lec03.jpg" alt="" /></a></td>
<td>
<p class="mediatitle">3: Common Code Patterns: Iterative Programs</p>
<p><a class="bullet medialink" href="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-3">Youtube（英文字幕）</a></p>
<p><a href="http://v.youku.com/v_playlist/f4862914o1p2.html" target="_blank">优酷（无字幕）</a></td>
</tr>
</tbody>
</table>
<p><span id="more-3723"></span></p>
<table width="100%">
<tbody>
<tr>
<td width="120"><a title="4: Abstraction through Functions; Introduction to Recursion" href="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-4">&nbsp;</p>
<p></a><a title="4: Abstraction through Functions; Introduction to Recursion" href="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-4"><img decoding="async" src="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-4/lec04.jpg" alt="" /></a></td>
<td>
<p class="mediatitle">4: Abstraction through Functions; Introduction to Recursion</p>
<p><a class="bullet medialink" href="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-4">Youtube（英文字幕）</a></p>
<p><a href="http://v.youku.com/v_playlist/f4862914o1p3.html" target="_blank">优酷（无字幕）</a></td>
</tr>
</tbody>
</table>
<table width="100%">
<tbody>
<tr>
<td width="120"><a title="5: Floating Point Numbers, Successive Refinement, Finding Roots" href="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-5">&nbsp;</p>
<p></a><a title="5: Floating Point Numbers, Successive Refinement, Finding Roots" href="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-5"><img decoding="async" src="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-5/lec05.jpg" alt="" /></a></td>
<td>
<p class="mediatitle">5: Floating Point Numbers, Successive Refinement, Finding Roots</p>
<p><a class="bullet medialink" href="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-5">Youtube（英文字幕）</a></p>
<p><a href="http://v.youku.com/v_playlist/f4862914o1p4.html" target="_blank">优酷（无字幕）</a></td>
</tr>
</tbody>
</table>
<table width="100%">
<tbody>
<tr>
<td width="120"><a title="6: Bisection Methods, Newton/Raphson, Introduction to Lists" href="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-6">&nbsp;</p>
<p></a><a title="6: Bisection Methods, Newton/Raphson, Introduction to Lists" href="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-6"><img decoding="async" src="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-6/lec06.jpg" alt="" /></a></td>
<td>
<p class="mediatitle">6: Bisection Methods, Newton/Raphson, Introduction to Lists</p>
<p><a class="bullet medialink" href="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-6">Youtube（英文字幕）</a></p>
<p><a href="http://v.youku.com/v_playlist/f4862914o1p5.html" target="_blank">优酷（无字幕）</a></td>
</tr>
</tbody>
</table>
<table width="100%">
<tbody>
<tr>
<td width="120"><a title="7: Lists and Mutability, Dictionaries, Introduction to Efficiency" href="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-7">&nbsp;</p>
<p></a><a title="7: Lists and Mutability, Dictionaries, Introduction to Efficiency" href="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-7"><img decoding="async" src="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-7/lec07.jpg" alt="" /></a></td>
<td>
<p class="mediatitle">7: Lists and Mutability, Dictionaries, Introduction to Efficiency</p>
<p><a class="bullet medialink" href="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-7">Youtube（英文字幕）</a></p>
<p><a href="http://v.youku.com/v_playlist/f4862914o1p6.html" target="_blank">优酷（无字幕）</a></td>
</tr>
</tbody>
</table>
<table width="100%">
<tbody>
<tr>
<td width="120"><a title="8: Complexity: Log, Linear, Quadratic, Exponential Algorithms" href="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-8">&nbsp;</p>
<p></a><a title="8: Complexity: Log, Linear, Quadratic, Exponential Algorithms" href="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-8"><img decoding="async" src="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-8/lec08.jpg" alt="" /></a></td>
<td>
<p class="mediatitle">8: Complexity: Log, Linear, Quadratic, Exponential Algorithms</p>
<p><a class="bullet medialink" href="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-8">Youtube（英文字幕）</a></p>
<p><a href="http://v.youku.com/v_playlist/f4862914o1p7.html" target="_blank">优酷（无字幕）</a></td>
</tr>
</tbody>
</table>
<table width="100%">
<tbody>
<tr>
<td width="120"><a title="9: Binary Search, Bubble and Selection Sorts" href="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-9">&nbsp;</p>
<p></a><a title="9: Binary Search, Bubble and Selection Sorts" href="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-9"><img decoding="async" src="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-9/lec09.jpg" alt="" /></a></td>
<td>
<p class="mediatitle">9: Binary Search, Bubble and Selection Sorts</p>
<p><a class="bullet medialink" href="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-9">Youtube（英文字幕）</a></p>
<p><a href="http://v.youku.com/v_playlist/f4862914o1p8.html" target="_blank">优酷（无字幕）</a></td>
</tr>
</tbody>
</table>
<table width="100%">
<tbody>
<tr>
<td width="120"><a title="10: Divide and Conquer Methods, Merge Sort, Exceptions" href="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-10">&nbsp;</p>
<p></a><a title="10: Divide and Conquer Methods, Merge Sort, Exceptions" href="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-10"><img decoding="async" src="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-10/lec10.jpg" alt="" /></a></td>
<td>
<p class="mediatitle">10: Divide and Conquer Methods, Merge Sort, Exceptions</p>
<p><a class="bullet medialink" href="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-10">Youtube（英文字幕）</a></p>
<p><a href="http://v.youku.com/v_playlist/f4862914o1p9.html" target="_blank">优酷（无字幕）</a></td>
</tr>
</tbody>
</table>
<table width="100%">
<tbody>
<tr>
<td width="120"><a title="11: Testing and Debugging" href="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-11">&nbsp;</p>
<p></a><a title="11: Testing and Debugging" href="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-11"><img decoding="async" src="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-11/lec11.jpg" alt="" /></a></td>
<td>
<p class="mediatitle">11: Testing and Debugging</p>
<p><a class="bullet medialink" href="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-11">Youtube（英文字幕）</a></p>
<p><a href="http://v.youku.com/v_playlist/f4862914o1p10.html" target="_blank">优酷（无字幕）</a></td>
</tr>
</tbody>
</table>
<table width="100%">
<tbody>
<tr>
<td width="120"><a title="12: Debugging, Knapsack Problem, Introduction to Dynamic Programming" href="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-12">&nbsp;</p>
<p></a><a title="12: Debugging, Knapsack Problem, Introduction to Dynamic Programming" href="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-12"><img decoding="async" src="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-12/lec12.jpg" alt="" /></a></td>
<td>
<p class="mediatitle">12: Debugging, Knapsack Problem, Introduction to Dynamic Programming</p>
<p><a class="bullet medialink" href="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-12">Youtube（英文字幕）</a></p>
<p><a href="http://v.youku.com/v_playlist/f4862914o1p11.html" target="_blank">优酷（无字幕）</a></td>
</tr>
</tbody>
</table>
<table width="100%">
<tbody>
<tr>
<td width="120"><a title="13: Dynamic Programming: Overlapping Subproblems, Optimal Substructure" href="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-13">&nbsp;</p>
<p></a><a title="13: Dynamic Programming: Overlapping Subproblems, Optimal Substructure" href="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-13"><img decoding="async" src="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-13/lec13.jpg" alt="" /></a></td>
<td>
<p class="mediatitle">13: Dynamic Programming: Overlapping Subproblems, Optimal Substructure</p>
<p><a class="bullet medialink" href="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-13">Youtube（英文字幕）</a></p>
<p><a href="http://v.youku.com/v_playlist/f4862914o1p12.html" target="_blank">优酷（无字幕）</a></td>
</tr>
</tbody>
</table>
<table width="100%">
<tbody>
<tr>
<td width="120"><a title="14: Introduction to Object-oriented Programming" href="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-14">&nbsp;</p>
<p></a><a title="14: Introduction to Object-oriented Programming" href="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-14"><img decoding="async" src="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-14/lec14.jpg" alt="" /></a></td>
<td>
<p class="mediatitle">14: Introduction to Object-oriented Programming</p>
<p><a class="bullet medialink" href="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-14">Youtube（英文字幕）</a></p>
<p><a href="http://v.youku.com/v_playlist/f4862914o1p13.html" target="_blank">优酷（无字幕）</a></td>
</tr>
</tbody>
</table>
<table width="100%">
<tbody>
<tr>
<td width="120"><a title="15: Abstract Data Types, Classes and Methods" href="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-15">&nbsp;</p>
<p></a><a title="15: Abstract Data Types, Classes and Methods" href="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-15"><img decoding="async" src="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-15/lec15.jpg" alt="" /></a></td>
<td>
<p class="mediatitle">15: Abstract Data Types, Classes and Methods</p>
<p><a class="bullet medialink" href="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-15">Youtube（英文字幕）</a></p>
<p><a href="http://v.youku.com/v_playlist/f4862914o1p14.html" target="_blank">优酷（无字幕）</a></td>
</tr>
</tbody>
</table>
<table width="100%">
<tbody>
<tr>
<td width="120"><a title="16: Encapsulation, Inheritance, Shadowing" href="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-16">&nbsp;</p>
<p></a><a title="16: Encapsulation, Inheritance, Shadowing" href="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-16"><img decoding="async" src="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-16/lec16.jpg" alt="" /></a></td>
<td>
<p class="mediatitle">16: Encapsulation, Inheritance, Shadowing</p>
<p><a class="bullet medialink" href="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-16">Youtube（英文字幕）</a></p>
<p><a href="http://v.youku.com/v_playlist/f4862914o1p15.html" target="_blank">优酷（无字幕）</a></td>
</tr>
</tbody>
</table>
<table width="100%">
<tbody>
<tr>
<td width="120"><a title="17: Computational Models: Random Walk Simulation" href="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-17">&nbsp;</p>
<p></a><a title="17: Computational Models: Random Walk Simulation" href="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-17"><img decoding="async" src="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-17/lec17.jpg" alt="" /></a></td>
<td>
<p class="mediatitle">17: Computational Models: Random Walk Simulation</p>
<p><a class="bullet medialink" href="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-17">Youtube（英文字幕）</a></p>
<p><a href="http://v.youku.com/v_playlist/f4862914o1p16.html" target="_blank">优酷（无字幕）</a></td>
</tr>
</tbody>
</table>
<table width="100%">
<tbody>
<tr>
<td width="120"><a title="18: Presenting Simulation Results, Pylab, Plotting" href="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-18">&nbsp;</p>
<p></a><a title="18: Presenting Simulation Results, Pylab, Plotting" href="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-18"><img decoding="async" src="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-18/lec18.jpg" alt="" /></a></td>
<td>
<p class="mediatitle">18: Presenting Simulation Results, Pylab, Plotting</p>
<p><a class="bullet medialink" href="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-18">Youtube（英文字幕）</a></p>
<p><a href="http://v.youku.com/v_playlist/f4862914o1p17.html" target="_blank">优酷（无字幕）</a></td>
</tr>
</tbody>
</table>
<table width="100%">
<tbody>
<tr>
<td width="120"><a title="19: Biased Random Walks, Distributions" href="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-19">&nbsp;</p>
<p></a><a title="19: Biased Random Walks, Distributions" href="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-19"><img decoding="async" src="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-19/lec19.jpg" alt="" /></a></td>
<td>
<p class="mediatitle">19: Biased Random Walks, Distributions</p>
<p><a class="bullet medialink" href="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-19">Youtube（英文字幕）</a></p>
<p><a href="http://v.youku.com/v_playlist/f4862914o1p18.html" target="_blank">优酷（无字幕）</a></td>
</tr>
</tbody>
</table>
<table width="100%">
<tbody>
<tr>
<td width="120"><a title="20: Monte Carlo Simulations, Estimating pi" href="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-20">&nbsp;</p>
<p></a><a title="20: Monte Carlo Simulations, Estimating pi" href="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-20"><img decoding="async" src="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-20/lec20.jpg" alt="" /></a></td>
<td>
<p class="mediatitle">20: Monte Carlo Simulations, Estimating pi</p>
<p><a class="bullet medialink" href="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-20">Youtube（英文字幕）</a></p>
<p><a href="http://v.youku.com/v_playlist/f4862914o1p19.html" target="_blank">优酷（无字幕）</a></td>
</tr>
</tbody>
</table>
<table width="100%">
<tbody>
<tr>
<td width="120"><a title="21: Validating Simulation Results, Curve Fitting, Linear Regression" href="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-21">&nbsp;</p>
<p></a><a title="21: Validating Simulation Results, Curve Fitting, Linear Regression" href="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-21"><img decoding="async" src="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-21/lec21.jpg" alt="" /></a></td>
<td>
<p class="mediatitle">21: Validating Simulation Results, Curve Fitting, Linear Regression</p>
<p><a class="bullet medialink" href="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-21">Youtube（英文字幕）</a></p>
<p><a href="http://v.youku.com/v_playlist/f4862914o1p20.html" target="_blank">优酷（无字幕）</a></td>
</tr>
</tbody>
</table>
<table width="100%">
<tbody>
<tr>
<td width="120"><a title="22: Normal, Uniform, and Exponential Distributions" href="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-22">&nbsp;</p>
<p></a><a title="22: Normal, Uniform, and Exponential Distributions" href="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-22"><img decoding="async" src="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-22/lec22.jpg" alt="" /></a></td>
<td>
<p class="mediatitle">22: Normal, Uniform, and Exponential Distributions</p>
<p><a class="bullet medialink" href="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-22">Youtube（英文字幕）</a></p>
<p><a href="http://v.youku.com/v_playlist/f4862914o1p21.html" target="_blank">优酷（无字幕）</a></td>
</tr>
</tbody>
</table>
<table width="100%">
<tbody>
<tr>
<td width="120"><a title="23: Stock Market Simulation" href="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-23">&nbsp;</p>
<p></a><a title="23: Stock Market Simulation" href="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-23"><img decoding="async" src="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-23/lec23.jpg" alt="" /></a></td>
<td>
<p class="mediatitle">23: Stock Market Simulation</p>
<p><a class="bullet medialink" href="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-23">Youtube（英文字幕）</a></p>
<p><a href="http://v.youku.com/v_playlist/f4862914o1p22.html" target="_blank">优酷（无字幕）</a></td>
</tr>
</tbody>
</table>
<table width="100%">
<tbody>
<tr>
<td width="120"><a title="24: Course Overview; What Do Computer Scientists Do?" href="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-24">&nbsp;</p>
<p></a><a title="24: Course Overview; What Do Computer Scientists Do?" href="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-24"><img decoding="async" src="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-24/lec24.jpg" alt="" /></a></td>
<td>
<p class="mediatitle">24: Course Overview; What Do Computer Scientists Do?</p>
<p><a class="bullet medialink" href="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-24">Youtube（英文字幕）</a></p>
<p><a href="http://v.youku.com/v_playlist/f4862914o1p23.html" target="_blank">优酷（无字幕）</a></td>
</tr>
</tbody>
</table>
<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/4657.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2011/05/OSS-License-150x150.jpg" alt="狗日的开源软件许可证" width="150" height="150" /></a><a href="https://coolshell.cn/articles/4657.html" class="wp_rp_title">狗日的开源软件许可证</a></li><li ><a href="https://coolshell.cn/articles/2474.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg" alt="（麻省理工免费课程）C语言内存管理和C++面向对象编程" width="150" height="150" /></a><a href="https://coolshell.cn/articles/2474.html" class="wp_rp_title">（麻省理工免费课程）C语言内存管理和C++面向对象编程</a></li><li ><a href="https://coolshell.cn/articles/19612.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2019/07/1920px-Margaret_Hamilton_-_restoration-e1563697198766-1-150x150.jpg" alt="50年前的登月程序和程序员有多硬核" width="150" height="150" /></a><a href="https://coolshell.cn/articles/19612.html" class="wp_rp_title">50年前的登月程序和程序员有多硬核</a></li><li ><a href="https://coolshell.cn/articles/17929.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2017/06/go-hardhat-150x150.png" alt="Go编程模式：修饰器" width="150" height="150" /></a><a href="https://coolshell.cn/articles/17929.html" class="wp_rp_title">Go编程模式：修饰器</a></li><li ><a href="https://coolshell.cn/articles/17757.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2017/04/IMG_7411-150x150.jpg" alt="如何重构“箭头型”代码" width="150" height="150" /></a><a href="https://coolshell.cn/articles/17757.html" class="wp_rp_title">如何重构“箭头型”代码</a></li><li ><a href="https://coolshell.cn/articles/12052.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg" alt="Leetcode 编程训练" width="150" height="150" /></a><a href="https://coolshell.cn/articles/12052.html" class="wp_rp_title">Leetcode 编程训练</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/3723.html">（麻省理工免费课程）计算机科学和编程导论</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/3723.html/feed</wfw:commentRss>
			<slash:comments>171</slash:comments>
		
		
			</item>
	</channel>
</rss>
