T | 如何将三万行代码从 Flow 移植到 TypeScript?
Posted WorldLanguage
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了T | 如何将三万行代码从 Flow 移植到 TypeScript?相关的知识,希望对你有一定的参考价值。
如何将三万行代码从 Flow 移植到 TypeScript?
以下为译文:
最近我们将MemSQL Studio的3万行javascript代码从Flow移植到了TypeScript。在本文中,我将介绍我们移植代码库的原因以及移植的全过程。
事先声明,我写这篇文章的目的不在于谴责Flow或Flow的使用。我非常欣赏Flow这个项目,我认为在JavaScript社区中Flow和TypeScript这两种类型检查器都有足够的发展空间。但是,每个团队都需要仔细研究并选择最适合自己的。因此我真诚地希望这篇文章对你的选择能有所帮助。
背景
首先介绍一下背景故事。在MemSQL,我们都喜欢静态强类型的JavaScript代码,这是为了避免动态弱类型的常见问题。例如:
动态弱类型问题中不同部分的代码对于隐式类型契约的假设不一致,会引发运行时的类型错误;
而且动态弱类型在测试小问题上花费的时间太多,比如参数类型检查(运行时类型检查也会增大打包文件的尺寸);
此外动态弱类型还缺乏编辑器/ IDE集成,因为在没有静态类型的情况下,很难实现跳转定义、自动重构以及其他功能;
静态强类型还具有动态弱类型问题所缺失的围绕数据写代码的能力,这意味着我们可以先设计数据类型,然后我们的代码就会“自然成型”。
这些还只是静态类型部分的优点。
2016年初,我们开始在一个内部的JavaScript项目上使用tcomb,以确保运行时的类型安全(声明:我并没有参与那个项目)。虽然有时运行时的类型检查很有用,但它与静态类型毫不沾边。考虑到这一点,2016年的时候我们决定在另一个项目中使用Flow。当时,Flow是一个很好的选择,因为:
Flow由Facebook支持,在日益壮大的React社区中收到了相当多的好评(React也是用Flow开发的);
我们没有必要尝试一个全新的JavaScript开发生态系统,抛弃Babel转向tsc(TypeScript编译器)的风险会很大,还会失去切换到Flow或其他类型检查器的灵活性(显然后来情况发生了变化);
我们也没有必要在整个代码库上采用类型(因为我们想先尝试一下静态类型的JavaScript),我们只想在部分文件上采用类型(不过请注意,现在的Flow和TypeScript都允许开发者这么做);
当时的TypeScript缺少Flow支持的一些基本功能,例如查找类型、泛型参数默认值等。
当2017年年末开始开发MemSQL Studio时,我们准备在整个应用程序中实现完整的类型覆盖(整个应用程序都是用JavaScript编写的,前端和后端都在浏览器中运行)。因为以前成功使用的经验,所以我们决定此次也使用Flow。
然而,最新发布的Babel 7已经开始支持TypeScript了,这引起了我的注意。这个发布意味着采用TypeScript不再需要引入整个TypeScript生态系统,我们可以继续通过Babel来生成JavaScript。更重要的是,这意味着实际上我们可以将TypeScript作为类型检查器,而不是作为一种“语言”。
就个人而言,我认为将类型检查与JavaScript的生成分离是在JavaScript中实现静态(强)类型的更优雅的方式,因为:
将生成ES5和类型检查从思想上进行某种程度的分离是一个好主意。如此一来可以减少类型检查锁定的范围,并加快开发速度(即使类型检查因某些原因而变慢,你的代码生成也不会受到影响)。
Babel拥有一些非常优秀的插件和了不起的功能,这些都是TypeScript的生成器所没有的。例如,Babel允许开发者指定想要支持的浏览器,它将自动生成在这些浏览器上有效的代码。不过这实现起来非常复杂,因此应当让Babel做这一切,而不是让社区在两个不同项目上重复这种努力。
我喜欢JavaScript这种编程语言(除了它缺少静态类型),我不知道TypeScript会最终存活多久,但我相信ECMAScript会长期存在。出于这个原因,我更喜欢用JavaScript思考和写代码。
注意,我一直在说“使用Flow”或“使用TypeScript”,是因为我总是把它们当成工具,而非编程语言。
当然,这种方法也有一些缺点:
理论上,TypeScript编译器可以根据类型执行优化,但如果将生成与类型检查分离就失去这个优势了;
如果需要依赖很多工具和开发,那么项目配置会变得稍复杂。不过我认为这个不足为虑,因为在我们的项目中Babel + Flow从来都没出现过配置的问题。
TypeScript 能替代 Flow 方案吗?
我注意到网上和本地JavaScript社区对TypeScript的兴趣越来越浓厚。因此,当发现Babel 7支持TypeScript时,我就开始调查代替Flow的可能性。最重要的是,在使用Flow的时候我们遇到了很多挫折:
编辑器/ IDE集成的质量很低(与TypeScript相比)。Nuclide(Facebook自己的IDE,拥有最好的Flow集成)已经不再维护,所以没什么用了。
社区很小。各种代码库数量较少,且总体的类型定义质量较低。
Facebook和社区的Flow团队之间缺乏公共的规划,且互动很少。
内存消耗很高且内存泄漏频繁,我们团队的工程师偶尔会经历Flow占用10GB的RAM的现象。
当然,我们还必须研究TypeScript是否合适我们。调查的过程非常复杂,但通过全面地阅读文档,我们发现Flow的每个功能在TypeScript中都有相应的支持。之后,我又研究了TypeScript的项目规划,发现上面提到的功能都有非常满意的支持(例如,我们在Flow中使用的一个部分类型参数推断的功能)。
将三万多行代码从 Flow 移植到 TypeScript
实际上,将所有代码从Flow移植到TypeScript的第一步是将Babel从6升级到7。这项工作看似简单,但由于我们决定将Webpack 3升级到4,所以最后花了两天的时间。由于我们的代码中有一些遗留的依赖,所以此次的难度要比绝大多数JavaScript项目都高。
完成这一步后,我们就可以用新的TypeScript预设替换Babel的Flow预设,然后在用Flow编写的完整源代码上运行TypeScript编译器——结果发生了8245个语法错误(只有在没有语法错误的情况下tsc的命令行工具才会报告项目中的真正的错误)。
我们被这个数字吓到了,但是很快我们就发现其中大部分是由于TypeScript不支持.js文件导致的。经过一番调查,我发现TypeScript文件必须以“.ts”或“.tsx”结尾(包含JSX的情况)。我不想在创建新文件的时候犹豫是应该使用“.ts”还是“.tsx”的扩展名,因为这是一种糟糕的开发体验。所以,我决定将所有文件都重命名为“.tsx”(理想情况下,应当像Flow一样所有的文件都具有“.js”扩展名,但我也可以接受使用“.ts”)。
经过这次修改后,我们有大约4000个语法错误。其中大多数都与导入类型有关,我们可以TypeScript的“import”替换,也可以使用Flow({||} vs {})中的密封对象表示法替换。在使用了几个正则表达式替换之后,我们的语法错误数量降到了414个。剩下的部分只能手动修复了:
部分泛型类型参数推断中使用的既存型别必须替换为显式命名的各种类型的参数,或通过unknown类型告诉TypeScript我们并不关心某些类型的参数;
$Keys类型和其他Flow高级类型在TypeScript中具有不同的语法,例如,$Shape <>与TypeScript中的Partial<>对应)。
修复了所有语法错误之后,tsc(TypeScript编译器)终于告诉我们,代码库中大约有1300个真正的类型错误。这时我们不得不坐下商量是否还应该继续,毕竟,如果要花费数周的开发时间,此次移植就得不偿失了。但是,我们发现只需花费不到1周的时间就可以完成移植,所以我们决定继续。
注意,在转换期间,我们必须停止代码库的开发工作。当然,在移植期间依然可以继续开发新代码,但是你必须在可能有数百种之多的类型错误上进行工作,这不是一件易事。
都有哪些类型错误?
在很多方面,TypeScript和Flow都做出了不同的假设,在实践中这意味着JavaScript代码的行为会有所不同。在某些方面Flow更严格,而TypeScript在其他方面又更为严格。深入比较两种类型检查会花费大量时间,所以在本文中我只举几个例子。
注意:本文中所有的TypeScript练习环境(http://www.typescriptlang.org/play/)的链接都假设所有的“严格”设置都被打开了,但遗憾的是在分享TypeScript练习环境时,这些设置都不会保存到URL中。因此,可以点击上面的连接打开TypeScript练习环境之后再手动设置。
invariant.js
我们的源代码中有一个很常用的函数invariant,这个文档(https://github.com/zertosh/invariant#invariantcondition-message)很好地解释了它的功能:
var invariant = require('invariant');
invariant(someTruthyVal, 'This will not throw');
// No errors
invariant(someFalseyVal, 'This will throw an error with this message');
// Error raised: Invariant Violation: This will throw an error with this message
这是个非常简单的函数,它能在某些条件下抛出异常。下面让我们来看看在Flow中它的实现与使用:
type Maybe<T> = T | void;
function invariant(condition: boolean, message: string) {
if (!condition) {
throw new Error(message);
}
}
function f(x: Maybe<number>, c: number) {
if (c > 0) {
invariant(x !== undefined, "When c is positive, x should never be undefined");
(x + 1); // works because x has been refined to "number"
}
}
下面,我们通过TypeScript运行完全相同的代码片段。正如在链接中看到的那样,TypeScript出错了,因为它不清楚最后一行是否可以确保“x”不会被定义为undefined。这是一个众所皆知的TypeScript的问题——它无法在函数中进行这类的推理。但是,由于这样的代码在我们代码库中很常见,所以我们就被迫手动替换每一个invariant的实例(有150多个):
type Maybe<T> = T | void;
function f(x: Maybe<number>, c: number) {
if (c > 0) {
if (x === undefined) {
throw new Error("When c is positive, x should never be undefined");
}
(x + 1); // works because x has been refined to "number"
}
}
虽然这不如invariant那么好,但也不算大问题。
$ ExpectError vs @ ts-ignore
Flow有一个非常有趣的功能,类似于@ ts-ignore,不过不同的是如果下一行不是错误,那么它就会出错。在编写“类型测试”时,这个功能很有用。类型测试可以确保类型检查(无论是TypeScript还是Flow)按照我们的期望找到某些类型错误。
不幸的是,TypeScript没有这个功能,这意味着我们的类型测试失去了部分价值——这也是我期待TypeScript能够实现的功能。
一般的类型错误和类型推断
通常,TypeScript会比Flow更清晰,如下例所示:
type Leaf = {
host: string;
port: number;
type: "LEAF";
};
type Aggregator = {
host: string;
port: number;
type: "AGGREGATOR";
}
type MemsqlNode = Leaf | Aggregator;
function f(leaves: Array<Leaf>, aggregators: Array<Aggregator>): Array<MemsqlNode> {
// The next line errors because you cannot concat aggregators to leaves.
return leaves.concat(aggregators);
}
Flow推断leaves.concat(aggregators) 的类型为Array<Leaf | Aggregator> ,然后将其转换为Array<MemsqlNode>。我认为这是一个很好的例子,说明有的地方Flow很聪明,而TypeScript可能需要一点帮助(在这种情况下,我们可以用类型断言来帮助TypeScript,但是类型断言的使用很危险,请小心谨慎)。
尽管没有正式的证据,但是我还是想说我认为在类型推断方面Flow比TypeScript更优越。我非常希望TypeScript能够向Flow看齐, 因为TypeScript正处于非常积极的开发中,并且最近TypeScript有了许多改进。而纵观我们的源代码,我们必须通过解释或类型断言给予TypeScript一些帮助(还是尽可能地避免使用类型断言)。让我们再来看一个例子(我们有200多个这种类型错误的实例):
type Player = {
name: string;
age: number;
position: "STRIKER" | "GOALKEEPER",
};
type F = () => Promise<Array<Player>>;
const f1: F = () => {
return Promise.all([
{
name: "David Gomes",
age: 23,
position: "GOALKEEPER",
}, {
name: "Cristiano Ronaldo",
age: 33,
position: "STRIKER",
}
]);
};
在TypeScript你不能这样写,因为它不允许你将{ name: "David Gomes", age: 23, type: "GOALKEEPER" }当作Player类型的对象(打开练习环境可以看到确切的错误)。这是另一个我觉得TypeScript“不够聪明”的地方——至少与Flow相比不够聪明。
为了修正这个错误,开发者有几个选择:
断言"STRIKER"为"STRIKER",这样TypeScript就可以理解该字符串是个有效的枚举类型"STRIKER" | "GOALKEEPER";
断言整个对象为“Player”(as Player);
或者(我认为的最佳解决方案)无需任何类型的断言,只需写Promise.all<Player>(...)。
另一个TypeScript的例子如下所示,这段代码再次表明Flow具有更好的类型推断:
type Connection = { id: number };
declare function getConnection(): Connection;
function resolveConnection() {
return new Promise(resolve => {
return resolve(getConnection());
})
}
resolveConnection().then(conn => {
// TypeScript errors in the next line because it does not understand
// that conn is of type Connection. We have to manually annotate
// resolveConnection as Promise<Connection>.
(conn.id);
});
一个很小但非常有趣的例子是Flow判断Array<T>.pop()的类型为T,而TypeScript则认为它属于T | void。这是我喜欢TypeScript的一个地方,因为它会强制你仔细检查该项是否存在(如果数组为空,则Array.pop返回undefined)。
TypeScript对于第三方依赖的定义
当然,在编写任何JavaScript应用程序时都有可能会有一些依赖。这些依赖都需要类型定义,否则开发者就失去了静态类型分析的大部分威力(如本文开头所述)。
从npm导入的库可以附带Flow类型定义或TypeScript类型定义,也可以两者兼有或两者都没有。许多小型库不带有任何方式的类型,所以必须自己编写类型定义,或从社区中找。Flow和TypeScript社区都有一个标准的JavaScript包的第三方类型定义代码仓库:flow-typed和DefinitelyTyped。
我不得不说使用DefinitelyTyped的体验更好。在使用flow-typed的时候,我们必须通过它的命令行工具将各种依赖的类型定义引入到项目中。DefinitelyTyped找到了一个很好的方法与npm的命令行集成,即它的软件包均以@types/package-name的方式命名。这一点非常了不起,有了它我们就可以很容易地为依赖引入类型定义了(jest、react、lodash、react-redux等等)。
除此之外,我花了大量时间向DefinitelyTyped贡献代码(当将代码从Flow移植到TypeScript时,不要指望类型定义是等价的)。我已经发送了几个拉取请求,所有工作都易如反掌。开发者只需要克隆、编辑类型定义、添加测试,然后发送拉取请求,DefinitelyTyped GitHub会将曾向这个类型定义贡献过代码的人标记为审核者。如果7日之内没有人审核代码,那么DefinitelyTyped的维护者会审核PR。在合并到master分支后,新版本的依赖包将会发送到npm。例如,当我第一次更新@types/redux-form包时,在合并到master分支后版本7.4.14自动被推送到了npm。我们可以非常容易地更新package.json文件,就可以获取新的类型定义。如果等不到PR被接受,那么也可以随时覆盖项目中使用的类型定义。
总的来说,DefinitelyTyped中类型定义的质量更好,这要归功于TypeScript背后的社区更大、更繁荣。事实上,在将我们的项目从Flow移植到TypeScript之后,我们的类型覆盖率从88%提高到了96%,主要是由于更好的第三方依赖类型定义,“any”类型的依赖减少了。
Linting与测试
在移植过程中,我们发现使用TypeScript的eslint比较复杂,所以我们就选择了tslint,从eslint转移到了tslint。
此外,我们还使用ts-jest来运行TypeScript的测试。有些测试是有类型的,而有些是无类型的(如果给测试用例添加类型的工作量太大,我们就将它们保存成.js文件)。
修复了所有类型错误后,情况怎样了?
经过历时一周的修复工作后,我们遇到了最后一个类型错误,我们决定利用@ts-ignore将其暂且搁置。
在解决了一些代码审查注释并修复了一些错误之后(不幸的是,我们不得不修改少量运行时来修复TypeScript无法理解的逻辑),在这个PR被合并后,我们就开始使用TypeScript了。(还有,我们在后续的PR中修复了最后一个@ts-ignore)。
除了编辑器集成之外,TypeScript的使用体验与Flow非常相似。Flow服务器的性能稍微快一点,但这并不是一个大问题,因为在为你正在查看的文件提供内联错误时它们一样快。唯一的性能差异在于TypeScript需要更长的时间(约0.5到1秒)才能告诉你在保存某个文件后,项目中是否有新的错误。服务器启动时间大约相同(约2分钟),但这并不重要。到目前为止,我们还没遇到过内存消耗的问题,tsc使用的内存一直稳定在大约600Mb。
可能看起来Flow的类型推断比TypeScript更好,但是两个原因可以解释为什么这不是什么大问题:
我们将代码库从Flow移植到了TypeScript,这意味着我们在其中发现了Flow可以表达但TypeScript却不能表达的地方。如果这次移植是从TypeScript到Flow的,那么我们可能就会发现TypeScript的推断/表达比Flow更好。
类型推断很重要,它有助于保持我们的代码更简洁。但是强大的社区、类型定义的可用性等更为重要,因为弱类型推断只需要加强下类型检查就可以解决。
代码统计
$ npm run type-coverage # https://github.com/plantain-00/type-coverage
43330 / 45047 96.19%
$ cloc # ignoring tests and dependencies
--------------------------------------------------------------------------------
Language files blank comment code
--------------------------------------------------------------------------------
TypeScript 330 5179 1405 31463
下一步计划?
虽然移植完成了,但是代码中的静态类型分析还没有完成。
MemSQL还有其他项目也打算弃用Flow、转而投入TypeScript的怀抱(有些JavaScript项目可能一开始就使用TypeScript),所以我们希望使我们的TypeScript配置更加严格。
目前我们已经打开了“strictNullChecks”,但“noImplicitAny”仍处于禁用状态,这也需要后续解决。
此外,我们还打算删除代码中的一些危险的类型断言。
1.08 亿条投注信息遭泄露,Elasticsearch 是祸因?!
据外媒报道,美国一家网上赌场集团已经泄露了超过1.08亿笔投注的信息,其中包括有关客户个人信息,存款和取款等详细信息。
对此,安全研究员表示,这些数据是从 Elasticsearch 服务器泄露出来的,并且不需要密码就可获得这些信息。
Elasticsearch 是一个搜索引擎,该引擎多是应用在企业内部用来处理敏感信息,一般这些信息不会泄露到网上。
安全研究员 Justin Paine 在上周就发现这个没有安全保护和身份验证的 Elasticsearch 实例,并确认了这是在线投注有关的信息。尽管服务器只有一个,但该 Elasticsearch 处理的信息大且来自多个网站,可以判断信息出处是一家运营多个博彩门户的大公司。
目前该服务器已下线。目前尚不清楚该服务器在网上曝光了多久、有多少用户受到了影响、是否有安全研究员以外的人看到了这些信息。
近年来企业 Elasticsearch 服务器数据泄露的新闻不在少数,原因多是企业未对该服务器进行加密。为保证数据安全,企业应对 Elasticsearch 进行加密,或是考虑升级到最新版、确认与该服务器配套工具的安全性。
CTO职场解惑指南系列(二)春节篇
很多理财、电商平台要在春节要搞大促,担心黑客冒充用户去抢样品抢补贴,占用大量CPU服务器资源,导致真的用户进不去网站买不了,白白将真金白银给到了黄牛。短期好看的数据背后,自己的产品并没有卖给真正的用户,业务不好是谁的错?CTO还是运营负责人?
或者用户在网站购物时,黑客在页面注入病毒,把不是消费者想买的东西加入购物车,趁其不注意一起下单,帮商家刷单;又或者用户的个人信息被盗,消费者投诉体验差,媒体爆负面,CEO出面道歉,CMO狂处理公关危机,售后忙着进行赔付,这是CTO失责,还是黑客防不胜防?
最可怕的是网站漏洞被黑客入侵,企业丢失数据。丢失数据的代价有多高?据BM Security和Ponemon Institute发布的《2018数据泄露损失研究》评估显示,大型数据泄露代价高昂,百万条记录可致损失4000万美元,5000万条记录可致损失3.5亿美元。企业挣一个亿难得要命,而数据泄露问题损失一个亿真的是太容易了。
数据对企业的价值已毋庸置疑,特别对于在线交易平台,在线教育平台等,用户行为数据、交易数据等是让企业生存的基础和二次收益的金矿。企业获取核心数据千辛万苦,CTO只有攻克网络安全问题,才能守住这份“财”。
全球都在攻克网络安全 让企业守住自己的数据金矿
近日,网络安全投资研究公司Strategic Cyber Ventures发布报告称,2018年全球网络安全公司获得了53亿美元的风险投资,创历史最高纪录,与2017年的44亿美元相比增长20%。
以BAT等企业为首的科技巨头也早已在网络安全产品上布局。阿里云安全事业部总经理肖力近日接受媒体采访时说,“未来100%的企业都会上云,传统的外挂式系统安全在云时代会变成内置安全,这样才能保障不管是企业用户还是个人用户的数据甚至人身安全。目前阿里云平台和云产品都已经实现了默认安全,我们希望在保障平台安全的同时与用户一起保障应用和业务安全”。
由此可见,巨头们的入局,会让企业有利器傍身,守住自己的数据金矿。
云上比云下更安全 安全AI比资深专家更厉害
正如前面提到的,未来都是云时代,你的企业还在用传统网络安全产品?要知道云安全拥有更快的“看见”和“响应”能力。对中小企业或自身安全成熟度稍弱的企业来说,云的快速响应、止血、修复的能力将极大提升其安全水位。
在传统IT环境下,众多的企业需要逐个修复其安全漏洞,无法做到小时级应急响应全量修复,更难以形成云上整体的安全防御协同网络。但现在云上可以解决这个困境,例如阿里云目前可以做到85分钟应急响应,2小时完成从发现风险,到防御策略发布全过程。通过“高危漏洞预警 - 应急响应和分析 –最佳安全方案制定 – 智能化修复 - 持续分析跟踪风险”五个环节,保证应急响应的高效运转,确保企业运营和数据的安全。
同时选择云安全的好处是,云计算的场景决定了其安全防御能力会永不停止的进化。像在文中开篇提及的因为网络安全造成的用户体验不佳,业务不稳定会得到规避外,特别是在运营中可能出现的新的安全问题也能得到有效控制。
此外,AI也用在了网络安全上。阿里云将深度学习技术应用到WAF上,通过使用深度学习算法对样本进行训练,产出模型来检测和识别流量中的风险,不仅提高了防护准确率,而且能应对一些0-day的攻击。安全AI比资深专家更厉害。基于深度学习的阿里云WAF比专家经验规则的异常请求检出率提升30%以上,运营成本降低50%。同时,通过对全网数据的态势分析及强大的威胁情报能力,可以做到提前预警,自适应的调整防护策略,并且可以做到在云端瞬时下发,以应对不断变化的安全风险,让普通企业也能享受到专家级的安全服务。
选政府用过的、中国40%的网站都在用的同款才安全
政府对网络安全的要求绝对高于任何企业。G20杭州峰会期间,阿里云为300多个国家级和浙江省政务和民生政务相关业务提供安全保障服务,最终实现了峰会期间0业务中断、0安全事件、0舆情事件的信息安全状态,有效的保障了G20峰会的平稳进行。
其中阿里云WAF已广泛应用在金融、新零售、电商、政府、能源、航空、传媒、教育、医疗、互联网服务等多个领域,服务亚洲航空、12306、新华社、飞利浦等多个企业客户。今天阿里云正在保护中国40%的网站,每天帮助企业抵御超过36亿次攻击。
人类再次彻底败给 AI!
今日凌晨,DeepMind 与暴雪进行了联合直播,在直播中公布了 Google 最新 AI 程序 AlphaStar 与《星际争霸 2》职业选手此前的比赛结果,AlphaStar 以总成绩 10-1 战胜了两位来自欧洲的职业选手 TLO 和 MANA。这或许是自 2017 年 AlphaGo 在围棋上战胜人类后,再次让人类刷新 AI 认知的人机大战。
此次与人类对战的 AI 是 AlphaGo 兄弟:AlphaStar。开赛前几个小时,DeepMind CEO 在 Twitter 上公布了这一名字。而在开赛前一天,DeepMind 宣布 AI 将与人类在《星际争霸 II》中公开对战的消息,并表示在《星际争霸 II》游戏的疯狂难度模式下,其 AI 的胜率为 50%。
需要说明的是,正如之前打 Dota 的 OpenAI 受到的降低反应速度一样,DeepMind 此次将 AlphaStar 的反应速度“限制”到 APM 280,大致相当于星际中等职业玩家的水平。就 AlphaStar 的表现来看,虽然最终在现场 PK 时输了最后一局比赛,但整体上还是完虐人类。
比赛开始,与 AlphaStar 对战的第一个职业选手是 TLO,其世界排名为第 68 位。此次人机大战是五局三胜制,TLO 虽然不是顶级星际选手,但让人足够惊叹的是,AlphaStar 最终在约五十分钟内结束战斗,以 3:0 的成绩获胜,人类职业选手似乎毫无还手之力。
先来简单回顾下人机大战第一场:AlphaStar 第一局一开始就表现的很有全局观,在 PvP 两矿打一矿的情况下,TLO 很快被击败;第二局一度打得很焦灼,但 AlphaStar 依然以 2:0 再赢一局;十分钟后,TLO 溃败给 AlphaStar,有网友说选手太初级,批评 TLO 作为职业选手在瞎打,没有尊重熬夜看人机大赛的观众。不过,也有人说 AI 很可怕。
如果说第一回合 AlphaStar 战胜的是“初级”选手,那随后与其对战的职业选手是世界排名第 19 位的 MaNa ,其职业水平至少是中上游。
上图显示的是 AlphaStar 的决策过程,具体比赛过程就不说了,结果是 MaNa 在大约 45 分钟内连输五局被 AlphaStar 斩落马下。
前一个职业选手 TLO 在 MaNa 输掉比赛后称,“相信我, 和一个像 AlphaStar 这样的对手比赛是非常困难的, 它的打法和人类完全不同,是你以前从未有过的经验。 AlphaStar 给人留下了极深的印象, 这在游戏 AI 中也是前所未有的。”
AlphaStar 遇神杀神,干脆利落。这次应该没人责怪 MaNa 打得不好,他显然拼尽了全力,而网友发出的慨叹是希望终结者不会来那么快。
但最后一局让网友们为人类命运紧张的情绪稍稍放松,与 MaNa 现场对战的 AlphaStar 一着不慎,让 MaNa 扳回一局,但最终以 1:5 输掉比赛。
AlphaStar 的来历
早在 2016 年,谷歌 DeepMind 的 AI 研究人员已经开始关注暴雪娱乐的《星际争霸 II》。通过游戏来确定 AI 是否可以胜过人类相对简单的复杂任务是 DeepMind 的研究人员长期以来都在尝试的项目。同年 11 月,DeepMind 曾在暴雪嘉年华上宣布与暴雪达成合作协议,将在《星际争霸》系列游戏中进行合作,将在游戏平台中引入机器学习方法,进行人工智能研究。
2017 年 8 月,Deepmind 与暴雪共同发布了合作计划的首个成果:基于《星际争霸 II》的人工智能研究环境 SC2LE,它允许研究者在 Linux 系统中接入游戏 API,开放了 65000 场比赛的数据缓存用于训练 AI,并将添加 50 万场游戏数据到缓存中。
2018 年,同样在暴雪嘉年华上,他们分享了人工智能模型在《星际争霸》游戏中的突破。人工智能模型能执行基本的宏观战略,还能防御像 Tower Rush 这样的激进战术。而刚刚结束的“星际争霸”赛则是是一项“重大挑战”,因为这要求 AI 智能体“实现和平衡一些子目标”,以便最终击败人类选手。
例如,虽然游戏的目标是击败对手,但玩家还必须考虑平衡多个子目标,比如收集资源(水晶和气矿)或建筑。此外,一场比赛的时间通常为几分钟到一小时不等,这意味着游戏中提早执行的行动会在很长时间不见成效。最后,由于玩家智能看到部分地图,这意味着智能体必须结合记忆与规划才能成功。
AlphaStar 背后的 AI 技术
在围棋任务中,对手间可以看到整个棋盘,但《星际争霸 II》与此不同,我们通常无法看到整个地图,需要派小兵出去侦查。而且游戏是不间断进行的,整个游戏甚至会有超过 5000 步的操作。
DeepMind 科学家 Oriol Vinyals 曾在去年的一场以《AI 对战星际争霸胜算几何?》的演讲中提到,这个游戏基本上就是建造一些建筑物以及单位,在同一个地图里与不同的组织相互竞争。在这个游戏中,哪怕只是建造建筑物,也需要做出许多决策。除此之外,他们还要不断收集和利用资源、建造不同的建筑物、不断扩张,因此整个游戏非常具有挑战性。
他们在这个游戏中使用的方法依旧是强化学习。他们要让 AI 模仿人类玩这个游戏的方式,但即使是模仿人类点击鼠标和敲击键盘的行为也非常困难,需要收集整个《星际争霸》玩家的行为进行分析。为此还引入了一个游戏引擎。
DeepMind 还为游戏过程设计了不同的指令和模式,这使得整个玩《星际争霸》的过程变得独一无二。如今,我们看到的 AlphaStar 正是上述想法的结晶。
现在的 AlphaStar 还是刚出生的婴幼儿,不过以其极快的进化速度,你也能想到日后的进化版会是多么无可匹敌。到现在,或许还有很多人期待 2 月 15 日 AlphaStar 与世界排名第一的 Serral 进行终极对决,但最终的结果可想而知。正如柯洁输给 AlphaGo 时面临的绝望一样,人类可能不愿意面对这样的现实:在更多复杂的场景中,人类可能正以难以想象的速度输给 AI。
滴滴顺风车春运暂不上线;锤子员工被强制离职;苹果聘请三星高管 | 极客头条
快讯速知
滴滴顺风车春运暂不上线
冬季达沃斯开幕,马云化身思想者,刘强东缺席
锤子员工自述:被变相强制离职,罗永浩一直活在梦里
必应搜索恢复访问,禁用、营销众说纷纭
第四批游戏版号公布,移动游戏居多,游戏大厂均在
摩拜改名美团单车,前董事长李斌发声:这是人家的权利
苹果也开始裁员了:自动驾驶项目“泰坦计划”裁员200逾人
苹果聘请前三星电池高管,有自主开发电池的打算
谷歌拟改进Chrome安全与隐私,或影响广告拦截插件
微软CEO:面部识别技术需要管制,以免竞争导致丧失底线
TensorFlow 1.13.0-rc0 正式发布,谷歌开源的机器学习框架
React UI 库:React Suite 3.7.0 版本更新
国内要闻
滴滴顺风车春运暂不上线
据中新社报道,就舆论关注的今年春运滴滴顺风车能否恢复上线,滴滴回应,今年春运,滴滴顺风车无法继续提供服务,仍在全力整改中。
接近滴滴的人士表示,目前各方对顺风车的产品设计、安全标准以及责任界定尚未达成共识,相应方案还在讨论中。
2018年,在多方压力下,滴滴宣布无限期下线顺风车业务,进行整改。滴滴数据显示,2018年春运,滴滴顺风车运送乘客3067万人次,等于17万架波音737飞机的运送量。
冬季达沃斯开幕,马云化身思想者,刘强东缺席
1月22日-25日,2019年冬季达沃斯论坛上演,世界上最聪明的脑袋再次聚集在瑞士小镇。
今年论坛的主题被定为“全球化4.0:打造第四次工业革命时代的全球架构”,希望为更加包容的全球化寻找崭新的路径。
众多来自中国的企业家中,马云算是最令人期待的一位,近些年的达沃斯论坛他几乎从未缺席,且往往表现地更像一个思想者、而不是企业家,时常言辞尖锐、金句频出。
国内另一位电商大佬刘强东此次缺席了达沃斯,虽然京东再次赞助了世界经济论坛。
去年,京东第一次成为世界经济论坛的战略合作伙伴,刘强东高调亮相,与各界名流会谈。
但今年取而代之的是京东商城CEO徐雷、京东数科CEO陈生强、京东物流CEO王振辉等核心高管。
锤子员工自述:被变相强制离职,罗永浩一直活在梦里
近日,锤子科技员工转签字节跳动合同的消息,对此,字节跳动方面回应称,只是收购了锤子科技部分专利使用权。
某锤子员工自述称,已转签合同,工作岗位和内容仍未知,期间锤子科技和字节跳动都没有管理层出面安抚这批员工;
不签面临的结果很可能是公司没钱,工资都无法保障。他认为,罗永浩“是个活在梦里的人,太理想主义”, 对财务和盈利关注甚少。
必应搜索恢复访问,禁用、营销众说纷纭
近日,作为全球领先的搜索引擎之一,微软的必应(Bing)搜索在国内出现大规模无法访问的问题,引发广泛关注,遭“禁用”的猜测一时四起。
昨晚,微软必应搜索中国版(cn.bing.com)已部分恢复访问,预计在今日全面恢复,目前已可正常访问。
此前微软在给媒体的声明中表示,“我们已知晓在中国的一些客户无法访问必应网站,正在展开调查。”此次事故持续了一天多时间,事故原因暂未公布。
但鉴于其间耗时过长,亦不乏评论猜测此番乃是又一场以营销为目的的“人造”事故。
第四批游戏版号公布,移动游戏居多,游戏大厂均在
此前,游戏版号将公布的消息使得大流量游戏公司的股票飞涨。如今,第四批游戏版号终于公布了。资料显示,这一批游戏版号总数为95个,审批时间为1月13日。
从名单中可以看到,大多数通过审批的游戏类型均为移动游戏。此外,包括腾讯、网易、完美世界等知名大厂均在名单之中。
令人感到惊讶的是,在此前合法的三批游戏版号中,腾讯、网易均没有获得通过审批的游戏版号,然而在第四批版号中,腾讯获得了两款游戏的获批,分别是《折扇》与《榫接卯和》;而网易获得审批的游戏名为《战春秋》。
摩拜改名美团单车,前董事长李斌发声:这是人家的权利
近日,有国内媒体拿到了摩拜前董事长李斌的一份录音采访,在录音中,李斌回应对于摩拜单车被改名一事。
首先对于摩拜单车李斌表示:“(摩拜)这已经不是我的主战场,在我这里早就翻篇了。”
至于改名一事李斌明显并不赞同但也无可奈何,他说:“至于它改不改名,当然我不认为这是个好的idea,但这是人家的权利。”
国际要闻
苹果也开始裁员了:自动驾驶项目“泰坦计划”裁员200逾人
据外媒报道,知情人士称,苹果本周从其隐秘的自动驾驶汽车项目“泰坦计划”解雇了200多名员工。苹果的一位发言人承认了裁员消息,并表示该公司仍认为这一领域存在很大机遇:2018年8月,苹果聘请特斯拉工程副总裁、苹果前高管道格·菲尔德与鲍勃·曼斯菲尔德一起领导泰坦项目。在苹果内部,该团队本周的裁员被视为新领导上任后可预期的调整活动。库克还表示,苹果将在2019年推出新的服务。2016年,苹果也曾从该项目裁员,并调整了战略。
苹果聘请前三星电池高管,有自主开发电池的打算
1月24日上午消息,据彭博社报道,苹果最近聘请了前三星电池高管Soonho Ahn,担任公司电池开发全球主管。
Ahn之前就职于Samsung SDI,后于2018年12月加入苹果。
Samsung SDI为三星一家子公司,以开发智能手机锂电池为主。Ahn的LinkedIn资料显示,他曾担任三星“下一代电池组和材料创新”的高级副总裁。同时,他还是韩国蔚山科学技术院能源和化学工程部门的教授。
谷歌拟改进Chrome安全与隐私,或影响广告拦截插件
据彭博报道,谷歌今年或对Chrome浏览器作出调整,调整后的浏览器将限制外部开发人员开发的浏览器插件与网站的交互。
这一措施的本意是改进Chrome用户的安全和隐私体验。
但是,部分广告拦截软件开发商称,这项措施会导致他们的工具无法正常使用,从而给谷歌自己的在线广告业务带来好处。谷歌女发言表示,公司目前仍在与开发人员商议这一措施。
微软CEO:面部识别技术需要管制,以免竞争导致丧失底线
据外媒报道,微软首席执行官萨提亚·纳德拉表示欢迎政府机构对面部识别技术进行监管,因为人们越来越担心该技术被用于监视人们和侵犯隐私。
纳德拉认为,随着面部识别技术变得越来越普遍,科技公司的自我监管可能不足以应对它可能带来的社会威胁。
他在达沃斯论坛上说:“我觉得,在市场上,只要有竞争,那么正确使用面部识别技术和错误使用面部识别技术之间就没有区别。”
PDF 翻译神器,再也不担心读不懂英文 Paper 了 | 开发攻略
如果你经常跟文献打交道,那你应该切身体验过那种令人抓狂的心情:流畅地阅读 PDF 外文文献,必要情况下还得逐字逐句地翻译出来。
例如,主流翻译引擎之一的谷歌翻译关键时刻会掉链子:
甚至一旦我的文件大小超过 1MB,就只能尝试拆分成为较小的文档。要知道,一篇普通的文献,不加图片只有文字,转换过来也有 1MB 大小。我为什么不选择直接复制粘粘?
然后,接下来的画面更加惨不忍睹……直接通过PDF复制粘贴到翻译引擎中的文档,对多余换行并没有任何处理。
我们常说“翻译无国界”,于是我求助了翻译公司,却觉得不太划算;再于是我求助了某些付费的在线翻译转换文件,却不是翻译慢就是质量有问题;好吧,还不如最后自己操刀,却发现时间上根本不允许……
PDF 格式的文本,本质上是保证了在大部分设备上都能保持清晰完整的排版格式,但不利于进一步使用,痛点是 PDF 文档文字复制会包括回车键,文字粘粘和翻译都不方便。通常的做法就是,先转换成 Word 格式,再一段一段翻译,但这时很多 PDF 本身带有的特殊格式,以及图片、表格、链接等也很容易丢失。
近日,营长发现一款体验“十分优秀”的复制即翻译的外文辅助阅读翻译解决方案——CopyTranslator,它很好地解决了PDF文本换行的麻烦,并借助谷歌翻译API支持,在速度和质量上都有了很好的满足。堪称人类智慧的结晶!为不失公允,特与另款获得不少点赞的 Windows 翻译软件 Qtranslate 进行对比。
那么,下面就来说说这款翻译神器怎么用?
CopyTranslator 0.0.7:
https://github.com/elliottzheng/CopyTranslator/blob/master/README_zh.md
Qtranslate 6.7.2 :
https://quest-app.appspot.com/home
注意,两款软件只能在 Windows 系统中使用,使用 Mac 电脑的同志们无福享用了。
大家也可以试一下!
2、它们各有什么特色?
先来看看 PDF 翻译神器 CopyTranslator:
主要功能:PDF 复制翻译换行问题;多段同时翻译;点按复制;强大的专注模式;智能互译;智能词典;增量复制;双模式自由切换,对应不同场景。
核心用法:打开网页或 PDF,Ctrl+C 复制要要翻译的本文,CopyTranslator 监听到剪贴板变化,会将剪贴板内容进行处理(如去除多余换行等),自动翻译,不用粘粘,并立刻给出结果。
转换能力:
网页版
PDF 格式
响应速度:
翻译水准:
英译中
中译英
让我们再来看看 Qtranslate:
主要功能:支持文本选取的任何应用中的文本翻译;语音识别;图像文本识别(OCR);文本到语音合成;在线词典搜索;离线 XDXF 词典搜索;拼写检查;单词建议/自动填充;翻译历史;虚拟键盘;支持 Babylon 、Baidu 、DeepL 、Google 、Microsoft 、Naver、Promt SDL、Yandex、youdao 等多款翻译引擎 API。
核心用法:选择要翻译的文本,Ctrl+Q,翻译文本会马上显示在当前页面的弹出窗口;或连续按两次 Ctrl,翻译即显示在 QTranslate 主窗口中;即时功能,选中你要翻译的文本,然后按快捷键 Ctrl+E;按下 Ctrl+W 便会进行网络搜索。
响应速度:
由于 QTranslate 在线翻译需要 Internet 连接,在不同在线翻译引擎切换时,估计需要至少 10 秒以上的等待。
翻译水平:
由于 Google、微软必应似乎无法连接(你懂得),营长只选取了 BabyLon 和百度翻译进行了对比。
英译中
中译英
转换能力:
不知是否版本的问题,营长只能说有些惨不忍睹,Qtranlate 与其他多数翻译软件一样并没有 PDF 格式转换的能力。
值得一提的是,只有营长关闭了 360 等各类防火墙后,才能使用该款软件,否则就得出现以下不友好的画面(请小心使用)。
看完以上的主要功能和试用情况,我相信大家都对两款翻译工具有一个清晰的认知了。
从最根本的功能性来讲,QTranslate 胜在有多款翻译引擎工具支持,可迅速选取合适的翻译工具,并进行翻译;而 CopyTranslator 的强大之处在于对 PDF 等格式文本的快速复制,达到了复制即翻译的速度。对于翻译速度和成本均有需求的同志们来说,这一战 CopyTranslator 完胜!
据了解, CopyTranslator 是一款开源软件,本次版本的更新为 Github 上名为“elliottzheng”的贡献者所有。
持续集成系列(1)------https私有docker仓库harbor搭建
持续集成系列(1)------https私有docker仓库harbor搭建
文章目录
持续集成系列(1)------https私有docker仓库harbor搭建
harbor简介
环境准备
docker安装
Harbor下载
Harbor配置
ssl证书部署
Harbor运行
常用操作
思考
## 目标 gitlab+jenkins+docker+harbor+k8s初步实现持续集成
harbor简介
Harbor是VMware公司开源的企业级Docker Registry项目,其目标是帮助用户迅速搭建一个企业级的Docker registry服务。
环境准备
系统:Centos7.4
docker v17.03
ip:10.79.163.40
域名:hub.domain.com
docker安装
#安装依赖包
yum install -y yum-utils device-mapper-persistent-data lvm2
#添加docker源
yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
#安装docker
yum install -y --setopt=obsoletes=0
docker-ce-17.03.1.ce-1.el7.centos
docker-ce-selinux-17.03.1.ce-1.el7.centos
#切换国内hub
curl -sSL https://get.daocloud.io/daotools/set_mirror.sh | sh -s http://ef017c13.m.daocloud.io
#启动
systemctl enable docker
systemctl start docker
#安装docker-compose
yum install -y docker-compose
Harbor下载
cd /opt
wget https://storage.googleapis.com/harbor-releases/harbor-offline-installer-v1.5.2.tgz
tar xzvf harbor-offline-installer-v1.5.2.tgz
cd harbor
Harbor配置
vim harbor.cfg
修改如下内容
hostname = hub.domain.com
ui_url_protocol = https
customize_crt = off
ssl_cert = /opt/certs/hub.domain.com.cer
ssl_cert_key = /opt/certs/hub.domain.com.key
参数解释
customize_crt:使用自签名证书时设置为on
ssl_cert&ssl_cert_key:ssl证书存放的路径
ssl证书部署
自动申请免费ssl证书,这里以阿里云dns接口为例,其它认证方式请参考官方文档
acme脚本安装
cd /opt
#下载Let's Encrypt免费证书自动申请脚本
git clone --depth=1 https://github.com/Neilpang/acme.sh.git
cd ./acme.sh/
./acme.sh --install
阿里云dnskey获取
#导入获取的key
export Ali_Key="yourkey"
export Ali_Secret="yoursecret"
#申请ssl证书
./acme.sh --issue --dns dns_ali -d hub.domain.com
#部署证书
mkdir /opt/certs
./acme.sh --installcert -d hub.domain.com --key-file /opt/certs/hub.domain.com.key --fullchain-file /opt/certs/hub.domain.com.cer
Harbor运行
cd /opt/harbor && ./install.sh
更改域名解析指向服务器ip
访问hub.domain.com
常用操作
默认使用db认证方式,初始用户密码为admin/Harbor12345
登陆仓库
docker login hub.domain.com
推送镜像
#标记镜像
docker tag SOURCE_IMAGE[:TAG] hub.domain.com/library/IMAGE[:TAG]
#推送镜像到项目
docker push hub.domain.com/library/IMAGE[:TAG]
从仓库拉取镜像
docker pull hub.domain.com/library/IMAGE[:TAG]
配置修改后重启
cd /opt/harbor
vim harbor.cfg
docker-compose down
./prepare.sh
docker-compose up -d
服务启停
cd /opt/harbor
docker-compose stop
dokcer-compose start
思考
harbor高可用集群
mysql集群
镜像分布式存储
session保持(多个harbor实例,通过nginx进行负载均衡)
持续集成系列(2)------中文私有git仓库gitlab搭建
持续集成系列(2)------中文私有git仓库gitlab搭建
文章目录
持续集成系列(2)------中文私有git仓库gitlab搭建
gitlab简介
环境准备
gitlab安装
gitlab升级
配置邮件通知及访问域名
gitlab登陆
## 目标 gitlab+jenkins+docker+harbor+k8s初步实现持续集成
gitlab简介
GitLab是由GitLabInc.开发,使用MIT许可证的基于网络的Git仓库管理工具,且具有wiki和issue跟踪功能。使用Git作为代码管理工具,并在此基础上搭建起来的web服务。
环境准备
系统:Centos7.4
docker v17.03
ip:10.79.163.41
域名:git.domain.com
gitlab安装
获取镜像 ``` docker pull beginor/gitlab-ce ``` 运行 通常会将 GitLab 的配置 (etc) 、 日志 (log) 、数据 (data) 放到容器之外, 便于日后升级, 因此请先准备这三个目录。 ``` mkdir -p /opt/gitlab/etc mkdir -p /opt/gitlab/log mkdir -p /opt/gitlab/data ``` 准备好这三个目录之后, 就可以开始运行 Docker 镜像了。 我的建议是使用unless-stopped 作为重启策略, 因为这样可以手工停止容器, 方便维护。
完整的运行命令如下:
docker run
--detach
--publish 443:443
--publish 80:80
--name gitlab
--restart unless-stopped
--volume /opt/gitlab/etc:/etc/gitlab
--volume /opt/gitlab/log:/var/log/gitlab
--volume /opt/gitlab/data:/var/opt/gitlab
beginor/gitlab-ce
gitlab升级
小版本升级(例如从 8.8.2 升级到 8.8.3), 参照官方的说明, 将原来的容器停止, 然后删除:
docker stop gitlab
docker rm gitlab
然后重新拉一个新版本的镜像下来,
docker pull beginor/gitlab-ce
还使用原来的运行命令运行,
docker run
--detach
--publish 443:443
--publish 80:80
--name gitlab
--restart unless-stopped
--volume /opt/gitlab/etc:/etc/gitlab
--volume /opt/gitlab/log:/var/log/gitlab
--volume /opt/gitlab/data:/var/opt/gitlab
beginor/gitlab-ce
GitLab 在初次运行的时候会自动升级, 为了预防万一, 还是建议先备份一下 /opt/gitlab/ 这个目录。
大版本升级(例如从 8.7.x 升级到 8.8.x)用上面的操作有可能会出现错误, 如果出现错误可以尝试登录到容器内部, 依次执行下面的命令:
gitlab-ctl reconfigure
gitlab-ctl restart
配置邮件通知及访问域名
常用邮箱服务配置方式
以腾讯企业邮为例
docker exec -it gitlab bash
cat >>/etc/gitlab/gitlab.rb<<EOF
#配置smtp
gitlab_rails['smtp_enable'] = true
gitlab_rails['smtp_address'] = "smtp.exmail.qq.com"
gitlab_rails['smtp_port'] = 465
gitlab_rails['smtp_user_name'] = "username@domain.com"
gitlab_rails['smtp_password'] = "yourpassword"
gitlab_rails['smtp_authentication'] = "login"
gitlab_rails['smtp_enable_starttls_auto'] = true
gitlab_rails['smtp_tls'] = true
gitlab_rails['gitlab_email_from'] = 'username@domain.com'
gitlab_rails['smtp_domain'] = "exmail.qq.com"
external_url 'http://git.domain.com'
EOF
gitlab-ctl reconfigure
exit
gitlab登陆
浏览器访问http://git.domain.com
初始用户为root,首次登陆会让你设置root密码
以上是关于T | 如何将三万行代码从 Flow 移植到 TypeScript?的主要内容,如果未能解决你的问题,请参考以下文章
我们是如何将3万代码从Flow移植到TypeScript的?
将 30 万行代码从 Flow 迁移到 TypeScript 是一种怎样的体验?