---------------------------自己动手做智能产品:嵌入式JavaScript实现---------------------------
人类的想象力可以说是永无止境的,“创造”(making)这个词可以说是最适合描述人类经历的词汇之一了,它包括作画、设计、建造或者编程。回望自己所创之物,心中浮起 “这是我创造的”念头——这可以说是我所知的最美妙感觉之一了。
时至今日,我们已不单单能创建那些无生命物体,还可以让那些原本无生命的物体变得栩栩如生,让物体变得更加“聪明”。本书会告诉你如何利用微控制器让物体变得可编程化。
通过日常生活中的物品和技能学习,你将可以学习到如何创造数码相机、打印机、机器人,或者早期的电视机等。只要往下看,你会了解你日常所用的物品是如何组成的以及它们背后那些关于创造的故事,也将学会如何使用目前世界上数百万Web工程师正在使用的计算机语言——JavaScript。因为有了计算机语言解释器的帮助,你将见到你所添加的一行行代码实际产生的影响。
我喜欢在生活中创造并完成一件事情。希望本书可以激励你去创造和学习,并在其中找到乐趣。
本书排版约定
本书使用以下排版约定:
斜体(Italic)
用于菜单项、URL链接、邮件地址、文件名和文件扩展名。
等宽字体(?Constant width?)
用于程序清单,或者是所引用的程序元素,如变量或函数名、数据类型、语句和关键字。
等宽粗体(?Constant width bold?)
用于表示命令或是读者应输入的其他内容。
等宽斜体(?Constant width italic?)
用于表示需要由用户提供的值或者根据上下文确定的值所替换的内容。
. 该符号表示提示或一般注释。
该符号表示需要注意的信息或警告。
使用本书代码示例
本书补充资料(如代码示例、练习等)可从以下网址下载:https://github.com/espruino/making-things-smart。
本书是为了帮助你完成那些想要完成的事情。正常情况下,本书所提供的代码用例都可以在你的程序或者文档中使用。除非需引用大段的示例代码,否则你不需要得到我们的许可。例如,将本书中的某部分代码用于程序中并不需要得到许可,但是若想将本书的代码用于销售或者打包在CD-ROM中发布则需要得到我们的许可。引用本书中的内容或者示例代码来回答问题不需要许可,但是如果需要将本书的重要示例代码加入你的产品文档中则需要得到我们的许可。
我们希望读者在引用本书内容时指出出处(但非强制要求),通常包括书名、作者、出版社和国际标准书号。比如:“Making Things Smart by Gordon F. Williams (O’Reilly). Copyright 2017 Gordon F. Williams, 978-1-680-45189-4”。
在使用本书代码示例的过程中,如果你发现错误或者需要获得以上所提许可,欢迎通过以下邮箱联系我们:
bookpermissions@makermedia.com。
O’Reilly Safari
Safari(之前称为Safari Books Online)是一个以会员制为基础,为企业、政府、教育机构和个人提供图书服务及培训内容的平台。
会员可以访问众多的书籍、培训视频、学习资源、互动教程和专题课程等资源,这些资源来自全球的250多家出版社,包括O’Reilly Media、Harvard Business Review、Prentice Hall Professional、Addison-Wesley Professional、Microsoft Press、Sams、Que、Peachpit Press、Adobe、Focal Press、Cisco Press、John Wiley & Sons、Syngress、Morgan Kaufmann、IBM Redbooks、Packt、Adobe Press、FT Press、Apress、Manning、New Riders、McGraw-Hill、Jones & Bartlett、Course Technology等。
若需了解更多相关信息,请访问 http://oreilly.com/safari。
本书网址为http://shop.oreilly.com/product/0636920031246,https://www.oreilly.com/catalog/errata.csp?isbn=0636920031246列出了本书的勘误。
致谢
首先要感谢Maker Media团队给我机会完成本书——尽管这是我的第一本书——感谢他们有足够的耐心指导我如何去写一本书!我也十分感谢Brian Jepson和Anna Kaziunas France,尽管他们现在已经不在Maker Media了,但我会开始写作本书是因为他们在其中起了至关重要的作用。
我要感谢我那了不起的太太Marianne——不仅仅是因为她在我写作本书的过程和我平时工作中给予的帮助,还因为她在过去10年中给予了我自信,并支持我那些疯狂的想法,让我可以专心工作。
如果没有最初这些了不起的支持者,本书和Espruino可能都不会存在。在最开始时正是在他们的帮助下我才完成了第一块Espruino开发板,并且从那之后,在他们的持续帮助下,Espruino才会一步步走向今天的成熟。Espruino论坛中的成员们做出了巨大的贡献,在他们的赞助下,我才有更多时间去完成更多的基于Espruino的有趣项目。同样要十分感谢那些在GitHub中向我提交漏洞报告的人们,正是在他们的帮助下,我才能完善该项目。
我现在采用的绝大部分都是开源软件,像Linux(Mint)、GCC、Chromium、Eclipse、Atom、Gimp、Inkscape和LibreOffice等工具。GCC常常被忽视,在我刚开始Espruino相关工作时,许多嵌入式设备还没有一个可用的、免费的C编译器。
ST微电子公司的Laurent Desseignes和Sebastien Marsanne在STM32的Espruino开发板方面给予了巨大的支持,还有Michael Dietz 等其他在Nordic Semiconductor译注工作的开发者们,在我们开发Puck.js期间也给予了我们巨大的帮助。
谷歌的Fran?ois Beaufort 很出色地帮我们完成了Web Bluetooth和Chrome浏览器物理层的支持,绿色软件园(Green Park Software)的David Park则完成了一个WebBLE 应用程序,该程序在iOS操作系统中实现了Web Bluetooth的支持。还需感谢ARM mbed的Rob Moran、Jonathan Austin、Hugo Vincent 和Simon Ford,他们给了我宝贵的建议,并帮助宣传了Espruino,促使micro:bit 这样的设计方式在Espruino中得以实现。
剑桥大学计算机实验室也提供了巨大的帮助——不仅仅因为它所给予我的教育,还因为它在我毕业之后仍给予了我持续支持。Stuart Newstead可以说是一位梦幻级导师,是他让我知道不能将所有时间都花在写代码上,而需要关注更多其他的事情!
我还要感谢Tim Hunkin和Rex Garrod,他们在20世纪80年代主持的电视节目《机器的秘密生活》(The Secret Life of Machines)对我有巨大的影响。本书中的打印机项目正是为了对标和模仿他们节目中所提到的传真机。
最后,我想说如果没有我的父母Fred和Pat Williams,我可能永远都不会做这件事情。如果在成长道路中没有他们的帮助和支持(如那些无止境供应的电脑器件和电子组件),我也许永远不会走向软件和电子这条路。我的整个童年都在制作和实验,那些电视节目中所提到的项目我都可以直接在我父亲做的设备中进行实验。希望本书可以帮助孩子们更多地像我童年一样,拥有充满兴奋和奇迹的经历。
---------------------------Go程序设计语言---------------------------
The Go Programming Language
“Go是一种开源的程序设计语言,它意在使得人们能够方便地构建简单、可靠、高效的软件。”(来自Go官网golang.org)
Go在2007年9月形成构想,并于2009年11月发布,其发明人是Robert Griesemer、Rob Pike和Ken Thompson,这几位都任职于Google。该语言及其配套工具集使得编译和执行既富有表达力又高效,而且使得程序员能够轻松写出可靠、健壮的程序。
Go和C从表面上看起来相似,而且和C一样,它也是专业程序员使用的一种工具,兼有事半功倍之效。但是Go远不止是C的一种升级版本。基于多种其他语言,它取其精华,去其糟粕。它实现并发功能的设施是全新的、高效的,实现数据抽象和面向对象的途径是极其灵活的。它还实现了自动化的内存管理,或称为垃圾回收。
Go特别适用于构建基础设施类软件(如网络服务器),以及程序员使用的工具和系统等。但它的的确确是一种通用语言,而且在诸多领域(如图像处理、移动应用和机器学习)中都能发现它的身影。它在很多场合下用于替换无类型的脚本语言,这是由于它兼顾了表达力和安全性:Go程序通常比动态语言程序运行速度要快,由于意料之外的类型错误而导致崩溃的情形更是少得多。
Go是个开源项目,所以其编译器、库和工具的源代码是人人皆可免费取得的。来自全世界的社区都在积极地向这个项目贡献代码。Go的运行环境包括类UNIX系统——Linux、FreeBSD、OpenBSD和Mac OS X,还有Plan 9和Microsoft Windows。只要在其中一个环境中写了一个程序,那么基本上不加修改它就可以运行在其他环境中。
本书旨在帮助读者立刻开始使用Go,以及熟练掌握这门语言,并充分地利用Go的语言特性和标准库来撰写清晰的、符合习惯用法的、高效的程序。
Go的起源
和生物学物种一样,成功的语言会繁衍后代,这些后代语言会从它们的祖先那里汲取各种优点;有时候,语言间的“混血”会产生异常强大的力量;在一些罕见情况下,某个重大的语言特性也可能凭空出现而并无先例。通过考察语言间的影响,我们可以学得不少知识,比如语言为什么会变成这个样子,以及它适合用于哪些环境,等等。
下图展示了更早出现的程序设计语言对Go产生的最重要影响。
Go有时会称为“类C语言”或“21世纪的C”。从C中,Go继承了表达式语法、控制流语句、基本数据类型、按值调用的形参传递和指针,但比这些更重要的是,继承了C所强调的要点:程序要编译成高效的机器码,并自然地与所处的操作系统提供的抽象机制相配合。
可是,Go的家谱中还有其他祖先。产生主要影响的是由Niklaus Wirth设计的、以Pascal为发端的一个语言支流。Modula-2启发了包概念。Oberon消除了模块接口文件和模块实现文件之间的差异。Oberon-2影响了包、导入和声明的语法,并提供了方法声明的语法。
Go的另一支世系祖先——它使得Go相对于当下的程序设计语言显得卓然不群,是在贝尔实验室开发的一系列名不见经传的研究用语言。这些语言都受到了通信顺序进程(Communicating Sequential Process,CSP)的启发,CSP由Tony Hoare于1978年在发表的关于并发性基础的开创性论文中提出。在CSP中,程序就是一组无共享状态进程的并行组合,进程间的通信和同步采用通道完成。不过,Hoare提出的CSP是一种形式语言,仅用于描述并发性的基本概念,并不是一种用来撰写可执行程序的程序设计语言。
Rob Pike等人开始动手做一些实验,尝试把CSP实现为真正的语言。第一种这样的语言称为Squeak(“和鼠类沟通的语言”),它是一种用于处理鼠标和键盘事件的语言,其中具有静态创建的通道。紧接着它的是Newsqueak,它具有类C的语句和表达式语法,以及类Pascal的类型记法。它是一种纯粹的函数式语言,具有垃圾回收功能,同样也以管理键盘、鼠标和窗口事件为目标。通道变成了“一等”值(first-class value),它可以动态创建并用变量存储。
Plan 9操作系统将这些思想都纳入一种称为Alef的语言中。Alef尝试将Newsqueak改造成一种可用的系统级程序设计语言,但垃圾回收功能的缺失使得它在处理并发性时捉襟见肘。
Go中的其他结构也会不时显示出某些并非来自祖先的基因。例如,iota多多少少有点APL的影子,而嵌套函数的词法作用域则来自Scheme(以及由之而来的大部分语言)。在Go语言中,也可以发现全新的变异。Go中新颖的slice不仅为动态数组提供了高效的随机访问功能,还允许旧式链表的复杂共享机制。另外,defer语句也是Go中新引入的。
Go项目
所有的程序设计语言都反映了其发明者的程序设计哲理,其中相当大的一部分是对于此前语言已知缺点的应对措施。Go这个项目也诞生于挫败感,这种挫败感来源于Google的若干复杂性激增的软件系统。(而且这个问题绝非Google所独有的。)
“复杂性是以乘积方式增长的。”Rob Pike如是说。为了修复某个问题,一点点地将系统的某个部分变得更加复杂,这不可避免地也给其他部分增加了复杂性。在不断要求增加系统功能、选项和配置,以及快速发布的压力之下,简单性往往被忽视了(尽管长期来看,简单性才是好软件的不二法门)。
要实现简单性,就要求在项目的一开始就浓缩思想的本质,并在项目的整个生命周期制定更具体的准则,以分辨出哪些变化是好的,哪些是坏的或致命的。只要足够努力,好的变化就既可以实现目的,又能够不损害Fred Brooks所谓软件设计上的“概念完整性”。坏的变化就做不到这一点,而致命的变化则会牺牲“简单性”去换得浅薄的“方便性”。但是,只有通过设计上的简单性,系统才能在增长过程中保持稳定、安全和自洽。
Go项目不仅包括该语言本身及其工具和标准库,还有决不能忽视的一点,就是它保持极端简单性的行为文化。在高级语言中,Go出现得较晚,因而有一定后发优势,它的基础部分实现得不错:有垃圾回收、包系统、一等公民函数、词法作用域、系统调用接口,还有默认用UTF-8编码的不可变字符串。但相对来说,它的语言特性不多,而且不太会增加新特性了。比如,它没有隐式数值类型强制转换,没有构造或析构函数,没有运算符重载,没有形参默认值,没有继承,没有泛型,没有异常,没有宏,没有函数注解,没有线程局部存储。这门语言成熟而且稳定,并且保证兼容更早版本:在旧版本的Go语言中写的程序,可以在新版本的编译器和标准库下编译与运行。
Go的类型系统足可以使程序员避免在动态语言中会无意犯下的绝大多数错误,但相对而言,它在带类型的语言中又算是类型系统比较简单的。其实现方法有时候会导致类型框架林立却彼此孤立的“无类型”程序设计风格,并且Go程序员在类型方面不会像C++或Haskell程序员那样走极端——反复表达类型安全性以证明语言是基于类型的。但在实际工作中,Go却能为程序员提供只有强类型的系统才能实现的安全性和运行时性能,而不让程序员承担其复杂性。
Go提倡充分利用当代计算机系统设计,尤其强调局部性的重要意义。其内置数据类型和大多数库数据结构都经过仔细设计,力求以自然方式工作,而不要求显式的初始化或隐式的构造函数。这么一来,隐藏在代码中的内存分配和内存写入就大大减少了。Go中的聚合类型(结构体和数组)都以直接方式持有其元素,与使用间接字段的语言相比,它需要更少的存储空间以及更少的分配操作和指针间接寻址操作。正如前面提到的那样,由于现代计算机都是并行工作的,因此Go具有基于CSP的并行特性。Go还提供了变长栈来运行其轻量级线程,或称为goroutine。这个栈初始时非常小,所以创建一个goroutine的成本极低,创建100万个也完全可以接受。
Go标准库常常称作“自带电池的语言”,它提供了清晰的构件,以及用于I/O、文本处理、图形、加密、网络、分布式应用的API,而且对许多标准文件格式和协议都提供了支持。Go的库和工具充分地尊重惯例,避免了配置和解释,从而简化了程序逻辑,提高了多种多样的Go程序之间的相似性,使得它更容易学习和掌握。采用go工具构建的项目,仅使用文件和标识符的名字(在极少情况下使用特殊注释),就可以推断出一个项目使用的所有库、可执行文件、测试、性能基准、示例、平台相关变体,以及文档。Go的源代码中就包含了构建的规格说明。
本书结构
我们假定你已用一两种其他语言编过程序,可能是像C、C++或Java那样的编译型语言,也可能是像Python、Ruby或JavaScript那样的解释型语言,所以本书不会像针对一个零基础的初学者那样事无巨细地讲述所有内容。表面上的语法大体雷同,变量、常量、表达式、控制流和函数也一样。
第1章是关于Go的基础结构的综述,通过十几个完成日常任务(包括读写文件、格式化文本、创建图像,以及在Internet客户端和服务器之间通信)的程序来介绍这门语言。
第2章讲述Go程序的组成元素——声明、变量、新类型、包和文件,以及作用域。第3章讨论数值、布尔量、字符串、常量,还解释如何处理Unicode。第4章描述复合类型,即使用简单类型构造的类型,形式有数组、map、结构体,还有slice(Go中动态列表的实现)。第5章概述函数,并讨论错误处理、宕机(panic)和恢复(recover),以及defer语句。
可以看出,第1~5章是基础性的,其内容是任何主流命令式语言都有的。Go的语法和风格可能与其他语言有所不同,但大多数程序员都能很快掌握这些内容。余下的章节重点讨论Go语言中与惯常做法有一定区别的内容,包括方法、接口、并发、包、测试和反射。
Go以一种不同寻常的方式来诠释面向对象程序设计。它没有类继承,甚至没有类。较复杂的对象行为是通过较简单的对象组合(而非继承)完成的。方法可以关联到任何用户定义的类型,而不一定是结构体。具体类型和抽象类型(即接口)之间的关系是隐式的,所以一个具体类型可能会实现该类型设计者没有意识到其存在的接口。第6章讲述方法,第7章讲述接口。
第8章介绍Go的并发性处理途径,它基于CSP思想,采用goroutine和通道实现。第9章则讨论并发性中基于共享变量的一些传统话题。
第10章讨论包,也就是组织库的机制。该章也说明如何高效地利用go工具,仅仅这个工具,就提供了编译、测试、性能基准测试、程序格式化、文档,以及完成许多其他任务的功能。
第11章讨论测试,在这里Go采取了显著的轻量级途径,避免了重重抽象的框架,转而使用简单的库和工具。测试库提供了一个基础,在其之上根据需要可以构建更复杂的抽象。
第12章讨论反射,即程序在执行期间考察自身表示方式的能力。反射是一种强大的工具,不过要慎重使用它,该章通过演示如何用它来实现某些重要的Go库,解释了如何统筹兼顾。第13章解释低级程序设计的细节(它运用unsafe包来绕过Go的类型系统),以及什么时候适合这样做。
每章都配以一定数量的练习,可以用来测试你对Go的理解,或者探索对书中示例的扩展和变形。
除了最简单的示例代码以外,书中所有的示例代码都可以从gopl.io网站的公开Git仓库下载。每个示例以其包的导入路径开头和命名,从而能够方便地使用go get命令获取、构建和安装。你需要选取一个目录作为你的Go工作空间,并使GOPATH环境变量指向它。在必要时,go工具会创建该目录。例如:
要运行这些例子,至少需要使用1.5版本的Go语言。
如果你的计算机上的go工具版本太旧或者缺失,请按https://golang.org/doc/install上的步骤操作。
更多信息来源
关于Go的更多信息,最好的来源就是Go的官方网站:https://golang.org。其中列出了文档供读者访问,包括Go程序设计语言规范、标准包等。其中还列出Go语言教程,指导如何撰写Go程序,以及如何撰写好的Go程序,还有大量在线文本和视频资源,这些都是本书的主要补充资源。位于blog.golang.org的Go博客发布的是关于Go的最好文章,内容涉及该语言当下的状态、未来的计划、会议方面的报告,还有Go相关的大量话题的深度解读。
Go官网在线访问最有用的一个方面(这也是纸质书的一个令人遗憾的限制),就是提供了从描述Go程序的网页上直接运行的能力。这种功能由位于play.golang.org的Go训练场(Playground)提供,也可以嵌入其他页面,比如golang.org的首页,或者由godoc工具提供的文档页面。
训练场为读者对简短的程序执行简单的实验提供了方便,有助于读者检验自己对语法、语义和库包的理解,并且它在很多方面取代了其他语言中的读取–求值–输出循环(Read-Eval-Print Loop,REPL)。它的永久URL对于共享Go代码段、报告bug或提出建议都很有用。
在训练场的基础之上,位于tour.golang.org的Go Tour就是一系列简短的交互式课程(内容是Go语言的基础思想和结构),也是学习整门语言的系统资源。
训练场和Go Tour的主要缺点在于它只允许导入标准库,并且很多库特性(比如网络库)都出于可操作性或安全原因限制使用。而要编译和运行每个程序,都要求Internet连接。所以,欲进行更详尽的实验,需要在本机上运行Go程序。幸运的是,下载过程相当简单,从golang.org获取Go的安装版本并开始撰写和运行你自己的Go程序,用不了几分钟。
由于Go是个开源项目,因此你可以从https://golang.org/pkg上在线读取标准库中的任何类型或函数的代码,每个供下载的版本都同样包含这些代码。请使用这些代码来弄明白某些程序的运行原理、回答关于程序细节的问题,也可以用它们来学一学专家是如何写出一流的Go代码的。
致谢
Go团队的核心成员Rob Pike和Russ Cox仔细通读了初稿数次,他们从遣词造句到整体结构都对本书提出了重要的建议。在准备本书的日语版时,柴田芳树所做的贡献大大超过了他负担的义务,他的火眼金睛发现了英语版中的上下文不一致性,以及代码中的错误。非常感谢Brian Goetz、Corey Kosak、Arnold Robbins、Josh Bleecher Snyder以及Peter Weinberger对全书初稿进行彻底的审查并提出批判性的建议。
感谢Sameer Ajmani、Ittai Balaban、David Crawshaw、Billy Donohue、Jonathan Feinberg、Andrew Gerrand、Robert Griesemer、John Linderman、Minux Ma、Bryan Mills、Bala Natarajan、Cosmos Nicolaou、Paul Staniforth、Nigel Tao以及Howard Trickey提供的诸多有用建议。也感谢David Brailsford和Raph Levien的排版建议。
Addison-Wesley出版社的编辑Greg Doench策划了本书,而且一直不断地给予帮助。Addison-Wesley的制作团队——John Fuller、Dayna Isley、Julie Nahil、Chuti Prasertsith以及Barbara Wood——非常杰出,给予作者大量的支持。
Alan Donovan想要感谢Google的Sameer Ajmani、Chris Demetriou、Walt Drummond以及Reid Tatge让他腾出时间来写作这本书,还要感谢Stephen Donovan的建议和及时的鼓励。最重要的是,感谢他的妻子Leila Kazemi无限的热情和长期的支持,谅解了他在家庭生活中的疏忽。
Brian Kernighan对他的朋友和同事深表谢意,他们对Kernighan花费了很长时间以通俗易懂的语言写作本书表现出了极大的耐心和理解。尤其是他的妻子Meg,她为Kernighan的写作以及太多的其他事务提供了不懈的支持。