如何将三万行代码从Flow移植到TypeScript?
作者 | David Gomes
译者 | 弯月
责编 | 郭芮
来源 | CSDN(ID:CSDNnews)
【编者按】在内存安全中,类型安全是很重要的一个命题。为了确保JavaScript项目运行的类型安全,本文的作者介绍了2016年时使用Flow的经历:由Facebook支持的Flow方案,不仅拥有查找类型、泛型参数默认值等基本功能,还有着较为完善的JavaScript开发生态系统。但是随着项目的不断复杂,以及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”仍处于禁用状态,这也需要后续解决。
此外,我们还打算删除代码中的一些危险的类型断言。
原文链接:https://davidgom.es/porting-30k-lines-of-code-from-flow-to-typescript/
(本文为 AI大本营转载文章,转载请联系作者。)
公开课预告
◆
今晚8点直播
◆
如何用AI技术为黑白老照片上色?本次公开课中,百度高级研发工程师李超将讲述对抗生成网络相关,学术界的研究现状和应用场景,以及GAN在百度视觉+百度PR+新华社合作的焕彩项目中的应用。
推荐阅读
熬夜写代码,不如换女装入GitHub获上千Star?
春节停车难?用Python找空车位
Python告诉你:为何年终奖多发一元,到手却少两千多?
送你一份春节看片攻略~
加班的程序员:996 没有未来
极客头条
微服务架构下静态数据通用缓存机制
打破区块链不可能三角!2 华人专家论文将登 NSDI 2019 计算机顶会
女程序员:我敲代码养家,老公负责貌美如花!
相关文章:

CRM——插件流程回顾
1. Django项目启动 自动加载文件 制作启动文件1. 注册strak 在apps.py 类里面增加如下 def ready(self):from django.utils.module_loading import autodiscover_modulesautodiscover_modules("stark")2. 在已经注册的app中创建stark.py文件 加载2. 在stark中模仿Adm…
Linux驱动:TI达芬奇系列kernel中cup类型的判断,以cpu_is_ti81xx()为例
cpu_is_ti81xx() 为真 cpu.h (arch\arm\plat-omap\include\plat) 1、 # define cpu_is_ti81xx() is_ti81xx() # define cpu_is_ti814x() is_ti814x() 2、 #define IS_TI_CLASS(class, id) \ static inline int is…

IT人请注意你的身体![转]
IT人请注意你的身体![转]IT一直是很多人的梦想,外行的无数人挤破了脑袋想进这个圈子,在一般人看来,他们是时代的宠儿,他们可以不修边幅,他们工作时间可以身着便装,他们可以不受早九晚五的束缚,他…

WF4.0实战(六):控制WPF动画
这个例子改造了王晓冬老师的:用WF流程控制WPF动画。 本文用一个小例子演示了在WF中定义两个操作步骤,用来控制WPF页面元素的动画。王冬老师当时使用的是WF3.0,现在我改成WF4.0。 先看效果: 活动: 流程: 实现:实现很简单…

嵌入式学习:存储器总结
1、nor flash:NOR采用的并行接口,其特点读取的速度比之NAND快乐很多倍,其程序可以直接在NOR里面运行。但是它的擦除速度比较慢,集成度低,成本高的。现在的NOR的容量一般在2M左右,一般是用在代码量小的嵌入式…

10门必看的机器学习免费课程
整理 | 琥珀出品 | AI科技大本营(ID: rgznai100)文本将介绍来自全球10所著名学府的机器学习和数据科学领域的免费公开课程,范围涉及从入门机器学习到自然语言处理等。1、机器学习华盛顿大学链接:https://courses.cs.washington.ed…

