Bulletproof 20150926 - Bentley-Ottmann

根据阿卡林/tt君的建议,移植了一下 Bentley-Ottmann 算法实现到 TypeScript 上了,准备去做性能分析。原来的是 Java 实现。

初版本发布在 https://github.com/Hozuki/xross。未测试。

关于坑、配置问题,请展开。


代码里大量涉及了 MapSet。我一想这不是 ECMAScript 6(已更名为 ECMAScript 2015)提供的新特性嘛,咱上 ES6 去。

于是打开 VS Code,设立工程。

首先这是个 TypeScript 工程,为了能正常编译到 ES6,需要进行如下设置。

更新 TypeScript 至 1.6(写本文时的最新版本):

# 如果未安装
npm install -g typescript
# 如果已安装
npm upgrade -g typescript

一个比较坑爹的事情是,如果你安装了 Visual Studio 的 TypeScript 支持,则要好好检查一下 %PATH% 环境变量,一定要保证 tsc 命令调用的是 NPM 下载下来的 tsc.cmd。有时微软家的会往路径中写入类似 C:\Program Files\Microsoft SDKs\TypeScript\1.*\ 这样的路径,编译器就可能跟不上时代了。

然后在工程目录下建立 tsconfig.json

{
    "compilerOptions": {
        "target": "es6",
        "noImplicitAny": true,
        "module": "commonjs",
        "removeComments": true,
        "sourceMap": true
    }
}

这样 VS Code 在智能感知的时候会去选择 lib.es6.d.ts 而不是 lib.d.ts,就能兼容 ES6 的声明了。而且,VS Code 默认生成的 tasks.json 中,命令行是带 -p 开关的,需要比较版本较高的 TypeScript 编译器才支持。

对了,如果目标是 ES6 的话,编译时不能指定 --module 参数(如果采用 -p . 加上工程目录的 tsconfig.json 这种参数指定方式,则会智能忽略),编译出来的 JavaScript 代码和 CommonJS、AMD 有很大的不同。此时如果直接在 VS Code 中浏览,可能会有大量的红线(媒婆?),因为编译结果中也存在用 ES6 语法的代码。所以要在工程目录下建立 jsconfig.json

{
    "compilerOptions": {
        "target": "es6",
        "module": "commonjs"
    }
}

再次浏览,就不会“满屏红”了。

如果编写正确的话,就应该能正常编译和检查了。接下来希望加入 Gulp 的支持。我的 gulpfile.js 是这么写的:

var gulp = require("gulp");
var concat = require("gulp-concat");
var rename = require("gulp-rename");
var uglify = require("gulp-uglify");
var transpiler = require("gulp-es6-transpiler");

var _build = function () {
    gulp.src("./build/*.js")
        .pipe(concat("xross-build.js"))
        .pipe(transpiler())
        .pipe(gulp.dest("./build/"))
        .pipe(uglify())
        .pipe(rename("xross-build.min.js"))
        .pipe(gulp.dest("./build/"));
};

gulp.task("default", _build);
gulp.task("build", _build);

里面的 gulp-es6-transpiler 是一个将 ES6 语法部分翻译成 ES5 语法的 Gulp 插件。不过运行结果总是出错——因为它不支持 import * from "file" 这个语法。

嘛,还有人说加上 babelgulpfile.babel.js)给 Gulp 提供 ES6 支持。我傻乎乎就试了一下,发现这是给 gulpfile.js 提供 ES6 支持的,而不是给处理上(Esprima/Acorn)提供 ES6 支持的……

算了,整个回退到 ES5 的状态。考虑到现代浏览器(至少 IE11)已经实现了 MapSet,而且运行环境估计就在这里面,所以找一个即使是 ES5 语法也带了这两个的支持的工具吧。WebStorm 默认的环境就是这样,TypeScript 1.4 加上一些 ES6 特性(例如 MapSet 部分的实现)。

写好之后准备调试。然而突然想到一个问题,JavaScript 的 Map 和 Java 的 Map<T> 语义相同吗?当初 KeiSystem 从 C# 转到 Java 的时候,Java 的类型擦除和没有自定义值类型这两个特点让我大伤脑筋。于是我做了一个实验:

var a = {x:1}, b = {x:1};
debug.log(a == b);
var s = new Set([a, b]);
debug.log(s);

结果:

false
Set {Object {x: 1}, Object {x: 1}}

明显不是逐元素比较的,推测是比较对象引用。查阅文档可知,Map 使用的是 === 运算符,更不可能相等了。Java 里的 Map<T> 用的是 equals() 方法。这个方法是可以覆盖的,JavaScript 则没有运算符重载的机制。这就意味着,如果我直接写原文那样的代码,就会造成大量的意料之外的结果出现。好险!

那么,如果我用一个模拟实现会怎么样?例如 ES6Shim

更不可能了吧。如果是按照标准来做的话,最后肯定也不可能判断那两个对象相等的。

所以只剩下一种方法,自己写,即使牺牲了大量的效率。整个项目已经回退到了 ES5 的状态,ES6 是暂时用不了的了。

要是 ES6 早日进入 Living Standard 状态就好了,少了很多麻烦事呢。(到时我也许已经换到 Go 上了,JavaScript 的异步真的好麻烦,语言本身的原因。)


又试用了一下 Atom,总体美观、可扩展性比 VS Code 好,但是响应效率和代码显示就差多了。默认行距那么大(12号字太小,空间空,14号字字体可以,行距大),自动完成列表那么大,我屏幕都快被占了1/16呢,这是推我去买巨大高分屏吗?!敲代码的时候也有很强烈的延迟感。Code 在代码显示上继承了微软家的传统,就是能在同样的空间里让工作空间安排最舒服。JetBrains 和 GitHub 要好好学习。(不仅如此,在在线文档显示上,微软做的也是巨头里很不错的一个。Mozilla 也很好。不服的话对比 MSDN 和 Java API Docs,还有审美狂 Adobe 自家的 ActionScript API Reference。)

分享到 评论