<?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>Tony Bai &#187; 素数筛</title>
	<atom:link href="http://tonybai.com/tag/%e7%b4%a0%e6%95%b0%e7%ad%9b/feed/" rel="self" type="application/rss+xml" />
	<link>https://tonybai.com</link>
	<description>一个程序员的心路历程</description>
	<lastBuildDate>Sat, 30 May 2026 00:02:47 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>
		<item>
		<title>世界读书日：带你走近Go语言编程思维</title>
		<link>https://tonybai.com/2022/04/23/taking-a-closer-look-at-programming-thinking-in-go/</link>
		<comments>https://tonybai.com/2022/04/23/taking-a-closer-look-at-programming-thinking-in-go/#comments</comments>
		<pubDate>Fri, 22 Apr 2022 22:53:42 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[C]]></category>
		<category><![CDATA[Cpp]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[Golang]]></category>
		<category><![CDATA[Gopher]]></category>
		<category><![CDATA[goroutine]]></category>
		<category><![CDATA[Go语言精进之路]]></category>
		<category><![CDATA[Haskell]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[OO]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[世界读书日]]></category>
		<category><![CDATA[你一生的故事]]></category>
		<category><![CDATA[假说]]></category>
		<category><![CDATA[内存]]></category>
		<category><![CDATA[并发]]></category>
		<category><![CDATA[指针]]></category>
		<category><![CDATA[泛型]]></category>
		<category><![CDATA[素数筛]]></category>
		<category><![CDATA[编程思维]]></category>
		<category><![CDATA[萨丕尔—沃夫假说]]></category>
		<category><![CDATA[降临]]></category>
		<category><![CDATA[面向对象]]></category>

		<guid isPermaLink="false">https://tonybai.com/?p=3514</guid>
		<description><![CDATA[本文永久链接 &#8211; https://tonybai.com/2022/04/23/taking-a-closer-look-at-programming-thinking-in-go 经过十几年的演化和发展，Go语言在全世界范围内已经拥有了百万级别的拥趸，在这些开发者当中，除了一部分新入行的编程语言初学者之外，更多的是从其他编程语言阵营转过来的开发者。由于Go语言上手容易，在转Go的初期大家很快就掌握了Go的语法。 但在编写更多Go代码之后，很多人发现自己写的Go代码总是感觉很别扭，并且总是尝试在Go语言中寻找自己上一门语言中熟悉的语法元素。自己的Go代码风格似乎和Go标准库、主流Go开源项目的代码在思考角度和使用方式上存在不小差异，并且每每看到Go核心开发团队的代码时总有一种醍醐灌顶的感觉。出现这种情况的主要原因就是大脑中上一门编程语言的思维方式在“作祟”。 本文将通过《Go语言精进之路：从新手到高手的编程思想、方法与技巧》这本书的内容来详细看一看编程语言与编程思维的关系以及Go语言的编程思维究竟是什么，以帮助大家更加深入地理解Go编程。 了解Go编程思维之前，我们先看看思维与语言之间究竟有什么联系呢？ 1.语言与思维——来自大师的观点 在人类自然语言学界有一个很著名的假说——“萨丕尔—沃夫假说”，这个假说的内容是这样的：“语言影响或决定人类的思维方式。” 说到这个假说，我们不能不提及在2017年年初国内上映了一部口碑不错的美国科幻大片《降临》，这部片子改编自雨果奖获得者华裔科幻小说家Ted姜的《你一生的故事》。片中主线剧情的理论基础就是“萨丕尔—沃夫假说”。更夸张的是片中直接将该假说应用到外星人语言上，将其扩展到宇宙范畴。片中的女主作为人类代表与外星人沟通，并学会了外星语言，从此思维大变，拥有了预知未来的“超能力”，这也算是语言影响思维的极致表现了。 奇妙的是，在编程语言界，有位大师级人物也有着与“萨丕尔-沃夫假说”异曲同工的观点和认知，他就是首届图灵奖得主、著名计算机科学家艾伦·佩利（Alan J. Perlis），他从另外一个角度提出：“不能影响到你的编程思维方式的编程语言不值得去学习和使用。” 2.现实中的“投影” 从上述大师们的理论和观点，我们看到了语言与思维之间存在着某种联系。那么两者间的这种联系在真实编程世界中的投影又是什么样子的呢？我们来看一个简单的编程问题——素数筛： 问题描述：素数是一个自然数，它具有两个截然不同的自然数除数：1和它本身。这里的问题是如何找到小于或等于给定整数n的素数。针对这个问题，我们可以采用埃拉托斯特尼素数筛算法。 算法描述：先用最小的素数2去筛，把2的倍数剔除掉；下一个未筛除的数就是素数（这里是3）。再用这个素数3去筛，筛除掉3的倍数&#8230; 这样不断重复下去，直到筛完为止（算法图示见图1）。 图1 素数筛算法图示 下面是该素数筛算法的不同编程语言的实现版本。 （1）C语言版本 // sieve.c #include &#60;stdio.h&#62; #define LIMIT 50 #define PRIMES 10 void sieve() { int c, i,j,numbers[LIMIT], primes[PRIMES]; for (i=0;i&#60;LIMIT;i++){ numbers[i]=i+2; /*fill the array with natural numbers*/ } for (i=0;i&#60;LIMIT;i++){ if (numbers[i]!=-1){ for (j=2*numbers[i]-2;j&#60;LIMIT;j+=numbers[i]) [...]]]></description>
			<content:encoded><![CDATA[<p><img src="https://tonybai.com/wp-content/uploads/taking-a-closer-look-at-programming-thinking-in-go-1.jpeg" alt="" /></p>
<p><a href="https://tonybai.com/2022/04/23/taking-a-closer-look-at-programming-thinking-in-go">本文永久链接</a> &#8211; https://tonybai.com/2022/04/23/taking-a-closer-look-at-programming-thinking-in-go</p>
<p>经过十几年的演化和发展，Go语言在全世界范围内已经拥有了百万级别的拥趸，在这些开发者当中，除了一部分新入行的编程语言初学者之外，更多的是从其他编程语言阵营转过来的开发者。由于Go语言上手容易，在转Go的初期大家很快就掌握了Go的语法。</p>
<p>但在编写更多Go代码之后，很多人发现自己写的Go代码总是感觉很别扭，并且总是尝试在Go语言中寻找自己上一门语言中熟悉的语法元素。自己的Go代码风格似乎和Go标准库、主流Go开源项目的代码在思考角度和使用方式上存在不小差异，并且每每看到Go核心开发团队的代码时总有一种醍醐灌顶的感觉。出现这种情况的主要原因就是大脑中上一门编程语言的思维方式在“作祟”。</p>
<p>本文将通过<a href="https://item.jd.com/13694000.html">《Go语言精进之路：从新手到高手的编程思想、方法与技巧》</a>这本书的内容来详细看一看编程语言与编程思维的关系以及Go语言的编程思维究竟是什么，以帮助大家更加深入地理解Go编程。</p>
<p><img src="https://tonybai.com/wp-content/uploads/taking-a-closer-look-at-programming-thinking-in-go-2.png" alt="" /></p>
<p>了解Go编程思维之前，我们先看看思维与语言之间究竟有什么联系呢？</p>
<h3>1.语言与思维——来自大师的观点</h3>
<p>在人类自然语言学界有一个很著名的假说——“萨丕尔—沃夫假说”，这个假说的内容是这样的：“<strong>语言影响或决定人类的思维方式</strong>。”</p>
<p>说到这个假说，我们不能不提及在2017年年初国内上映了一部口碑不错的美国科幻大片《降临》，这部片子改编自雨果奖获得者华裔科幻小说家Ted姜的《你一生的故事》。片中主线剧情的理论基础就是“萨丕尔—沃夫假说”。更夸张的是片中直接将该假说应用到外星人语言上，将其扩展到宇宙范畴。片中的女主作为人类代表与外星人沟通，并学会了外星语言，从此思维大变，拥有了预知未来的“超能力”，这也算是语言影响思维的极致表现了。</p>
<p>奇妙的是，在编程语言界，有位大师级人物也有着与“萨丕尔-沃夫假说”异曲同工的观点和认知，他就是首届图灵奖得主、著名计算机科学家艾伦·佩利（Alan J. Perlis），他从另外一个角度提出：“不能影响到你的编程思维方式的编程语言不值得去学习和使用。”</p>
<h3>2.现实中的“投影”</h3>
<p>从上述大师们的理论和观点，我们看到了语言与思维之间存在着某种联系。那么两者间的这种联系在真实编程世界中的投影又是什么样子的呢？我们来看一个简单的编程问题——素数筛：</p>
<ul>
<li>
<p>问题描述：素数是一个自然数，它具有两个截然不同的自然数除数：1和它本身。这里的问题是如何找到小于或等于给定整数n的素数。针对这个问题，我们可以采用埃拉托斯特尼素数筛算法。</p>
</li>
<li>
<p>算法描述：先用最小的素数2去筛，把2的倍数剔除掉；下一个未筛除的数就是素数（这里是3）。再用这个素数3去筛，筛除掉3的倍数&#8230; 这样不断重复下去，直到筛完为止（算法图示见图1）。</p>
</li>
</ul>
<p><img src="https://tonybai.com/wp-content/uploads/taking-a-closer-look-at-programming-thinking-in-go-3.gif" alt="" /><br />
<center>图1 素数筛算法图示</center></p>
<p>下面是该素数筛算法的不同编程语言的实现版本。</p>
<h4>（1）C语言版本</h4>
<pre><code>// sieve.c
#include &lt;stdio.h&gt;

#define LIMIT  50
#define PRIMES 10

void sieve() {
    int c, i,j,numbers[LIMIT], primes[PRIMES];

    for (i=0;i&lt;LIMIT;i++){
        numbers[i]=i+2; /*fill the array with natural numbers*/
    }

    for (i=0;i&lt;LIMIT;i++){
        if (numbers[i]!=-1){
            for (j=2*numbers[i]-2;j&lt;LIMIT;j+=numbers[i])
                numbers[j]=-1; /* 筛除非素数 */
        }
    }

    c = j = 0;
    for (i=0;i&lt;LIMIT&amp;&amp;j&lt;PRIMES;i++) {
        if (numbers[i]!=-1) {
            primes[j++] = numbers[i]; /*transfer the primes to their own array*/
            c++;
        }
    }

    for (i=0;i&lt;c;i++) printf("%d\n",primes[i]);
}
</code></pre>
<h4>（2）Haskell版本</h4>
<pre><code>// sieve.hs

sieve [] = []
sieve (x:xs) = x : sieve (filter (\a -&gt; not $ a `mod` x == 0) xs)
n = 100
main = print $ sieve [2..n]
</code></pre>
<h4>（3）Go语言版本</h4>
<pre><code>// sieve.go

func Generate(ch chan&lt;- int) {
    for i := 2; ; i++ {
        ch &lt;- i
    }
}

func Filter(in &lt;-chan int, out chan&lt;- int, prime int) {
    for {
        i := &lt;-in
        if i%prime != 0 {
            out &lt;- i
        }
    }
}

func main() {
    ch := make(chan int)
    go Generate(ch)
    for i := 0; i &lt; 10; i++ {
        prime := &lt;-ch
        print(prime, "\n")
        ch1 := make(chan int)
        go Filter(ch, ch1, prime)
        ch = ch1
    }
}
</code></pre>
<p>对比上述的三个语言版本的素数筛算法的实现，我们看到：</p>
<ul>
<li>
<p>C版本的素数筛程序是一个常规实现。它定义了两个数组：numbers和primes，“筛”的过程在numbers这个数组中进行（即基于纯内存修改），非素数的数组元素被设置为-1，便于后续提取；</p>
</li>
<li>
<p>Haskell版本采用了函数递归的思路，通过“filter操作集合”，用下面谓词（过滤条件）筛除素数的倍数，将未筛除的数的集合作为参数传递归递给下去；</p>
</li>
</ul>
<pre><code>\a -&gt; not $ a `mod` x == 0；
</code></pre>
<ul>
<li>Go版本程序实现了一个并发素数筛，它采用的是goroutine的并发组合。程序从素数2开始，依次为每个素数建立一个goroutine，用于作为筛除该素数的倍数。ch指向当前最新输出素数所位于的筛子goroutine的源channel，这段代码来自于Rob Pike的一次关于并发的分享。Go版本程序的执行过程可以用图2立体的展现出来。</li>
</ul>
<p><img src="https://tonybai.com/wp-content/uploads/taking-a-closer-look-at-programming-thinking-in-go-4.gif" alt="" /><br />
<center>图2 Go版本素数筛执行图示</center></p>
<h3>3.Go语言原生编程思维</h3>
<p>通过上述这个现实中的问题我们可以看到：面对同一个问题，来自不同编程语言的程序员给出了思维方式截然不同的解决方法：C的命令式思维、Haskell的函数式思维和Go的并发思维。结合“萨丕尔—沃夫假说”，我们可以得到一个未经理论证实但又确实对现实有影响的推论：<strong>编程语言影响编程思维，或者说每种编程语言都有属于自己的原生编程思维</strong>。</p>
<p>Go语言诞生较晚，大多数Gopher（包括笔者在内）第一语言都不是Go，都是“半路出家”从其他语言转过来的，如C、C++、Java、Python等。每种语言都有自己的原生编程思维。比如：C语言相信程序员，提供了指针和指针运算，让C程序员天马行空的发挥，接近底层的直接内存操作让C程序拥有很高的性能；C++支持多范式（命令式、OO和泛型），虽不强迫程序员使用某个特定的范式，但推荐使用最新代表现代语言发展特色的泛型等高级范式；Python语言更是形成了Pythonic规则来指导Python程序员写出符合Python思维或惯用法的代码。</p>
<p>经验告诉我们但凡属于某个编程语言的高质量范畴的代码，其必定是在这种编程语言原生思维下编写的代码。如果用A语言的思维去编写B语言的代码（比如用OO思维写C代码，用命令式的思维写Haskell代码等），那么你写出的代码多半无法被B语言社区所认可，更难以成为高质量代码的典范。并且，如果沿着这样的方向去学习和实践B语言，那么结果只能是“南辕北辙”，离编写出高质量代码的目标渐行渐远。</p>
<p>那Go原生编程思维究竟是什么呢？一门编程语言的编程思维是由语言设计者、语言实现团队、语言社区、语言使用者在长期的演化和实践中形成的一种统一的思维习惯、行为方式、代码惯用法和风格。Go语言从诞生到现在也近十年多了。经过Go设计哲学熏陶、Go开发团队的引导和教育、Go社区的实践，Go语言也渐渐形成了属于自己的原生编程思维，或者说形成了符合Go语言哲学的Go语言惯用法（idiomatic go）。它们是Go语言的精华，也是构建本书内容的骨架，并值得我们用一本书的规模去详细呈现。因此可以说阅读本书的过程也是学习和建立Go语言原生编程思维的过程。</p>
<h3>4. 小结</h3>
<p>本文详细介绍了编程语言与编程思维之间的联系。我们学习和使用一门编程语言，目标就是要用这门语言的原生思维方式去编写高质量代码。学习Go，就要用Go的原生编程思维去写Go代码，而不是用其他语言的思维方式。掌握Go原生编程思维就是我们通往高质量Go编程的学习方向和必经之路。如果您想要了解更多有关Go编程思维的内容，推荐您详细阅读我的新作《Go语言精进之路：从新手到高手的编程思想、方法与技巧》。</p>
<hr />
<p><img src="https://tonybai.com/wp-content/uploads/taking-a-closer-look-at-programming-thinking-in-go-5.jpeg" alt="" /></p>
<p>Gopher Daily(Gopher每日新闻)归档仓库 &#8211; https://github.com/bigwhite/gopherdaily</p>
<p>我的联系方式：</p>
<ul>
<li>微博：https://weibo.com/bigwhite20xx</li>
<li>微信公众号：iamtonybai</li>
<li>博客：tonybai.com</li>
<li>github: https://github.com/bigwhite</li>
<li>“Gopher部落”知识星球：https://public.zsxq.com/groups/51284458844544</li>
</ul>
<p><img src="http://image.tonybai.com/img/tonybai/iamtonybai-wechat-qr.png" alt="" /></p>
<p>商务合作方式：撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。</p>
<p style='text-align:left'>&copy; 2022, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2022/04/23/taking-a-closer-look-at-programming-thinking-in-go/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Golang的演化历程</title>
		<link>https://tonybai.com/2014/10/25/golang-history/</link>
		<comments>https://tonybai.com/2014/10/25/golang-history/#comments</comments>
		<pubDate>Sat, 25 Oct 2014 11:36:10 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[ANSI-C]]></category>
		<category><![CDATA[BCPL]]></category>
		<category><![CDATA[C]]></category>
		<category><![CDATA[Channel]]></category>
		<category><![CDATA[Concurrency]]></category>
		<category><![CDATA[CSP]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[Golang]]></category>
		<category><![CDATA[Google]]></category>
		<category><![CDATA[Package]]></category>
		<category><![CDATA[RobPike]]></category>
		<category><![CDATA[UTF8]]></category>
		<category><![CDATA[包]]></category>
		<category><![CDATA[并发编程]]></category>
		<category><![CDATA[标准C]]></category>
		<category><![CDATA[标准库]]></category>
		<category><![CDATA[素数筛]]></category>

		<guid isPermaLink="false">http://tonybai.com/?p=1585</guid>
		<description><![CDATA[本文来自Google的Golang语言设计者之一Rob Pike大神在GopherCon2014大会上的开幕主题演讲资料&#8220;Hello, Gophers!&#8221;。Rob大神在这次分 享中用了两个生动的例子讲述了Golang的演化历程，总结了Golang到目前为止的成功因素，值得广大Golang Programmer &#38; Beginner学习和了解。这里也用了&#34;Golang的演化历程&#34;作为标题。 1、Hello Gophers! package main import &#34;fmt&#34; func main() { &#160;&#160;&#160; fmt.Printf(&#34;Hello, gophers!\n&#34;) } Rob大神的见面礼，后续会有针对这段的演化历史的陈述。 2、历史 这是一个历史性的时刻。 Golang已经获得了一定的成功，值得拥有属于自己的技术大会。 3、成功 促成这份成功的因素有许多： &#160;&#160;&#160; &#8211; 功能 &#160;&#160;&#160; &#8211; 缺少的功能 &#160;&#160;&#160; &#8211; 功能的组合 &#160;&#160;&#160; &#8211; 设计&#160;&#160;&#160; &#160;&#160;&#160; &#8211; 人 &#160;&#160;&#160; &#8211; 时间 4、案例学习：两段程序 我们来近距离回顾两段程序。 第一个是你见过的第一个Go程序，是属于你的历史时刻。 第二个是我们见过的第一个Go程序，是属于全世界所有Gophers的历史时刻。 先看第一个&#8220;hello, world&#8221; 5、hello.b main( ) { &#160;&#160;&#160; [...]]]></description>
			<content:encoded><![CDATA[<p>本文来自Google的<a href="http://golang.org">Golang</a>语言设计者之一<a href="http://en.wikipedia.org/wiki/Rob_Pike">Rob Pike</a>大神在GopherCon2014大会上的开幕主题演讲资料&ldquo;<a href="http://talks.golang.org/2014/hellogophers.slide">Hello, Gophers</a>!&rdquo;。Rob大神在这次分 享中用了两个生动的例子讲述了Golang的演化历程，总结了Golang到目前为止的成功因素，值得广大Golang Programmer &amp; Beginner学习和了解。这里也用了&quot;Golang的演化历程&quot;作为标题。</p>
<p><b>1、Hello Gophers!</b></p>
<p><font face="Courier New">package main</font></p>
<p><font face="Courier New">import &quot;fmt&quot;</font></p>
<p><font face="Courier New">func main() {<br />
	&nbsp;&nbsp;&nbsp; fmt.Printf(&quot;Hello, gophers!\n&quot;)<br />
	}</font></p>
<p>Rob大神的见面礼，后续会有针对这段的演化历史的陈述。</p>
<p><b>2、历史</b></p>
<p>这是一个历史性的时刻。</p>
<p>Golang已经获得了一定的成功，值得拥有属于自己的技术大会。</p>
<p><b>3、成功</b></p>
<p>促成这份成功的因素有许多：</p>
<p>&nbsp;&nbsp;&nbsp; &#8211; 功能<br />
	&nbsp;&nbsp;&nbsp; &#8211; 缺少的功能<br />
	&nbsp;&nbsp;&nbsp; &#8211; 功能的组合<br />
	&nbsp;&nbsp;&nbsp; &#8211; 设计&nbsp;&nbsp;&nbsp;<br />
	&nbsp;&nbsp;&nbsp; &#8211; 人<br />
	&nbsp;&nbsp;&nbsp; &#8211; 时间</p>
<p><b>4、案例学习：两段程序</b></p>
<p>我们来近距离回顾两段程序。</p>
<p>第一个是你见过的第一个Go程序，是属于你的历史时刻。<br />
	第二个是我们见过的第一个Go程序，是属于全世界所有Gophers的历史时刻。</p>
<p>先看第一个&ldquo;hello, world&rdquo;</p>
<p><b>5、hello.b</b></p>
<p><font face="Courier New">main( ) {<br />
	&nbsp;&nbsp;&nbsp; extrn a, b, c;<br />
	&nbsp;&nbsp;&nbsp; putchar(a); putchar(b); putchar(c); putchar(&#39;!*n&#39;);<br />
	}<br />
	a &#39;hell&#39;;<br />
	b &#39;o, w&#39;;<br />
	c &#39;orld&#39;;</font></p>
<p>上面这段代码首先出现在1972年<a href="http://en.wikipedia.org/wiki/Brian_Kernighan">Brian W. Kernighan</a>的B语言教程中（也有另外一说是出现在那之前的BCPL语言中）。</p>
<p><b>6、hello.c</b></p>
<p><font face="Courier New">main()<br />
	{<br />
	&nbsp;&nbsp;&nbsp; printf(&quot;hello, world&quot;);<br />
	}</font></p>
<p>上面这段代码出现在1974年Brian W. Kernighan编写的《Programming in C: A Tutorial》中。这份教程当时是作为Unix v5文档的一部分。</p>
<p><b>7、hello.c </b></p>
<p><font face="Courier New">main()<br />
	{<br />
	&nbsp;&nbsp;&nbsp; printf(&quot;hello, world\n&quot;); //译注：与上面的hello.c相比，多了个换行符\n输出<br />
	}</font></p>
<p>这段代码首次出现在1978年Brian W. Kernighan和<a href="http://en.wikipedia.org/wiki/Dennis_Ritchie">Dennis M. Ritchie</a>合著的《<a href="http://book.douban.com/subject/4816029/">The C Programming Language</a>》一书中。</p>
<p><b>8、hello.c, 标准C草案</b></p>
<p><font face="Courier New">#include &lt;stdio.h&gt; //译注：与上面hello.c相比， 多了这个头文件包含</font></p>
<p><font face="Courier New">main()<br />
	{<br />
	&nbsp;&nbsp;&nbsp; printf(&quot;hello, world\n&quot;);<br />
	}</font></p>
<p>这段代码出现在1988年Brian W. Kernighan和Dennis M. Ritchie合著的《<a href="http://book.douban.com/subject/1236999/">The C Programming Language</a>》第二版一书中，基于标准C草案。</p>
<p>9、hello.c，标准C89</p>
<p><font face="Courier New">#include &lt;stdio.h&gt;</font></p>
<p><font face="Courier New">main(void) //</font><font face="Courier New"><font face="Courier&lt;br /&gt;<br />
        New">译注：与上面hello.c相比，多了个void</font><br />
	{<br />
	&nbsp;&nbsp;&nbsp; printf(&quot;hello, world\n&quot;);<br />
	}</font></p>
<p>这段代码出现在1988年Brian W. Kernighan和Dennis M. Ritchie合著的《The C Programming Language》第二版第二次修订中。</p>
<p><b>10、一两代之后&#8230;</b></p>
<p>(省略所有中间语言)</p>
<p>关于Golang的讨论开始于2007年年末。</p>
<p>第一版语言规范起草于2008年3月份。</p>
<p>用于实验和原型目的的编译器开发工作已经展开。</p>
<p>最初的编译器输出的是C代码。</p>
<p>语言规范一形成，我们就重写了编译器，输出本地代码（机器码）。</p>
<p><b>11、hello.go, 2008年6月6日</b></p>
<p><font face="Courier New">package main</font></p>
<p><font face="Courier New">func main() int {<br />
	&nbsp;&nbsp;&nbsp; print &quot;hello, world\n&quot;;<br />
	&nbsp;&nbsp;&nbsp; return 0;<br />
	}</font></p>
<p>针对首次提交代码的测试。</p>
<p>内置的print已经是当时的全部实现。main函数返回一个int类型值。<br />
	注意：print后面没有括号。</p>
<p><b>12、hello.go，2008年6月27日</b></p>
<p><font face="Courier New">package main</font></p>
<p><font face="Courier New">func main() {<br />
	&nbsp;&nbsp;&nbsp; print &quot;hello, world\n&quot;;<br />
	}</font></p>
<p>当main函数返回，程序调用exit(0)。</p>
<p><b>13、hello.go，2008年8月11日</b></p>
<p><font face="Courier New">package main</font></p>
<p><font face="Courier New">func main() {<br />
	&nbsp;&nbsp;&nbsp; print(&quot;hello, world\n&quot;);<br />
	}</font></p>
<p>print调用加上了括号，这时print是一个函数，不再是一个原语。</p>
<p><b>14、hello.go，2008年10月24日</b></p>
<p><font face="Courier New">package main</font></p>
<p><font face="Courier New">import &quot;fmt&quot;</font></p>
<p><font face="Courier New">func main() {<br />
	&nbsp;&nbsp;&nbsp; fmt.printf(&quot;hello, world\n&quot;);<br />
	}</font></p>
<p>我们熟知并喜欢的printf来了。</p>
<p><b>15、hello.go，2009年1月15日</b></p>
<p><font face="Courier New">package main</font></p>
<p><font face="Courier New">import &quot;fmt&quot;</font></p>
<p><font face="Courier New">func main() {<br />
	&nbsp;&nbsp;&nbsp; fmt.Printf(&quot;hello, world\n&quot;);<br />
	}</font></p>
<p>头母大写的函数名用作才是导出的符号。</p>
<p>16、hello.go, 2009年12约11日</p>
<p><font face="Courier New">package main</font></p>
<p><font face="Courier New">import &quot;fmt&quot;</font></p>
<p><font face="Courier New">func main() {<br />
	&nbsp;&nbsp;&nbsp; fmt.Printf(&quot;hello, world\n&quot;)<br />
	}</font></p>
<p>不再需要分号。</p>
<p>这是在2009年11月10日Golang开发发布后的一次重要改变。</p>
<p>这也是当前版本的hello, world</p>
<p>我们花了些时间到达这里（32年！）</p>
<p>都是历史了！</p>
<p><b>17、不仅仅有C</b></p>
<p>我们从&quot;C&quot;开始，但Go与C相比有着巨大的不同。</p>
<p>其他一些语言影响和贯穿于Go的设计当中。</p>
<p><font face="Courier New">&nbsp;&nbsp;&nbsp; C: 语句和表达式语法<br />
	&nbsp;&nbsp;&nbsp; Pascal: 声明语法<br />
	&nbsp;&nbsp;&nbsp; Modula 2, Oberon 2：包<br />
	&nbsp;&nbsp;&nbsp; CSP, Occam, Newsqueak, Limbo, Alef:&nbsp; 并发<br />
	&nbsp;&nbsp;&nbsp; BCPL: 分号规则<br />
	&nbsp;&nbsp;&nbsp; Smalltalk: 方法(method)<br />
	&nbsp;&nbsp;&nbsp; Newsqueak: &lt;-, :=<br />
	&nbsp;&nbsp;&nbsp; APL: iota</font><br />
	&nbsp;&nbsp;&nbsp;<br />
	等等。也有一些是全新发明的，例如defer、常量。</p>
<p>还有一些来自其他语言的优点和缺点：<br />
	<font face="Courier New">&nbsp;&nbsp;&nbsp; C++, C#, Java, JavaScript, LISP, Python, Scala, &#8230;</font></p>
<p><b>18、hello.go，Go 1版</b></p>
<p>将我们带到了今天。</p>
<p><font face="Courier New">package main</font></p>
<p><font face="Courier New">import &quot;fmt&quot;</font></p>
<p><font face="Courier New">func main() {<br />
	&nbsp;&nbsp;&nbsp; fmt.Println(&quot;Hello, Gophers (some of whom know 中文)!&quot;)<br />
	}</font></p>
<p>我们来深入挖掘一下，把这段代码做一个拆解。</p>
<p><b>19、Hello, World的16个tokens</b></p>
<p><font face="Courier New">package<br />
	main<br />
	import<br />
	&quot;fmt&quot;<br />
	func<br />
	main<br />
	(<br />
	)<br />
	{<br />
	fmt<br />
	.<br />
	Println<br />
	(<br />
	&quot;Hello, Gophers (some of whom know 中文)!&quot;<br />
	)<br />
	}</font></p>
<p><b>20、package</b></p>
<p>早期设计讨论的主要话题：扩展性的关键</p>
<p>package是什么？来自Modula-2等语言的idea</p>
<p>为什么是package?</p>
<p>&nbsp;&nbsp;&nbsp; &#8211; 拥有编译构建所需的全部信息<br />
	&nbsp;&nbsp;&nbsp; &#8211; 没有循环依赖(import)<br />
	&nbsp;&nbsp;&nbsp; &#8211; 没有子包<br />
	&nbsp;&nbsp;&nbsp; &#8211; 包名与包路径分离<br />
	&nbsp;&nbsp;&nbsp; &#8211; 包级别可见性，而不是类型级别<br />
	&nbsp;&nbsp;&nbsp; &#8211; 在包内部，你拥有整个语言，在包外部，你只拥有包许可的东西。</p>
<p><b>21、main</b></p>
<p>一个C语言遗留风范尽显之处<br />
	最初是Main，原因记不得了。<br />
	主要的包，main函数<br />
	很特别，因为它是初始化树(initialization tree)的根(root)。</p>
<p><b>22、import</b></p>
<p>一种加载包的机制<br />
	通过编译器实现（有别于文本预处理器。译注：C语言的include是通过preprocessor实现的）<br />
	努力使其高效且线性<br />
	导入的一个包，而不是一个标识符(identifiers)集合（译注：C语言的include是将头文件里的标识符集合引入）<br />
	至于export，它曾经是一个关键字。</p>
<p><b>23、&quot;fmt&quot;</b></p>
<p>包路径(package path)只是一个字符串，并非标识符的列表。<br />
	让语言避免定义它的含义 &#8211; 适应性。(Allows the language to avoid defining what it means&mdash;adaptability)<br />
	从一开始就想要一个URL作为一个选项。（译注：类似import &quot;github.com/go/tools/xxx）<br />
	可以应付将来的发展。</p>
<p><b>24、func</b></p>
<p>一个关键字，用于引入函数(类型、变量、常量），易于编译器解析。<br />
	对于函数字面量(闭包)而言，易于解析非常重要。<br />
	顺便说一下，最初这个关键字不是func，而是function。</p>
<p>小插曲：</p>
<p>Mail thread from February 6, 2008<br />
	From: Ken Thompson <a class="moz-txt-link-rfc2396E" href="mailto:ken@google.com">&lt;ken@google.com&gt;</a>  <br />
	To: gri, r<br />
	larry and sergey came by tonight. we  talked about go for more than an hour.  they both said they liked it very much.<br />
	p.s. one of larrys comments was &quot;why isnt function spelled func?&quot;<br />
	&#8212;<br />
	From: Rob Pike <a class="moz-txt-link-rfc2396E" href="mailto:r@google.com">&lt;r@google.com&gt;</a><br />
	 To: ken, gri<br />
	fine with me. seems compatible with &#39;var&#39;.<br />
	anyway we can always say, &quot;larry said to call it &#39;func&#39;&quot;</p>
<p><b>25、main</b></p>
<p>程序执行的起点。除非它不是。（译注：main不是起点，rob大神的意思是不是指下列情形，比如go test测试包，在google app engine上的go程序不需要main）<br />
	将初始化与正常执行分离，早期计划之中的。<br />
	初始化在哪里发生的？(译注：说的是package内的func init() {..}函数吧)<br />
	回到包设计。（译注：重温golang的package设计思想）</p>
<p><b>26、()</b></p>
<p>看看，没有void<br />
	main没有返回值，由运行时来处理main的返回后的事情。<br />
	没有函数参数（命令行选项通过os包获取）<br />
	没有返回值</p>
<p>返回值以及语法</p>
<p><b>27、{</b></p>
<p>用的是大括号，而不是空格（译注：估计是与python的空格缩进对比）<br />
	同样也不是方括号。<br />
	为什么在括号后放置换行符(newline)？</p>
<p><b>28、</b><b>fmt</b></p>
<p>所有导入的标识符均限定于其导入的包。（All imported identifiers are qualified by their import.）<br />
	每个标识符要么是包或函数的本地变量，要么被类型或导入包限定。<br />
	对代码可读性的重大影响。</p>
<p>为什么是fmt，而不是format？</p>
<p><b>29、.</b></p>
<p>句号token在Go中有多少使用？（很多）<br />
	a.B的含义需要使用到类型系统<br />
	但这对于人类来说非常清晰，读起来也非常容易。<br />
	针对指针的自动转换(没有-&gt;)。</p>
<p><b>30、Println</b></p>
<p>Println，不是println，头母大写才是导出符号。<br />
	知道它是反射驱动的(reflection-driven)<br />
	可变参数函数<br />
	参数类型是(&#8230;); 2010年2月1日变成(&#8230;interface{})</p>
<p><b>31、(</b></p>
<p>传统函数语法</p>
<p><b>32、</b><b><font face="Courier New">&quot;Hello, Gophers (some of whom know 中文)!&quot;</font></b></p>
<p>UTF-8编码的源码输入。字符串字面量也自动是utf8编码格式的。</p>
<p>但什么是字符串(string)呢？</p>
<p>首批写入规范的语法规则，今天很难改变了。(blog.golang.org/strings)</p>
<p><b>33、)</b></p>
<p>没有分号<br />
	在go发布后不久我们就去除了分号<br />
	早期曾胡闹地尝试将它们（译注：指得是括号）去掉<br />
	最终接受了BCPL的方案</p>
<p><b>34、}</b></p>
<p>第一轮结束。</p>
<p>旁白：还没有讨论到的</p>
<p>&nbsp;&nbsp;&nbsp; &#8211; 类型<br />
	&nbsp;&nbsp;&nbsp; &#8211; 常量<br />
	&nbsp;&nbsp;&nbsp; &#8211; 方法<br />
	&nbsp;&nbsp;&nbsp; &#8211; interface<br />
	&nbsp;&nbsp;&nbsp; &#8211; 库<br />
	&nbsp;&nbsp;&nbsp; &#8211; 内存管理<br />
	&nbsp;&nbsp;&nbsp; &#8211; 并发（接下来将讨论）<br />
	&nbsp;&nbsp;&nbsp;<br />
	外加工具，生态系统，社区等。<br />
	语言是核心，但也只是我们故事的一部分。</p>
<p><b>35、成功</b></p>
<p>要素：<br />
	&nbsp;&nbsp;&nbsp; &#8211; 站在巨人的肩膀上(building on history)<br />
	&nbsp;&nbsp;&nbsp; &#8211; 经验之作(building on experience) 译注：最初的三个神级语言设计者<br />
	&nbsp;&nbsp;&nbsp; &#8211; 设计过程<br />
	&nbsp;&nbsp;&nbsp; &#8211; 早期idea提炼到最终的方案中<br />
	&nbsp;&nbsp;&nbsp; &#8211; 由一个小团队专门集中精力做<br />
	&nbsp;&nbsp;&nbsp;<br />
	最终：承诺<br />
	&nbsp;&nbsp;&nbsp; Go 1.0锁定了语言核心与标准库。</p>
<p><b>36、另一轮</b></p>
<p>让我们看第二个程序的类似演化过程。</p>
<p><b>37、问题：素数筛(Prime sieve)</b></p>
<p>问题来自于Communicating Sequential Processes, by C. A. R. Hoare, 1978。</p>
<p>&ldquo;问题：以升序打印所有小于10000的素数。使用一个process数组：SIEVE，其中每个process从其前驱元素输入一个素数并打印它。接下 来这个process从其前驱元素接收到一个升序数字流并将它们传给其后继元素，这个过程会剔除掉所有是最初素数整数倍的数字。</p>
<p><b>38、解决方案</b></p>
<p>在1978年的CSP论文中。（注意不是Eratosthenes筛）</p>
<p>这个优美的方案是由David Gries贡献出来的。</p>
<p><b>39、CSP</b></p>
<p>在Hoare的CSP论文中：</p>
<p><font face="Courier New">[SIEVE(i:1..100)::<br />
	&nbsp;&nbsp;&nbsp; p,mp:integer;<br />
	&nbsp;&nbsp;&nbsp; SIEVE(i - 1)?p;<br />
	&nbsp;&nbsp;&nbsp; print!p;<br />
	&nbsp;&nbsp;&nbsp; mp := p; comment mp is a multiple of p;<br />
	*[m:integer; SIEVE(i - 1)?m &rarr;<br />
	&nbsp;&nbsp;&nbsp; *[m > mp &rarr; mp := mp + p];<br />
	&nbsp;&nbsp;&nbsp; [m = mp &rarr; skip<br />
	&nbsp;&nbsp;&nbsp; ||m &lt; mp &rarr; SIEVE(i + 1)!m<br />
	]&nbsp;&nbsp; ]<br />
	||SIEVE(0)::print!2; n:integer; n := 3;<br />
	&nbsp;&nbsp;&nbsp; *[n < 10000 &rarr; SIEVE(1)!n; n := n + 2]<br />
	||SIEVE(101)::*[n:integer;SIEVE(100)?n &rarr; print!n]<br />
	||print::*[(i:0..101) n:integer; SIEVE(i)?n &rarr; ...]<br />
	] </font></p>
<p>没有channel。能处理的素数的个数是在程序中指定的。</p>
<p><b>40、Newsqueak</b></p>
<p>circa 1988。</p>
<p>Rob Pike语言设计，Tom Cargill和Doug McIlroy实现。</p>
<p>使用了channels，这样个数是可编程的。(channel这个idea从何而来？）</p>
<p><font face="Courier New">counter:=prog(end: int, c: chan of int)<br />
	{<br />
	&nbsp;&nbsp;&nbsp; i:int;<br />
	&nbsp;&nbsp;&nbsp; for(i=2; i&lt;end; i++)<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; c&lt;-=i;<br />
	};</font></p>
<p><font face="Courier New">filter:=prog(prime: int, listen: chan of int, send: chan of int)<br />
	{<br />
	&nbsp;&nbsp;&nbsp; i:int;<br />
	&nbsp;&nbsp;&nbsp; for(;;)<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if((i=&lt;-listen)%prime)<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; send&lt;-=i;<br />
	};</font></p>
<p><font face="Courier New">sieve:=prog(c: chan of int)<br />
	{<br />
	&nbsp;&nbsp;&nbsp; for(;;){<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; prime:=&lt;-c;<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; print(prime, &quot; &quot;);<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; newc:=mk(chan of int);<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; begin filter(prime, c, newc);<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; c=newc;<br />
	&nbsp;&nbsp;&nbsp; }<br />
	};</font></p>
<p><font face="Courier New">count:=mk(chan of int);</font></p>
<p><font face="Courier New">begin counter(10000, count);<br />
	begin sieve(count);<br />
	&quot;&quot;;</font></p>
<p><b>41、sieve.go，2008年3月5日</b></p>
<p>使用go规范编写的第一个版本，可能是第二个由go编写的重要程序。</p>
<p>&gt;用于发送；&lt;用于接收。Channel是指针。Main是头字母大写的。</p>
<p><font face="Courier New">package Main</font></p>
<p><font face="Courier New">// Send the sequence 2, 3, 4, &#8230; to channel &#39;ch&#39;.<br />
	func Generate(ch *chan&gt; int) {<br />
	&nbsp;&nbsp;&nbsp; for i := 2; ; i++ {<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &gt;ch = i;&nbsp;&nbsp;&nbsp; // Send &#39;i&#39; to channel &#39;ch&#39;.<br />
	&nbsp;&nbsp;&nbsp; }<br />
	}</font></p>
<p><font face="Courier New">// Copy the values from channel &#39;in&#39; to channel &#39;out&#39;,<br />
	// removing those divisible by &#39;prime&#39;.<br />
	func Filter(in *chan&lt; int, out *chan&gt; int, prime int) {<br />
	&nbsp;&nbsp;&nbsp; for ; ; {<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; i := &lt;in;&nbsp;&nbsp;&nbsp; // Receive value of new variable &#39;i&#39; from &#39;in&#39;.<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if i % prime != 0 {<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &gt;out = i;&nbsp;&nbsp;&nbsp; // Send &#39;i&#39; to channel &#39;out&#39;.<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	&nbsp;&nbsp;&nbsp; }<br />
	}</font></p>
<p><font face="Courier New">// The prime sieve: Daisy-chain Filter processes together.<br />
	func Sieve() {<br />
	&nbsp;&nbsp;&nbsp; ch := new(chan int);&nbsp; // Create a new channel.<br />
	&nbsp;&nbsp;&nbsp; go Generate(ch);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Start Generate() as a subprocess.<br />
	&nbsp;&nbsp;&nbsp; for ; ; {<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; prime := &lt;ch;<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf(&quot;%d\n&quot;, prime);<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ch1 := new(chan int);<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; go Filter(ch, ch1, prime);<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ch = ch1;<br />
	&nbsp;&nbsp;&nbsp; }<br />
	}</font></p>
<p><font face="Courier New">func Main() {<br />
	&nbsp;&nbsp;&nbsp; Sieve();<br />
	}</font></p>
<p><b>42. sieve.go，2008年7月22日</b></p>
<p><font face="Courier New">-&lt;</font>用于发送；<font face="Courier New">-&lt;</font>用于接收。Channel仍然是指针。但现在main不是大写字母开头的了。</p>
<p><font face="Courier New">package main</font></p>
<p><font face="Courier New">// Send the sequence 2, 3, 4, &#8230; to channel &#39;ch&#39;.<br />
	func Generate(ch *chan-&lt; int) {<br />
	&nbsp;&nbsp;&nbsp; for i := 2; ; i++ {<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ch -&lt; i&nbsp;&nbsp;&nbsp; // Send &#39;i&#39; to channel &#39;ch&#39;.<br />
	&nbsp;&nbsp;&nbsp; }<br />
	}</font></p>
<p><font face="Courier New">// Copy the values from channel &#39;in&#39; to channel &#39;out&#39;,<br />
	// removing those divisible by &#39;prime&#39;.<br />
	func Filter(in *chan&lt;- int, out *chan-&lt; int, prime int) {<br />
	&nbsp;&nbsp;&nbsp; for {<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; i := &lt;-in;&nbsp;&nbsp;&nbsp; // Receive value of new variable &#39;i&#39; from &#39;in&#39;.<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if i % prime != 0 {<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; out -&lt; i&nbsp;&nbsp;&nbsp; // Send &#39;i&#39; to channel &#39;out&#39;.<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	&nbsp;&nbsp;&nbsp; }<br />
	}</font></p>
<p><font face="Courier New">// The prime sieve: Daisy-chain Filter processes together.<br />
	func Sieve() {<br />
	&nbsp;&nbsp;&nbsp; ch := new(chan int);&nbsp; // Create a new channel.<br />
	&nbsp;&nbsp;&nbsp; go Generate(ch);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Start Generate() as a subprocess.<br />
	&nbsp;&nbsp;&nbsp; for {<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; prime := &lt;-ch;<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf(&quot;%d\n&quot;,&nbsp;&nbsp;&nbsp; prime);<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ch1 := new(chan int);<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; go Filter(ch, ch1, prime);<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ch = ch1<br />
	&nbsp;&nbsp;&nbsp; }<br />
	}</font></p>
<p><font face="Courier New">func main() {<br />
	&nbsp;&nbsp;&nbsp; Sieve()<br />
	}</font></p>
<p><b>43、sieve.go，2008年9月17日</b></p>
<p>通信操作符现在是<font face="Courier New">&lt;-</font>。channel仍然是指针。</p>
<p><font face="Courier New">package main</font></p>
<p><font face="Courier New">// Send the sequence 2, 3, 4, &#8230; to channel &#39;ch&#39;.<br />
	func Generate(ch *chan &lt;- int) {<br />
	&nbsp;&nbsp;&nbsp; for i := 2; ; i++ {<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ch &lt;- i&nbsp; // Send &#39;i&#39; to channel &#39;ch&#39;.<br />
	&nbsp;&nbsp;&nbsp; }<br />
	}</font></p>
<p><font face="Courier New">// Copy the values from channel &#39;in&#39; to channel &#39;out&#39;,<br />
	// removing those divisible by &#39;prime&#39;.<br />
	func Filter(in *chan &lt;- int, out *&lt;-chan int, prime int) {<br />
	&nbsp;&nbsp;&nbsp; for {<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; i := &lt;-in;&nbsp; // Receive value of new variable &#39;i&#39; from &#39;in&#39;.<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if i % prime != 0 {<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; out &lt;- i&nbsp; // Send &#39;i&#39; to channel &#39;out&#39;.<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	&nbsp;&nbsp;&nbsp; }<br />
	}</font></p>
<p><font face="Courier New">// The prime sieve: Daisy-chain Filter processes together.<br />
	func Sieve() {<br />
	&nbsp;&nbsp;&nbsp; ch := new(chan int);&nbsp; // Create a new channel.<br />
	&nbsp;&nbsp;&nbsp; go Generate(ch);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Start Generate() as a subprocess.<br />
	&nbsp;&nbsp;&nbsp; for {<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; prime := &lt;-ch;<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; print(prime, &quot;\n&quot;);<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ch1 := new(chan int);<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; go Filter(ch, ch1, prime);<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ch = ch1<br />
	&nbsp;&nbsp;&nbsp; }<br />
	}</font></p>
<p><font face="Courier New">func main() {<br />
	&nbsp;&nbsp;&nbsp; Sieve()<br />
	}</font></p>
<p><b><font face="Courier New">44、sieve.go，2009年1月6日</font></b></p>
<p>引入了make内置操作符。没有指针。编码错误！（有个*被留下了，错误的参数类型）</p>
<p><font face="Courier New">package main</font></p>
<p><font face="Courier New">// Send the sequence 2, 3, 4, &#8230; to channel &#39;ch&#39;.<br />
	func Generate(ch chan &lt;- int) {<br />
	&nbsp;&nbsp;&nbsp; for i := 2; ; i++ {<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ch &lt;- i&nbsp; // Send &#39;i&#39; to channel &#39;ch&#39;.<br />
	&nbsp;&nbsp;&nbsp; }<br />
	}</font></p>
<p><font face="Courier New">// Copy the values from channel &#39;in&#39; to channel &#39;out&#39;,<br />
	// removing those divisible by &#39;prime&#39;.<br />
	func Filter(in chan &lt;- int, out *&lt;-chan int, prime int) {<br />
	&nbsp;&nbsp;&nbsp; for {<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; i := &lt;-in;&nbsp; // Receive value of new variable &#39;i&#39; from &#39;in&#39;.<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if i % prime != 0 {<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; out &lt;- i&nbsp; // Send &#39;i&#39; to channel &#39;out&#39;.<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	&nbsp;&nbsp;&nbsp; }<br />
	}</font></p>
<p><font face="Courier New">// The prime sieve: Daisy-chain Filter processes together.<br />
	func Sieve() {<br />
	&nbsp;&nbsp;&nbsp; ch := make(chan int);&nbsp; // Create a new channel.<br />
	&nbsp;&nbsp;&nbsp; go Generate(ch);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Start Generate() as a subprocess.<br />
	&nbsp;&nbsp;&nbsp; for {<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; prime := &lt;-ch;<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; print(prime, &quot;\n&quot;);<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ch1 := make(chan int);<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; go Filter(ch, ch1, prime);<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ch = ch1<br />
	&nbsp;&nbsp;&nbsp; }<br />
	}</font></p>
<p><font face="Courier New">func main() {<br />
	&nbsp;&nbsp;&nbsp; Sieve()<br />
	}</font></p>
<p><b>45、sieve.go，2009年9月25日</b></p>
<p>第一个正确的现代版本。同样，大写头母不见了，使用了fmt。</p>
<p><font face="Courier New">package main</font></p>
<p><font face="Courier New">import &quot;fmt&quot;</font></p>
<p><font face="Courier New">// Send the sequence 2, 3, 4, &#8230; to channel &#39;ch&#39;.<br />
	func generate(ch chan&lt;- int) {<br />
	&nbsp;&nbsp;&nbsp; for i := 2; ; i++ {<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ch &lt;- i;&nbsp;&nbsp;&nbsp; // Send &#39;i&#39; to channel &#39;ch&#39;.<br />
	&nbsp;&nbsp;&nbsp; }<br />
	}</font></p>
<p><font face="Courier New">// Copy the values from channel &#39;in&#39; to channel &#39;out&#39;,<br />
	// removing those divisible by &#39;prime&#39;.<br />
	func filter(src &lt;-chan int, dst chan&lt;- int, prime int) {<br />
	&nbsp;&nbsp;&nbsp; for i := range src {&nbsp;&nbsp;&nbsp; // Loop over values received from &#39;src&#39;.<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if i%prime != 0 {<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dst &lt;- i;&nbsp;&nbsp;&nbsp; // Send &#39;i&#39; to channel &#39;dst&#39;.<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	&nbsp;&nbsp;&nbsp; }<br />
	}</font></p>
<p><font face="Courier New">// The prime sieve: Daisy-chain filter processes together.<br />
	func sieve() {<br />
	&nbsp;&nbsp;&nbsp; ch := make(chan int);&nbsp; // Create a new channel.<br />
	&nbsp;&nbsp;&nbsp; go generate(ch);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Start generate() as a subprocess.<br />
	&nbsp;&nbsp;&nbsp; for {<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; prime := &lt;-ch;<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fmt.Print(prime, &quot;\n&quot;);<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ch1 := make(chan int);<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; go filter(ch, ch1, prime);<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ch = ch1;<br />
	&nbsp;&nbsp;&nbsp; }<br />
	}</font></p>
<p><font face="Courier New">func main() {<br />
	&nbsp;&nbsp;&nbsp; sieve();<br />
	}</font></p>
<p><b>46、sieve.go，2009年12月10日</b></p>
<p>分号不见了。程序已经与现在一致了。</p>
<p><font face="Courier New">package main</font></p>
<p><font face="Courier New">import &quot;fmt&quot;</font></p>
<p><font face="Courier New">// Send the sequence 2, 3, 4, &hellip; to channel &#39;ch&#39;.<br />
	func generate(ch chan&lt;- int) {<br />
	&nbsp;&nbsp;&nbsp; for i := 2; ; i++ {<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ch &lt;- i&nbsp; // Send &#39;i&#39; to channel &#39;ch&#39;.<br />
	&nbsp;&nbsp;&nbsp; }<br />
	}</font></p>
<p><font face="Courier New">// Copy the values from channel &#39;src&#39; to channel &#39;dst&#39;,<br />
	// removing those divisible by &#39;prime&#39;.<br />
	func filter(src &lt;-chan int, dst chan&lt;- int, prime int) {<br />
	&nbsp;&nbsp;&nbsp; for i := range src {&nbsp; // Loop over values received from &#39;src&#39;.<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if i%prime != 0 {<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dst &lt;- i&nbsp; // Send &#39;i&#39; to channel &#39;dst&#39;.<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	&nbsp;&nbsp;&nbsp; }<br />
	}</font></p>
<p><font face="Courier New">// The prime sieve: Daisy-chain filter processes together.<br />
	func sieve() {<br />
	&nbsp;&nbsp;&nbsp; ch := make(chan int)&nbsp; // Create a new channel.<br />
	&nbsp;&nbsp;&nbsp; go generate(ch)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Start generate() as a subprocess.<br />
	&nbsp;&nbsp;&nbsp; for {<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; prime := &lt;-ch<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fmt.Print(prime, &quot;\n&quot;)<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ch1 := make(chan int)<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; go filter(ch, ch1, prime)<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ch = ch1<br />
	&nbsp;&nbsp;&nbsp; }<br />
	}</font></p>
<p><font face="Courier New">func main() {<br />
	&nbsp;&nbsp;&nbsp; sieve()<br />
	}</font></p>
<p>这个优美的方案来自于几十年的设计过程。</p>
<p><b>47、旁边，没有讨论到的</b></p>
<p><font face="Courier New">select</font></p>
<p>真实并发程序的核心连接器（connector)<br />
	最初起源于Dijkstra的守卫命令(guarded command)<br />
	在Hoare的CSP理论实现真正并发。<br />
	经过Newsqueak、Alef、Limbo和其他语言改良后</p>
<p>2008年3月26日出现在Go版本中。</p>
<p>简单，澄清，语法方面的考虑。</p>
<p><b>48、稳定性</b></p>
<p>Sieve程序自从2009年末就再未改变过。&#8211; 稳定！</p>
<p>开源系统并不总是兼容和稳定的。</p>
<p>但，Go是。（兼容和稳定的）</p>
<p>这是Go成功的一个重要原因。</p>
<p><b>49、</b><b>趋势</b></p>
<p>图数据展示了Go 1.0发布后Go语言的爆发。</p>
<p><b>50、成功</b></p>
<p>Go成功的元素：</p>
<p>&nbsp;&nbsp;&nbsp; 显然的：功能和工具。</p>
<p><font face="Courier New">&nbsp;&nbsp;&nbsp; * 并发<br />
	&nbsp;&nbsp;&nbsp; * 垃圾回收<br />
	&nbsp;&nbsp;&nbsp; * 高效的实现<br />
	&nbsp;&nbsp;&nbsp; * 给人以动态类型体验的静态类型系统<br />
	&nbsp;&nbsp;&nbsp; * 丰富但规模有限的标准库<br />
	&nbsp;&nbsp;&nbsp; * 工具化<br />
	&nbsp;&nbsp;&nbsp; * gofmt<br />
	&nbsp;&nbsp;&nbsp; * 在大规模系统中的应用</font></p>
<p>&nbsp;&nbsp;&nbsp; 不那么显然的：过程</p>
<p>&nbsp;&nbsp;&nbsp; * 始终聚焦最初的目标<br />
	&nbsp;&nbsp;&nbsp; * 在冻结后的集中开发<br />
	&nbsp;&nbsp;&nbsp; * 小核心团队易于取得一致<br />
	&nbsp;&nbsp;&nbsp; * 社区的重要贡献<br />
	&nbsp;&nbsp;&nbsp; * 丰富的生态系统<br />
	&nbsp;&nbsp;&nbsp;<br />
	总之，开源社区共享了我们的使命，聚焦于为当今的世界设计一门语言。</p>
<p style='text-align:left'>&copy; 2014, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2014/10/25/golang-history/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
