求推荐能够在国外域名空间访问,稳定且自由度高的空间和域名申请网站。

谁能够给个国外的免费空间,再给个免费的一级域名注册地址_百度知道现在的学习已经不仅仅局限于课堂之上,也不光来自于工作之中,借着在线教育网站(Massive open online course, MOOC)的崛起,越来越多的人通过在线学习了补充自身的知识,提高各个方面的能力。之前分享过《》,这里再分享一些非常有用的提供在线课程的网站,可以在上下班路上或者平时休息时播放观看。
Udacity主要是编程开发技术类的在线课程,而且这些课程的由诸如Google、Facebook这些大公司提供,所以质量也很高。这些视频大部分需要付费才能观看,但也有很多免费又值得学习的内容。我个人在上边获得了很多关于Android开发方面的能力提升。
对于付费用户,网站还会在完成某些课程内容后提供“Nanodegree”的荣誉来证明对某项知识已被掌握。
Udemy上的视频教程包罗万象,对于不满足于一个领域的知识的人,可以在上边了解其他领域的基础知识,比如房产投资,股票交易。
同样的,网站的大部分课程的需要付费,而且价格还不低,但是一些免费课程也非常的受欢迎。另外很多课程每节视频的长度都较短,所以很容易快速完成一节课程的学习。
Code School上的内容注重与代码编写,课程会带着你一步步练习教授的知识。网站还有成就系统,完成课程内容会获得相应的徽章奖励,非常有趣。每个课程的界面都各有特色,但是课程内容偏于基础,适合作为复习知识之用。个人只学习了《》,了解许多Chrome开发工具的使用。
CodeCademy类似于Code School,也是注重于上手实践,让学习者通过一步步实际操作来了解一门编程技术知识。该网站的免费课程并不是很多的,但依然可用于复习巩固知识。
NewCircle其实主要从事公司职业培训,我也是因为Twitter University知道这个网站。虽然是面向企业,但是上边发布的视频内容很多都非常有用。
其他网站都是有一个组织维护着,但是Level Up Tutorials只由一个从事网页开发的工程师维护着。网站的内容只要集中于设计方面,但是对于从事前端开发的软件工程师来说非常值得学习和了解。
随着移动设备越来越普及,人们也越来越倾向在手机上来观看视频教学,这样也能更合理的利用通勤时间。Udacity和Udemy都提供了App下载,从而通过手机来学习上边的知识内容;Code School和CodeCademy因为要进行实际练习,所以只能在电脑上进行;NewCircle和Level Up Tutorials的视频都发布在YouTube上,所以如果可以访问YouTube,只要把那些视频加入播放列表也能方便的在手机上播放。
上边这些网站只要在国外,国内的在线教育发展也非常火热,而且以上也只是非常小的一部分。对于有更多学习目标,比如获得在线学位,可以在下方的引用链接中找到更多合适的在线教育资源。
References:
发信人: federal (先空着吧), 信区: Joke标
题: [转载]岁月发信站: 水木社区 (Mon Jan 18 10:17:44 2016), 站内
我正在候诊室等着和我的新牙医见面。挂在墙上的行医执照上面的名字令我想到了一位25年前我的高中男同学。我之所以记得这个名字,是因为他是一个又高又帅的家伙,那时女生都喜欢他。于是,我心里充满期待。可是,一见到他,我立即失望了。这个人满脸皱纹,秃顶,岁数太大了,不可能是我的同学。他在给我检查牙齿的时候,我提到我上过的那所中学,并问他是否也上过。“是呀,我就是那所中学毕业的。”他答道。“那你是哪一年毕业的?”我问。“1984年。”“啊,是我班上的!”我惊讶得喊了出来。他凝神看了看我,然后问:“您教的是哪一门课?”—
※ 来源:·水木社区 http://www.newsmth.net·[FROM: 166.111.134.*]
如果你经常看这个博客,就会知道我写了一本书。
现在第二版问世了,书名为。
购买链接如下。全部是现货。现在下单,最快明天就能拿到。
老读者都知道,这本书是的。你可以先看看,再决定买不买。出版社有利润压力,但还是支持我开源。希望不会影响销量,如果最后亏钱,就对不起朋友了。
这本书的质量,我很有信心。市场上唯一的ES6出版物,可能也是最好的 JavaScript 进阶教程。所有语法点都讲解了,尽量做到深入浅出。
上已经有了 2000+ 颗星,每一行代码都被无数读者过。
2015年,我只做成了一件事,就是写完这本书(第二版)。想想有些伤感,一年的起起伏伏,到头来只是一本书稿。
写书是一件寂寞的事,仿佛一个人坐在电脑前的漫漫长征。现在,我终于可以把它交给你了。你拿在手中的,就是我的这一年,所有的诚意和坚持。
JavaScript 这门语言会有远大未来,但愿我的这本书、以及你,我亲爱的读者,也都是如此。
版权声明:自由转载-非商用-非衍生-保持署名()
发表日期: 日
更多内容:
购买文集:
社交媒体:,
Feed订阅:
(说明:本文原载2016年第二期《财新周刊》)
日中午,Debian操作系统的创始人Ian Murdock,在推特上发布了一条简短的消息。
“我将在今晚自杀,请不要打扰,我要抓紧时间说一些事情,不让它们跟我一起走。”
没过多久,他就删除了这条消息,并且关闭了推特账户。大家猜测纷纷,不知道是真是假。第二天,他的死讯被确认。
对于大多数人来说,Debian是一个陌生的名字,但是在程序员之中,它非常有名,国际空间站就使用它。这是一种计算机操作系统,类似于Windows,但是一般只用于服务器。所以,普通用户接触不到,只有企业级用户和专业程序员才会使用。
你可以想象一下,有一样东西你每天使用,突然某天清晨传来消息,它的发明人非正常死亡,你会是什么感受,内心会不会震动?这就是整个IT业界听到这个消息的反应,尤其是死者只有42岁,实在太年轻了。
1973年,Ian Murdock生于西德,后来随父母移民美国。他从小就对计算机感兴趣,9岁开始编程。1993年8月,在普度大学读大二时,他接触到了芬兰大学生林奈斯的Linux项目,顿时产生了兴趣。Linux是操作系统的内核,就好比汽车的发动机,虽然重要,但是本身并不足以使用,必须配合各种零件,装配成一辆汽车以后才能行驶。Ian Murdock想做的就是,将Linux装配成一辆全功能的汽车。
经过几个月的努力,他在1993年底发布了Debian的最初版本。这个名字是女友的名字Deb和他自己名字的结合。当时的计算机社区,急需一个新的操作系统,很多人看到以后,就对这个项目产生了兴趣,纷纷试用和加入。
跟Windows不一样的是,Debian完全免费,可以任意使用。因为Ian Murdock相信免费软件可以帮助到更多需要的人,所以采用了一种叫做GPL的软件授权协议。根据这种协议,只要满足一个条件,你怎么使用都可以,那就是一旦你修改了软件,就必须把你的修改,也同样免费地公开出来。这个条件防止了有人将他人的免费成果占为己有。
随着加入者越来越多,以及GPL协议的保障,Debian项目的影响力越变越大,逐渐成为两种主流的基于Linux的操作系统之一。后来,又得到自由软件基金会的赞助,在全世界获得广泛采用。
1996年,Debian发布0.93版以后,Ian Murdock就不再参与开发了,把领导权交给了其他人。他成立了一家创业公司,担任CTO。2007年,他加入SUN公司担任副总裁,直到2011年。然后,又在不同的硅谷公司任职,直到去世为止。
由于家人要求保护隐私,他的死因没有公布,外界不得而知。现在只知道,他在早先的推特上曾经提到过,与警察发生了冲突,决心抗争到底。旧金山警方则说,有人报警他醉酒后企图闯入民宅,警方赶到制止时,他与警察发生暴力冲突,导致被拘留。警方没有公布,他到底是不是自杀。
Ian Murdock的性格内向,采访和报道都很少。1996年,他与大学时代的女友Debra Lynn(就是Debian这个名字的另一半)结婚,但是在2007年离婚,没有子女。
因为Ian Murdock的死,很多人提到了另一个计算机天才Phil Katz。他是ZIP压缩算法的发明人,日,他被发现孤独一人死在一家小旅馆的桌子旁边,怀里抱着一个空的酒瓶,死因是长期酗酒导致的胰脏严重内出血。此时,他与家人已经长期断绝关系,与自己创立的公司的员工也很久没联系了。他死时只有37岁。
奥地利小说家茨威格的最后一篇小说《象棋的故事》,描述过一个令人心碎的故事。一个普通人关进纳粹监狱,身边只有一本象棋棋谱。漫长的关押和惊恐绝望之中,他只能靠背棋谱,转移一些注意力。由于长期地自己跟自己下棋,他几乎陷入精神分裂和疯狂。等到后来放出监狱,他的棋艺竟然可以战胜世界冠军,但是他对象棋乃至人生,已经毫无兴趣了。茨威格用这篇小说比喻自己的人生,写完后五个月,他就自杀了。
Ian Murdock以及许多计算机天才,就像《象棋的故事》的这位主人公。他们精通计算机,但是个人生活失去了平衡。除了计算机以外,没有人可以对话,生活也失去了意义,最终走上了绝路。
我们这个计算机时代,让人可以不依赖社交,只靠机器就能满足生活的基本需求。生活因此变得方便,但也不可避免地变得更孤独。索尔·贝娄有一部长篇小说,名字叫做《更多的人死于心碎》,这大概就是Ian Murdock真正的死因。
版权声明:自由转载-非商用-非衍生-保持署名()
发表日期: 日
更多内容:
购买文集:
社交媒体:,
Feed订阅:
过去一年中,前端技术大发展,最耀眼的明星就是。
React 本身只涉及UI层,如果搭建大型应用,必须搭配一个前端框架。也就是说,你至少要学两样东西,才能基本满足需要:React + 前端框架。
Facebook官方使用的是 。本文就介绍如何在 React 的基础上,使用 Flux 组织代码和安排内部逻辑,使得你的应用更易于开发和维护。
阅读本文之前,我假设你已经掌握了 React 。如果还没有,可以先看我写的。与以前一样,本文的目标是使用最简单的语言、最好懂的例子,让你一看就会。
一、Flux 是什么?
简单说,Flux 是一种架构思想,专门解决软件的结构问题。它跟是同一类东西,但是更加。
Flux存在多种实现(),本文采用的是。
二、安装 Demo
为了便于讲解,我写了一个。
请先安装一下。
$ git clone /ruanyf/extremely-simple-flux-demo.git
$ cd extremely-simple-flux-demo && npm install
$ npm start
然后,访问 http://127.0.0.1:8080 。
你会看到一个按钮。这就是我们的Demo。
三、基本概念
讲解代码之前,你需要知道一些 Flux 的基本概念。
首先,Flux将一个应用分成四个部分。
View: 视图层
Action(动作):视图层发出的消息(比如mouseClick)
Dispatcher(派发器):用来接收Actions、执行回调函数
Store(数据层):用来存放应用的状态,一旦发生变动,就提醒Views要更新页面
Flux 的最大特点,就是数据的”单向流动”。
用户访问 View
View 发出用户的 Action
Dispatcher 收到 Action,要求 Store 进行相应的更新
Store 更新后,发出一个”change”事件
View 收到”change”事件后,更新页面
上面过程中,数据总是”单向流动”,任何相邻的部分都不会发生数据的”双向流动”。这保证了流程的清晰。
读到这里,你可能感到一头雾水,OK,这是正常的。接下来,我会详细讲解每一步。
四、View(第一部分)
请打开 Demo 的首页 ,你会看到只加载了一个组件。
// index.jsx
var React = require('react');
var ReactDOM = require('react-dom');
var MyButtonController = require('./components/MyButtonController');
ReactDOM.render(
&MyButtonController/&,
document.querySelector('#example')
上面代码中,你可能注意到了,组件的名字不是 MyButton,而是 MyButtonController。这是为什么?
这里,我采用的是 React 的
模式。”controller view”组件只用来保存状态,然后将其转发给子组件。MyButtonController的很简单。
// components/MyButtonController.jsx
var React = require('react');
var ButtonActions = require('../actions/ButtonActions');
var MyButton = require('./MyButton');
var MyButtonController = React.createClass({
createNewItem: function (event) {
ButtonActions.addNewItem('new item');
render: function() {
return &MyButton
onClick={this.createNewItem}
module.exports = MyButtonC
上面代码中,MyButtonController将参数传给子组件MyButton。后者的甚至更简单。
// components/MyButton.jsx
var React = require('react');
var MyButton = function(props) {
return &div&
&button onClick={props.onClick}&New Item&/button&
module.exports = MyB
上面代码中,你可以看到是一个纯组件(即不含有任何状态),从而方便了测试和复用。这就是”controll view”模式的最大优点。
MyButton只有一个逻辑,就是一旦用户点击,就调用 方法,向Dispatcher发出一个Action。
// components/MyButtonController.jsx
createNewItem: function (event) {
ButtonActions.addNewItem('new item');
上面代码中,调用createNewItem方法,会触发名为addNewItem的Action。
五、Action
每个Action都是一个对象,包含一个actionType属性(说明动作的类型)和一些其他属性(用来传递数据)。
在这个Demo里面, 对象用于存放所有的Action。
// actions/ButtonActions.js
var AppDispatcher = require('../dispatcher/AppDispatcher');
var ButtonActions = {
addNewItem: function (text) {
AppDispatcher.dispatch({
actionType: 'ADD_NEW_ITEM',
text: text
上面代码中,ButtonActions.addNewItem方法使用AppDispatcher,把动作ADD_NEW_ITEM派发到Store。
六、Dispatcher
Dispatcher 的作用是将 Action 派发到 Store、。你可以把它看作一个路由器,负责在 View 和 Store 之间,建立 Action 的正确传递路线。注意,Dispatcher 只能有一个,而且是全局的。
Facebook官方的 输出一个类,你要写一个,生成 Dispatcher 实例。
// dispatcher/AppDispatcher.js
var Dispatcher = require('flux').D
module.exports = new Dispatcher();
AppDispatcher.register()方法用来登记各种Action的回调函数。
// dispatcher/AppDispatcher.js
var ListStore = require('../stores/ListStore');
AppDispatcher.register(function (action) {
switch(action.actionType) {
case 'ADD_NEW_ITEM':
ListStore.addNewItemHandler(action.text);
ListStore.emitChange();
上面代码中,Dispatcher收到ADD_NEW_ITEM动作,就会执行回调函数,对ListStore进行操作。
记住,Dispatcher 只用来派发 Action,不应该有其他逻辑。
Store 保存整个应用的状态。它的角色有点像 MVC 架构之中的Model 。
在我们的 Demo 中,有一个,所有数据都存放在那里。
// stores/ListStore.js
var ListStore = {
items: [],
getAll: function() {
return this.
addNewItemHandler: function (text) {
this.items.push(text);
emitChange: function () {
this.emit('change');
module.exports = ListS
上面代码中,ListStore.items用来保存条目,ListStore.getAll()用来读取所有条目,ListStore.emitChange()用来发出一个”change”事件。
由于 Store 需要在变动后向 View 发送”change”事件,因此它必须实现事件接口。
// stores/ListStore.js
var EventEmitter = require('events').EventE
var assign = require('object-assign');
var ListStore = assign({}, EventEmitter.prototype, {
items: [],
getAll: function () {
return this.
addNewItemHandler: function (text) {
this.items.push(text);
emitChange: function () {
this.emit('change');
addChangeListener: function(callback) {
this.on('change', callback);
removeChangeListener: function(callback) {
this.removeListener('change', callback);
上面代码中,ListStore继承了EventEmitter.prototype,因此就能使用ListStore.on()和ListStore.emit(),来监听和触发事件了。
Store 更新后(this.addNewItemHandler())发出事件(this.emitChange()),表明状态已经改变。 View 监听到这个事件,就可以查询新的状态,更新页面了。
(第二部分)
现在,我们再回过头来修改
,让它监听 Store 的 change 事件。
// components/MyButtonController.jsx
var React = require('react');
var ListStore = require('../stores/ListStore');
var ButtonActions = require('../actions/ButtonActions');
var MyButton = require('./MyButton');
var MyButtonController = React.createClass({
getInitialState: function () {
items: ListStore.getAll()
componentDidMount: function() {
ListStore.addChangeListener(this._onChange);
componentWillUnmount: function() {
ListStore.removeChangeListener(this._onChange);
_onChange: function () {
this.setState({
items: ListStore.getAll()
createNewItem: function (event) {
ButtonActions.addNewItem('new item');
render: function() {
return &MyButton
items={this.state.items}
onClick={this.createNewItem}
上面代码中,你可以看到当MyButtonController 发现 Store 发出 change 事件,就会调用 this._onChange 更新组件状态,从而触发重新渲染。
// components/MyButton.jsx
var React = require('react');
var MyButton = function(props) {
var items = props.
var itemHtml = items.map(function (listItem, i) {
return &li key={i}&{listItem}&/li&;
return &div&
&ul&{itemHtml}&/ul&
&button onClick={props.onClick}&New Item&/button&
module.exports = MyB
本文受到了Andrew Ray 的文章的启发。
版权声明:自由转载-非商用-非衍生-保持署名()
发表日期: 日
更多内容:
购买文集:
社交媒体:,
Feed订阅:
上个月,”机器人之父”去世。
的编辑让我写他的介绍。我查了网上资料,发现那些早期机器人的照片非常有意思。
下面就是我写的人物生平,配上这些有趣的照片。
12月1日,被誉为”机器人之父”的约瑟夫·恩格尔伯格(Joseph Engelberger),在美国因病去世,享年90岁。他发明制造了人类第一台机器人。
恩格尔伯格1925年生于纽约,先后获得哥伦比亚大学物理学士和电子工程硕士学位。
1950年,恩格尔伯格读到了阿西莫夫的小说集《我,机器人》(I, Robot),爱不释手,产生了制造机器人的念头。
1956年的一场酒会,他偶遇发明家德沃尔(George C. Devol)。后者提到,他刚刚申请了一个专利,叫做”可编程的用于移动物体的设备”(Programmed Article Transfer)。恩格尔伯格脱口而出,”这不是阿西莫夫笔下的机器人吗!”两人一拍即合,决定合作创立一家生产机器人的公司。
新公司取名Unimation,意为”自动化单位”,1958年正式运作。当年就拿出了第一个产品:一个可以自动完成搬运的机械手臂。虽然,这个产品庞大而笨重,只能完成很简单的任务,但它是人类历史上第一个机器人。
恩格尔伯格和德沃尔从一开始就很清楚,机器人的研发成本很高,只有大公司才买得起。他们把研发方向定在为重型制造业服务,让机器人承担对人类有危险的工作。美国最大的通用汽车公司,是他们首先想到的目标客户。
恩格尔伯格游说通用汽车,希望他们买一台机器人。由于以前从来没有这种东西,很多人对这个产品将信将疑。1961年,通用汽车好不容易答应,在离纽约最近的新泽西工厂,装一台试试看。第一台Unimation机器人的成本是6万美元,售价却只有2万5千美元,因为不这样就卖不出去。
工业机器人在生产线上的作用非常明显,动作精准、永不疲倦、不怕高温和污染。比如,焊接工作处于高温环境,会产生有害气体,工人一不小心就会中毒,机器人就没有这个问题。
通用汽车开始订购更多的机器人,安装在全美各地的工厂,承担的工作扩展到焊接、油漆、粘合和装配。这帮助它取得了自动化生产的革命性突破,巩固和扩大了行业领先地位。其他汽车公司纷纷跟进,将机器人用于自家的流水线。美国机器人协会后来评价,恩格尔伯格的这个发明”彻底改变了现代工业和汽车制造的流程”。
上个世纪60年代,现代工业革命达到了顶峰,阿波罗计划把人类送上了月球,整个美国都对新技术、新设备充满了兴趣。1966年,最热门的晚间谈话节目把Unimation机器人请上了电视,让它对着300万全国观众,发高尔夫球、倒啤酒、挥舞指挥棒,甚至拉手风琴。从此,恩格尔伯格一举成名。
1969年,日本人将恩格尔伯格请到东京演讲,指导日本汽车厂商研发机器人。
川崎重工引进了Unimation机器手臂。这件事对日本汽车工业有深远影响,日本后来超过美国成了”机器人王国”。恩格尔伯格在这个过程中扮演了重要角色,他在日本的知名度甚至比在美国还要大。
随着年龄和荣誉的增长,恩格尔伯格在技术上变得保守。他不赞成在机器人内部,使用电机替代液压,也不认为制造有腿的机器人是必要的,使用轮子更符合实际。这导致他在1983年将Unimation公司以1.07亿美元的价格,卖给了西屋公司,退出了工业机器人行业。
那时,恩格尔伯格可谓功成名就。第一个Unimation机器人,在运作了10万个小时之后,已经光荣退役,送进了博物馆。全世界制作业使用的机器人,超过了300万个。他本人也当选了美国工程院院士。
他并没有就此止步,1984年又创建了TRC公司,研发服务机器人。1988年,他又推出了世界第一个服务业机器人HelpMate。
这个机器人用来在医院走廊穿行,为病人送饭、送药、送邮件,并记录病人的情况。
恩格尔伯格的目标是为机器人装上各种传感器,使其能够直接为人类服务,首先是帮助老年人和残疾人。
他觉得,很多老人进入养老院时,心智还十分健康,只是行动不便,不能很好地照顾自己。
机器人能够承担重复性家务,比如取物、清洁、做饭等等,用来护理老年人,最合适不过了。
接受彭博商业周刊采访时,恩格尔伯格说道,”家用机器人比工业机器人有更广阔的市场前景”。
他认为,机器人未来一定无处不在。他更想作为”家用机器人之父”被人们铭记。
版权声明:自由转载-非商用-非衍生-保持署名()
发表日期: 2016年1月 8日
更多内容:
购买文集:
社交媒体:,
Feed订阅:
本文作者:方弦
计算无处不在。
走进一个机房,在服务器排成的一道道墙之间,听着风扇的鼓噪,似乎能嗅出0和1在CPU和内存之间不间断的流动。从算筹算盘,到今天的计算机,我们用作计算的工具终于开始量到质的飞跃。计算机能做的事情越来越多,甚至超越了它们的制造者。上个世纪末,深蓝凭借前所未有的搜索和判断棋局的能力,成为第一台战胜人类国际象棋世界冠军的计算机,但它的胜利仍然仰仗于人类大师赋予的丰富国际象棋知识;而仅仅十余年后,Watson却已经能凭借自己的算法,先“理解”问题,然后有的放矢地在海量的数据库中寻找关联的答案。长此以往,工具将必在更多的方面超越它的制造者。而这一切,都来源于越来越精巧的计算。
计算似乎无所不能,宛如新的上帝。但即使是这位“上帝”,也逃不脱逻辑设定的界限。
第一位发现这一点的,便是图灵。
相信每个人都见识过Windows那令人忧郁的蓝屏或者黑屏吧。有时因为它,一个上午的工作一瞬间毁于一旦,这就不仅是令人忧郁而是令人抓狂了。在这个时候,你是否会在心中大声咒骂那帮写程序不小心让蓝屏一而再再而三出现的程序员呢?但程序员也不是铁打的,他们也会犯错误。而且对于商业软件来说,在上市之前会进行大量的测试,即使有程序错误溜过去了,大多也可以通过打补丁来修复。
但是对于某些软件来说,情况就麻烦得多了。
在1996年的一个不能说的日子,欧洲航天局第一次发射了新研制的Ariane 5运载火箭。在起飞37秒之后,新火箭很想不开地开花了。这令砸了几亿欧元进去的欧洲航天局非常不爽。经过调查,专家组发现,事故的罪魁祸首竟然是短短的一段代码。
Ariane 5爆炸场景
在Ariane 5的软件中,有一部分代码是直接来自它的前辈Ariane 4。由于Ariane 4当时非常成功,所以大家觉得这样做没什么问题,根据新的参数稍微修改一下代码就好了。问题是,修改并不完全。有一行代码需要将64位浮点数转换成16位整数,他们认为不会出现什么问题,所以没有进行修改。也没有测试这段代码。
就是这行代码,因为Ariane 5比前辈Ariane 4强力得多,于是在Ariane 4上没有问题的这行代码,在Ariane 5上发生了溢出错误:那个64位的浮点数代表的数值超出了16位整数可以表达的范围。在出错后,备用代码系统被启动,其中包含着同样的一行代码。结果就是整个系统被锁死了。更为讽刺的是,这行代码所在的函数,对于Ariane 5来说是不必要的。这场事故完全就是人祸。
经过这场事故之后,欧洲航天局大为震怒,决定要一劳永逸地解决Ariane 5的问题。他们的要求相当大胆:Ariane 5的软件代码,正式使用前要证明它们不会出现毁灭性的错误,比如不会溢出,不会死循环等等。
这其实并非易事。
我们复习一下停机问题:是否存在一种算法,给定任意的程序和输入,都能在有限的时间内判断该程序在给定的输入下是否会停止?正是图灵,证明了这一点是不可能做到的。于是,要编写一个能判断程序会不会进入死循环的算法,这是不可能的。但对于其他类型的程序错误,能否用算法判定呢?
很可惜,这也是不可能的。实际上,我们可以将停机问题规约为检测错误的问题。假设我们有一个程序P,能检测某段代码是否会出现除以零的错误,而我们想要借助这个程序判断某个图灵机在给定的输入下会不会停止。我们可以怎么做呢?首先,对于给定的图灵机和输入,我们可以机械化地将它们转化为一段不用除法但能够模拟该图灵机的代码,然后在模拟结束之后,强行计算1/0。我们将这段代码称为T。代码T在执行时会出现除以零的错误,当且仅当图灵机会停机。然后,我们将代码T输入程序P。于是,既然程序P能判定任意的代码会不会出错,那么就相当于它能判定任意的图灵机会不会停机,而这是不可能的。停机问题不存在普遍的算法,也就是说证明代码无误同样没有普遍算法。
但是,欧洲航天局的任务是否完全不可能完成呢?那倒也不是。停机定理只是说明了不存在程序能正确判定所有程序是否会停止,但欧洲航天局只需要证明Ariane 5的软件代码这一个程序不会出错,所以这条路也没有完全被堵死。
那么,有什么办法呢?
虽然我们不能判定所有程序是否不会出错,但我们能有效判定某些程序不会出错。比如说,如果一个程序没有任何循环语句或者跳转语句,那么这个程序最终肯定停止。但如果程序有循环语句又怎么办?这时,我们并不能确定程序会不会停止,而最保险的办法就是说“我不知道”。
这就是抽象释义(Abstract Interpretation)方法的根本:先抽象出程序的某些信息,再对这些信息进行自动分析,来尝试确定程序是否有着我们想要的性质,比如说不会死循环、不会溢出等等。我们不强求完美的分析,不强求能够识别出所有不出错程序。但为了安全起见,我们要求这种分析是可靠的,也就是说,如果分析的结果认为程序有着我们想要的性质,那么这个结论就不会出错。正是因为这样的权衡取舍,抽象释义方法才能正确有效地实行。
Galois connection,来自Cousot课件
根据抽象出来的信息多少,不同的抽象释义方法判断同一种性质的效果也不一样。一般来说,抽象出的信息越详细,能识别的拥有某种性质的程序就越多,相应地计算时间也越长。如何在性能和识别率之间做取舍,也是一门复杂的学问,对于不同的应用和数据结构,需要开发不同的抽象方法和自动分析算法。
多种抽象方法还有另一个优点。如果某个程序有着我们想要的性质,但是手头上的抽象释义方法又不能确定时,我们可以换用更精细的、利用更多信息的抽象方法。直接改写代码也是一种规避分析失败的方法。例如,我们想要证明某段代码不会出错,但某种抽象释义方法指示在某句代码上可能会有问题,那么我们可以通过修改代码,换用更加谨慎的处理方法,来保证抽象释义方法能确认新的代码不出问题。
抽象释义方法的奠基者是法国的Patrick Cousot和Radhia Cousot。这对夫妻档总结了一些对程序进行自动形式证明的方法,在此之上提出了抽象释义方法,将其形式化严格化。他们对抽象释义方法的推广也功不可没。除了Ariane 5的代码之外,在别的一些关键应用处的代码也利用抽象释义方法进行了至关重要的验证。一个例子是空中客车A380的控制代码,经过Patrick Cousot的一个小组开发的抽象释义软件Astrée验证,证明了这些控制代码运行时,不会产生像死循环、溢出或者被零除之类的一些软件问题,从而也给安全系数多加了一层保险。
Cousot夫妇,图片来自Wikipedia
不过,抽象释义方法只能证明程序有着某种我们想要的性质,不能说明程序是否完成了我们希望的任务。有没有办法做到这一点呢?
有一种激进的做法:让程序员在编写代码的同时,给出这段代码确实完成了给定任务的数学证明。
要给出这种证明,首先要解决的就是如何将“代码完成了给定任务”转换成数学命题。程序代码可以相当容易用逻辑表达,但代码需要完成什么任务,这只有程序员才知道。所以,要让程序员在编写代码的同时给出证明,为的是让程序员能用逻辑的形式确定这个函数的功能,如此才能得到要证明的命题。这种想法不仅仅是数学家的纸上谈兵。对于某些关键系统,多么微小的疏失都会导致极其严重的后果,人们愿意几乎不惜一切代价防止错误的发生,而对于计算机程序而言,又有什么比数学更坚实的基础呢?
要贯彻这种想法,在编写程序之前,必须先选择一种逻辑体系以及描述它的一种形式语言。这种形式语言必须贯穿整个代码编写的过程:先用形式语言描述程序的输入、输出、功能与限制,然后利用这种与形式语言相近的编程语言去具体编写代码,最后还要利用形式语言给出编写的代码能完美无瑕疵地实现所需功能的数学证明。这种做法又被称为演绎验证,是所谓的“形式化验证”的手段之一。
但数理逻辑毕竟不是一门容易的学科,数学证明对于很多人来说大概比编写代码要困难得多。所以,演绎验证多数也只会用在那些不容有失的关键系统,比如说牵涉人数众多的公共交通设施。例如,在1998年开始运营的巴黎地铁14号线,就是一条全自动的地铁,列车上没有司机,安全行驶也全靠传感器和软件,调度也只需要在控制室点点鼠标就能增加或减少投入运营的列车数量。于是,安全在很大程度上在于软件的可靠性。在控制列车的计算机中,某些与乘客安全休戚相关的关键代码是利用演绎验证编写的。在2012年,巴黎历史最悠久的地铁1号线也从人工运营转到与14号线同系列的全自动化系统。现在,这两条地铁线每天接待的人数加起来超过一百万,但从未因为自动化系统的错误导致乘客伤亡。从笔者的经验来说,它们可以算是巴黎最可靠的地铁线。
巴黎地铁14号线,图片来自Wikipedia
但仅有代码的正确性可能还不足以保证程序同样正确,因为代码毕竟不是程序,计算机不能直接执行代码。我们需要另一种名为“编译器”的程序。编译器是将程序员写的代码翻译成计算机能读懂的、用机器语言写就的程序。即使代码是正确的,如果编译器有问题,得出的程序还是可能出错。要避免这个问题,我们同样需要利用数学方法加固编译器这一环。
贯彻这种设计理念的一个例子是一个叫CompCert的C编译器,它由法国计算机科学家Xavier Leroy和他的小组编写。编译器的任务就是进行忠实的代码翻译,确保源代码和目标代码的行为一致。这一点至关重要,否则即使代码是正确的,也不能保证编译生成的程序不出问题。然而,现代的编译器在优化模式下,其实并不能确保忠实的编译。CompCert要解决的就是这个问题。在编写CompCert的时候,对于编译程序的每一步操作,都附带一个数学证明,确保代码的语义不变。因此,数学证明的正确性保证了CompCert编译器会完全忠实地将代码翻译成机器语言。
但即使机器语言是正确的,也还不能完全保证最后执行结果的正确性,因为程序总需要输入输出,而这些功能是由操作系统保证的。如果操作系统本身有错误,即使执行的程序本身是正确的,由于操作系统的问题,也不能保证我们看到的结果是正确的。如果想将数学证明的保证贯彻到底,还需要对操作系统下工夫。这就是seL4所做的事情。seL4是一个微内核,可以看成操作系统的核心。它的每一个功能都附带一个数学证明,在对硬件做一定的假设之后,数学证明的正确性可以保证它提供的功能都会产生我们预先设定的行为。只要硬件不出错,seL4就会正确运行。
当然,一个自然的问题是,如果硬件出错怎么办?硬件的错误可以分为逻辑性错误和物理性错误。前者例如Intel当年在芯片上除法的错误,现在主流硬件厂商早已吸取教训,用演绎验证的方法加固他们的硬件设计;后者例如宇宙射线令硬盘数据出错,这即使是多复杂的证明也避免不了,只能自求多福。尽管数学方法不能保证错误不存在,但至少将可以避免的问题全数避免,这本身就有着莫大的价值。
(或者可以利用宇宙射线……?)(图片来自xkcd)
本文改写自果壳网文章《》。
Git 每次提交代码,都要写 Commit message(提交说明),否则就不允许提交。
$ git commit -m "hello world"
上面代码的-m参数,就是用来指定 commit mesage 的。
如果一行不够,可以只执行git commit,就会跳出文本编辑器,让你写多行。
$ git commit
基本上,你写什么都行(,和)。
但是,一般来说,commit message 应该清晰明了,说明本次提交的目的。
目前,社区有多种 Commit message 的。本文介绍(见上图),这是目前使用最广的写法,比较合理和系统化,并且有配套的工具。
一、Commit message 的作用
格式化的Commit message,有几个好处。
(1)提供更多的历史信息,方便快速浏览。
比如,下面的命令显示上次发布后的变动,每个commit占据一行。你只看行首,就知道某次 commit 的目的。
$ git log &last tag& HEAD --pretty=format:%s
(2)可以过滤某些commit(比如文档改动),便于快速查找信息。
比如,下面的命令仅仅显示本次发布新增加的功能。
$ git log &last release& HEAD --grep feature
(3)可以直接从commit生成Change log。
Change Log 是发布新版本时,用来说明与上一个版本差异的文档,详见后文。
二、Commit message 的格式
每次提交,Commit message 都包括三个部分:Header,Body 和 Footer。
&type&(&scope&): &subject&
其中,Header 是必需的,Body 和 Footer 可以省略。
不管是哪一个部分,任何一行都不得超过72个字符(或100个字符)。这是为了避免自动换行影响美观。
2.1 Header
Header部分只有一行,包括三个字段:type(必需)、scope(可选)和subject(必需)。
type用于说明 commit 的类别,只允许使用下面7个标识。
feat:新功能(feature)
fix:修补bug
docs:文档(documentation)
style: 格式(不影响代码运行的变动)
refactor:重构(即不是新增功能,也不是修改bug的代码变动)
test:增加测试
chore:构建过程或辅助工具的变动
如果type为feat和fix,则该 commit 将肯定出现在 Change log 之中。其他情况(docs、chore、style、refactor、test)由你决定,要不要放入 Change log,建议是不要。
(2)scope
scope用于说明 commit 影响的范围,比如数据层、控制层、视图层等等,视项目不同而不同。
(3)subject
subject是 commit 目的的简短描述,不超过50个字符。
以动词开头,使用第一人称现在时,比如change,而不是changed或changes
第一个字母小写
结尾不加句号(.)
Body 部分是对本次 commit 的详细描述,可以分成多行。下面是一个范例。
More detailed explanatory text, if necessary.
Wrap it to
about 72 characters or so.
Further paragraphs come after blank lines.
- Bullet points are okay, too
- Use a hanging indent
有两个注意点。
(1)使用第一人称现在时,比如使用change而不是changed或changes。
(2)应该说明代码变动的动机,以及与以前行为的对比。
2.3 Footer
Footer 部分只用于两种情况。
(1)不兼容变动
如果当前代码与上一个版本不兼容,则 Footer 部分以BREAKING CHANGE开头,后面是对变动的描述、以及变动理由和迁移方法。
BREAKING CHANGE: isolate scope bindings definition has changed.
To migrate the code follow the example below:
myAttr: 'attribute',
myAttr: '@',
The removed `inject` wasn't generaly useful for directives so there should be no code using it.
(2)关闭 Issue
如果当前 commit 针对某个issue,那么可以在 Footer 部分关闭这个 issue 。
Closes #234
也可以一次关闭多个 issue 。
Closes #123, #245, #992
2.4 Revert
还有一种特殊情况,如果当前 commit 用于撤销以前的 commit,则必须以revert:开头,后面跟着被撤销 Commit 的 Header。
revert: feat(pencil): add 'graphiteWidth' option
This reverts commit 667ecc31bf415f02.
Body部分的格式是固定的,必须写成This reverts commit &hash&.,其中的hash是被撤销 commit 的 SHA 标识符。
如果当前 commit 与被撤销的 commit,在同一个发布(release)里面,那么它们都不会出现在 Change log 里面。如果两者在不同的发布,那么当前 commit,会出现在 Change log 的Reverts小标题下面。
三、Commitizen
是一个撰写合格 Commit message 的工具。
安装命令如下。
$ npm install -g commitizen
然后,在项目目录里,运行下面的命令,使其支持 Angular 的 Commit message 格式。
$ commitizen init cz-conventional-changelog --save --save-exact
以后,凡是用到git commit命令,一律改为使用git cz。这时,就会出现选项,用来生成符合格式的 Commit message。
四、validate-commit-msg
用于检查 Node 项目的 Commit message 是否符合格式。
它的安装是手动的。首先,拷贝下面这个,放入你的代码库。文件名可以取为validate-commit-msg.js。
接着,把这个脚本加入 Git 的 hook。下面是在package.json里面使用 ,把这个脚本加为commit-msg时运行。
"config": {
"ghooks": {
"commit-msg": "./validate-commit-msg.js"
然后,每次git commit的时候,这个脚本就会自动检查 Commit message 是否合格。如果不合格,就会报错。
$ git add -A
$ git commit -m "edit markdown"
INVALID COMMIT MSG: does not match "&type&(&scope&): &subject&" ! was: edit markdown
五、生成 Change log
如果你的所有 Commit 都符合 Angular 格式,那么发布新版本时, Change log 就可以用脚本自动生成(,,)。
生成的文档包括以下三个部分。
New features
Breaking changes.
每个部分都会罗列相关的 commit ,并且有指向这些 commit 的链接。当然,生成的文档允许手动修改,所以发布前,你还可以添加其他内容。
就是生成 Change log 的工具,运行下面的命令即可。
$ npm install -g conventional-changelog
$ cd my-project
$ conventional-changelog -p angular -i CHANGELOG.md -w
上面命令不会覆盖以前的 Change log,只会在CHANGELOG.md的头部加上自从上次发布以来的变动。
如果你想生成所有发布的 Change log,要改为运行下面的命令。
$ conventional-changelog -p angular -i CHANGELOG.md -w -r 0
为了方便使用,可以将其写入package.json的scripts字段。
"scripts": {
"changelog": "conventional-changelog -p angular -i CHANGELOG.md -w -r 0"
以后,直接运行下面的命令即可。
$ npm run changelog
版权声明:自由转载-非商用-非衍生-保持署名()
发表日期: 2016年1月 6日
更多内容:
购买文集:
社交媒体:,
Feed订阅:
本文作者:云无心
大家从超市买回鸡蛋,都会放到冰箱中。在美国,这甚至是“标准流程”——不这么做,鸡蛋可能会很快坏掉。但是,自然界的鸡下了蛋,总要攒够了一窝才开始孵——等到开始孵化,第一颗已经在自然环境下放置两三个星期了。那些常温下放了两三个星期的蛋依然能孵出小鸡,说明应该是没有坏的。
为什么天然的蛋和超市的蛋之间,存在这么大的差别呢?
原来,蛋壳的表面有一层胶状的薄膜(图中的Cuticle)。这层薄膜由85%的蛋白质以及少量脂肪、碳水化合物以及矿物质构成。而鸡蛋壳是有碳酸钙组成的,上面具有几千上万个细小的孔穴。鸡蛋从母鸡体内出来,这层薄膜均匀地覆盖在蛋壳表面,把那些空穴“封闭”了。这样,外面的细菌就难以进入鸡蛋内部;而在鸡蛋内部的水也不会散失,从而保持鸡蛋的初始状态。
虽然组成那层膜的蛋白质主要是不溶性的,如果对鸡蛋进行清洗,同样会对它造成破坏。那层膜被破坏之后,外面的细菌就会穿透蛋壳进入内部。另一方面,蛋壳内部是蛋白部分,蛋白部分虽然看起来很粘稠,其实含水量高达87%左右。那层膜被破坏之后,蛋壳就成了一个通透性很好的“架子”,水分很容易散失——蛋白失水导致浓度增加,蛋白胶的会急剧增加,打开之后甚至让人怀疑是“假鸡蛋”。
那层膜很容易被破坏。实验显示,蛋壳完好的鸡蛋在水中浸泡1到3分钟,就有微生物可以穿透蛋壳。这也是为什么不打破鸡蛋,就可以让盐进入鸡蛋成为咸蛋,让碱进入鸡蛋成为皮蛋的原因。
因为清洗可能破坏掉这层膜,从而导致鸡蛋易被外界的细菌感染,或者失水导致品质下降,以欧盟为代表的国家反对清洗鸡蛋。除了极少数例外,欧盟的A级鸡蛋不允许进行清洗。
但是,鸡蛋是一种很容易染上细菌的食物。尤其是走地鸡,自由活动的空间大了,鸡蛋染上污物的可能性也就增加。沙门氏菌影响鸡蛋安全的一大风险因素。它有两种途径进入鸡蛋:一是通过感染母鸡,把沙门氏菌“传进”鸡蛋或者“沾染”在蛋壳上;二是鸡窝中,沾染到鸡蛋表面。总而言之,鸡蛋一生下来,表面就可能带着沙门氏菌。沙门氏菌是一种致病细菌,在美国、澳大利亚等国家,每年有上万人次受到感染,鸡蛋是一大途径。
沙门氏菌不耐高温,但生存能力也很强。即使是蛋壳上的那层膜保持完好,它们依然能够穿透蛋壳进入鸡蛋内部——那层膜,只是降低它们穿透难度而已。所以,美国、澳大利盐和日本等国采取了先破后立的战术——对鸡蛋进行清洗,直接去除蛋壳上的沙门氏菌等微生物。虽然打开了细菌进入鸡蛋的通道,但是先把蛋壳上的细菌尤其是沙门氏菌杀灭,也就算是釜底抽薪了。虽然环境中可能有其他细菌进入,但危险性不如沙门氏菌大。再把鸡蛋放置在冰箱中,即使有细菌进入,在低温下的增殖也受到很大抑制。从安全的角度说,这也就比不清洗要好。
美国农业部有鸡蛋清洗的操作指南。比如,清洗鸡蛋时,需要水温高于90华氏度(约32 °C),而且比鸡蛋温度高20华氏度(约11 °C)。否则,低的水温可能让鸡蛋收缩,导致吸入水和微生物。但是,水温也不能高过鸡蛋温度40华氏度(22 °C),以避免蛋壳破裂。合理的清洗流程,加上适当的洗涤剂,能够让鸡蛋表面的细菌数降低到十万分之一,相当于牛奶巴氏灭菌的效果。
美国、日本和澳大利亚,是这种要求清洗的代表。未经清洗的鸡蛋,是不允许上市销售的,这跟欧洲正好相反。
这两种规定的出发点都是为了鸡蛋的食用安全。虽然要求针锋相对,但各自都有理论上的支持,都能自圆其说。至于“哪种规定更合理”,也就一直没有争论清楚。不过,2011年《食品保护杂志》上发表了一项研究,显示在研究所使用的清洗方式下,蛋壳外膜并没有受到明显破坏。这至少说明,对外膜造成多大的破坏,跟如何清洗有密切关系。
其实,两种方案的理念也可以通过增加流程来兼顾到——先清洗去除细菌,然后在蛋壳上喷涂一层食用油脂来弥补被破坏的膜。这种方案在技术上是完全可行的,只不过在美国,鸡蛋从鸡场到超市再到餐桌,流通时间比较短,也就没有必要去弥补那层被破坏的膜。
目前,我国的养鸡行业规模化程度还不是那么高,生产、收集、储存、运输、分销中如何通过规范来减少食品风险,也还没有被足够重视。随着市场需求的不断增加,和公众对食品安全的关注越来越多,鸡蛋产业必然会越来越规模化、规范化。在可以预见的将来,中国是采取欧洲的“不许洗”政策,还是美澳日的“必须洗”政策,或许又是一番争论。
题图由海洛创意提供
最近,有一篇文章正在疯传。
它是上个月,Maciej Ceglowski在澳大利亚的一次演讲,名为《网站的肥胖症危机》(,),反思了互联网开发的现状。
该文非常值得一读,排行榜高居榜首,得到了1000多人的推荐。
下面就是我的中文节译版。
===============================
网站的肥胖症危机(节译版)
作者:Maciej Ceglowski
译者:阮一峰
原文网址:
大多数网站的主要内容是文本,更准确地说,是简短的文本。
文本本身并不大,但是展示它们的网页,正变得越来越大。Twitter展示单条评论(140个字符)的页面,超过900KB。Medium的一篇文章大约400个词,页面大小是1.2MB。
如果这种趋势持续下去,2020年,网页的体积平均将超过5MB,比一本俄罗斯长篇小说还大。比如,陀思妥耶夫斯基的《罪与罚》,文本压缩后不到800KB。
<有一篇报道,介绍Google正在为大网页做标记。但是,这篇报道的网页,体积为18MB,外加一个3MB的视频。
2015年5月,Facebook引入了&#8221;Instant Articles&#8221;,帮助用户快速浏览新闻。但是,介绍这个功能的页面,体积为6.8MB,外加一个41MB的视频。你想了解这个功能的细节,唯一的方法就是去看这个视频。
网页真的有必要这么大吗?明明200KB就足够,为什么要做成2MB?
因为我们要往里面塞很多不需要的东西:广告、高清图片、视频、用户追踪系统、社交媒体的代码&#8230;&#8230;你不塞,公司就可能解雇你。
如今的时代,你跟雇主说,想做一张体积只有几百KB的网页,就好像跟SUV车主谈论省油的经济型轿车。
有人会说,这是免费内容的代价。但是,我想问,谁会从海量的互联网广告获利?广告主,还是消费者?真正获得暴利的是网络服务提供商和互联网广告公司,其他人都付出了巨大的成本。
我们都忘了健康的网页,应该是什么样子。
值得阅读的文本,配上结构良好的标签。
适度的图片和视觉设计。
少量的JavaScript,只在必需时使用
但是,2015年真实的网页,却是下面这样。
一大堆垃圾
顶部还有一层监控代码
宽带和光纤上网并不解决问题,实际上还鼓励了人们往网页上添加更多的东西。
为了平衡网页体积,工程师想出了很多方法:首屏快速渲染、压缩文件、异步加载、批量HTTP请求、管道发送等等&#8230;&#8230;
网站开发越来越依赖代码精简、压缩、缓存、服务器配置这些中间步骤,这使得找出错误越来越困难,成本越来越高。
复杂性让聪明人上瘾。
即使我们知道复杂不是好事,但难以抵抗。复杂的东西总是显得很酷,让人情不自禁想继续干下去。
大多数网站都过度复杂了。
我们做的每件事,都使得创造网站或编辑网页变得困难。把一篇文章放上网,正在变得需要一个专家团队才能完成。
新手越来越难通过源码学习。我们抽走了人们学习互联网的梯子。
其实只需要两步,就可以大大缩小网页体积,提高性能。
第一步,确保最重要的内容,首先下载和渲染;
第二步,就此结束。
你不需要那些多余的垃圾,对最简主义保持信心就行。
让我们保持互联网是一个超链接构成的媒体,不要把它变成另一种东西。
版权声明:自由转载-非商用-非衍生-保持署名()
发表日期: 2016年1月 3日
更多内容:
购买文集:
社交媒体:,
Feed订阅:
Advertising

我要回帖

更多关于 国外域名空间 的文章

 

随机推荐