golang实现给图片加水印
2019独角兽企业重金招聘Python工程师标准>>> 最近跟在写golang图片加水印的方法,这里用的是一些基本的功能。 package mainimport ("fmt""image""image/draw""image/jpeg""image/png""os" …

编译android不再需要jdk1.5
2019独角兽企业重金招聘Python工程师标准>>> 今天编译Android的时候发现jdk不是1.5都可以通过。 转载于:https://my.oschina.net/zengsai/blog/4103

来自程序员的福利!用Python做一款翻译软件
来源 | Ahab杂货铺(ID:PythonLearningCamp)前两天吃了平哥的一波狗粮,他给女朋友写了一个翻译软件,自己真真切切的感受到了程序员的浪漫。在学习requests请求的时候做过类似的Demo,给百度翻译发送一个post请…

海思3536:osdrv编译过程中报错及解决方法
1、安装交叉编译工具 1.1 cd toolchain/arm-hisiv300-linux/ 1.2 tar -xvf arm-hisiv300-linux.tar.bz2 1.3 修改cross.install:使用bash 1.4 sudo ./cross.instal 1.5 执行source /etc/profile, 安装交叉编译器的脚本配置的环境变量就可以生效了 2…

Vmware linux 无法上网
流程如下: 1)点击 VM->Settings Hardware选项卡下面 2)点击Network Adapter 设置如下图所示,首先我们在虚拟机中将网络配置设置成NAT, 3、进入Windows操作系统,然后右键点击我们的电脑,进入到管理界面 …

编程25年后,现实将我打回菜鸟程序员的起点
在从事了 25 年的编程工作后,我却发现自己没有什么具体的东西可以展示给大家。我需要克服完美主义思想和对被批评的恐惧,向大家展示真实的自己。作者 | DeChamp译者 | 苏本如责编 | 仲培艺出品 | CSDN(ID:CSDNNews)下面…

MOSS 2010:Visual Studio 2010开发体验(14)——列表开发之事件接收器
通过前面几篇,我们已经完成了内容类型,列表定义,列表实例的开发。本篇继续讲解列表中的一个重要环节——事件接收器开发。 我们的场景是:我希望之前做好的订单列表这个内容类型自动地具有某些事件特征,例如当用户在添加…

海思3536:kernel编译和mpp_single编译过程报错及解决方法
### 1、编译kernel 1.1 cd kernel/linux-3.10.y 1.2 cp arch/arm/configs/hi3536_full_defconfig.config 1.3 make ARCHarmCROSS_COMPILEarm-hisiv300-linux- menuconfig 1.4 make ARCHarmCROSS_COMPILEarm-hisiv300-linux- uImage 1.4.1 弹出选择板子型号:39&…

026——VUE中事件修饰符之使用$event与$prevent修饰符操作表单
<!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>事件修饰符之使用$event与$prevent修饰符操作表单</title><script src"vue.js"></script> </head> <body> &…

Linux shell脚本基础学习
Linux shell脚本基础学习这里我们先来第一讲,介绍shell的语法基础,开头、注释、变量和 环境变量,向大家做一个基础的介绍,虽然不涉及具体东西,但是打好基础是以后学习轻松地前提。1. Linux 脚本编写基础◆1.1 语法基本…

海思3536:PC客户端编译过程报错及解决方法
1、Hi3536V100R001C02SPC040版本编译出错 --------- 错误 4 error LNK2001: 无法解析的外部符号 _IveOpenFile D:\share\Hi3536V100R001C02SPC040\01.software\pc\IVE_CLIB\HiIVE_PC_V2.0.0.7\sample\ive_samples_2.0\Integ\ive_clib_2.0.lib(ive_clib.obj) 错误 5 error LNK20…

百度15篇论文被AAAI 2019收录
1月27日,第33届 AAAI(AAAI 2019)在美国夏威夷召开,其中百度共有15篇论文被收录。AAAI于1979年成立,是国际人工智能领域的顶级国际会议。这一协会如今在全球已有超过6000名的会员,汇集了全球最顶尖的人工智能…

使用 xcworkspace 管理 iOS 工程
首先创建目标工程 创建工作空间xcworkspace文件,并将创建的*.xcworkspace文件放到刚创建的目标工程同级目录下 关闭刚刚创建的目标工程,打开*.xcworkspace文件,把刚刚创建的目标工程添加到工作空间中来 如果要添加一些框架,则将目…

Windows 2008 部署服务之Windows 7 应答文件创建
——————————————————————————————Windows 2008 部署服务八步走:1、Windows 2008 部署服务之WDS概述 2、Windows 2008 部署服务之WDS安装及配置 3、Windows 2008 部署服务之添加启动映像 4、Windows 2008 部署服务之Windows XP应答文件…

DotNetCore跨平台~System.DrawingCore部署Linux需要注意的
回到目录 你在windows上使用图像组件没有任务问题,但部署到linux之后,将注意以下几点: 安装nuget包ZKWeb.System.Drawing项目里还是引用System.DrawingCore,这点不用改安装gdiplus插件,这个需要根据linux类型不同&…

iPhone曝严重漏洞,用户接听FaceTime前或被“监听”!
作者 | 琥珀 出品 | AI科技大本营(ID: rgznai100) 近日,据 9to5Mac 等多家外媒报道,苹果手机 FaceTime 一项重大漏洞被曝光,该漏洞可以让用户通过 FaceTime 群聊功能(Group FaceTime)打电话给任…

海思3536:交叉编译Qt4.8.4
一、交叉编译qt 1、下载Qt源码包qt-everywhere-opensource-src-4.8.4.tar.gz; 2、解压Qt源码包tar -xvfzqt-everywhere-opensource-src-4.8.4.tar.gz 3、创建qmake.conf 3.1在qt-everywhere-opensource-src-4.8.4/mkspecs/qws/下创建linux-hisiv400-g++; 3.2将qt-everyw…

理解LoadRunner中的局部变量和全局变量
以下内容均选自个人作品:《精通软件性能测试与LoadRunner实战》在C语言中,变量有效性的范围称变量的作用域。不仅对于形参变量,C语言中所有的量都有自己的作用域,按作用域范围不同,可分为局部变量和全局变量…
TI-DM8127:MCFW、ISS中对sensor的驱动和控制
1、sensor生命周期 mcfw的bios6层调用iss接口Iss_platformDeviceInit(),该接口调用sensor(以IMX185为例)初始化Iss_Imx185Init(),通过HDVPSS提供的FVID2接口FVID2_registerDriver去注册一个FVID2设备,bios6再创建一个…

小米开源FALSR算法:快速精确轻量级的超分辨率模型
作者 | 周强(我爱计算机视觉)、刘畅编辑 | Jane出品 | AI科技大本营这是来自小米最新出炉的论文,使用神经架构搜索技术自动确定超分辨率网络模型,取得了又快又好的效果,模型已开源,非常赞!摘要深…

Node.js服务器启用Gzip压缩
Gzip是什么 复制大神们的解释吧: GZIP最早由Jean-loup Gailly和Mark Adler创建,用于UNIX系统的文件压缩。我们在Linux中经常会用到后缀为.gz的文件,它们就是GZIP格式的。现今已经成为Internet 上使用非常普遍的一种数据压缩格式,或…

【bzoj1251】序列终结者(伸展树)
【bzoj1251】序列终结者(伸展树) Description 网上有许多题,就是给定一个序列,要你支持几种操作:A、B、C、D。一看另一道题,又是一个序列 要支持几种操作:D、C、B、A。尤其是我们这里的某人&…

再谈PowerPoint 2010导出幻灯片为图片
前些日子写了篇《利用VBA导出幻灯片为图片》,结果被Jackson告知,PowerPoint 2010已经有此功能了,并且PowerPoint 2007可能就已经有了。并且经最终验证,在PowerPoint 2003中同样有此功能。由于平时用PowerPoint并不多,所…

【网络编程】非阻塞connect详解
一、为什么使用非阻塞connect TCP连接的建立涉及一个在三路握手过程,阻塞的connect一直等到客户收到自己的SYN的ACK才返回,这需要至少一个RTT时间,RTT时间波动很大从几毫秒到几秒。而且在没有响应时,会等待数秒再次发送࿰…