Type.im – 一款专注于写作的软件

作为一名爱好写作的程序员,我一直在寻找一款适合写作尤其是长篇写作的软件,不过遗憾的是一直没有找到特别满意的。于是,忍不住发扬程序员重复造轮子的精神,自己写了一个:Type.im✨✨✨。目前在 Mac App Store 上限免中。

Type.im 截图

Type.im 是一款写作软件,虽然也可用于记笔记,但它的主要设计目标是帮助作者写作,比如采用了树状目录组织文档,支持切换到全屏免干扰写作模式,可以快速导出指定目录及其所有子节点等。当然,它才刚刚面世,还远远称不上完美,不过我可以保证的是我会持续打磨、改进它,让它成为一款让写作爱好者称心的软件。

顺便一题,我曾经是 Scrivener 的重度用户,目前我已经将写作工作完全转移到了 Type.im 上了。

一些问题:

Q:会有 Windows 版吗?

A:会有。Type.im 使用的是 Electron 构建,是跨平台的。不过平台之间毕竟还是存在一些差异,目前 Windows 版还有一些体验上的问题,等调好后会发布。如果想提前体验,可以给我发 Email,我的 Email 地址在这儿可以找到。

Q:会有移动版吗?

A:暂时不会有,桌面版还有较多的开发计划,等桌面版大致稳定后会考虑移动版。

Q:会有云同步功能吗?

A:暂时不会有,主要原因是开发云同步会涉及服务器、安全性等一系列问题,暂时还没有精力处理。目前如果想在多台机器上同步,可以将 Type.im 的工作目录放在 Dropbox 等云盘目录下,使用云盘来同步,我目前就是这样在两台电脑之间同步写作内容的。另外,Type.im 的数据文件是纯文本格式,因此,你也可以使用 Git 等工具管理数据,进行版本控制。

最后,欢迎使用 Type.im,使用过程中有任何问题,可以给我留言或发 Email。希望你喜欢它。🙂

黑客的统计学

最近看到一个题为《Statistics for Hackers》(黑客的统计学)的 PPT,觉得挺有意思,便尝试将它的主要内容整理一下,放在这里。

需要注意的是,这儿的 Hacker(黑客)并不是指那些尝试窃取你银行密码的人,而是指那些善用编程来解决问题的人。作者 Jake VanderPlas 是华盛顿大学的资深数据科学研究员,他在 PPT 中表示统计学很难,但使用编程技能后,它也可以很简单,他甚至宣称:只要你会写 for 循环,你就能做统计。

最重要的是:问正确的问题。

1、热身:抛硬币问题(直接模拟)

问题:你抛一个硬币 30 次,其中 22 次正面朝上。问这个硬币是均匀的吗?

这是一个经典的问题。有人认为均匀的硬币抛 30 次应该有 15 次朝上,所以这个硬币不均匀,也有人认为即使是均匀的硬币也有可能因为偶然而抛出 22 次朝上。

经典的解法如下:

假设硬币是均匀的,然后验证这个原假设,计算一个均匀的硬币抛出 22 次正面的概率是多少。

开始计算,如下图所示:

20

列出公式后,计算得知如果硬币是均匀的,那么抛 30 次并且有 22 次正面朝上的概率是 0.008,或者说如果抛 30 次并且观察到 22 次正面朝上,则这枚硬币是均匀的概率为 0.008。这个 0.008 一般称为 p 值,习惯上,当 p 值小于 0.05(有些时候取 0.01)时,我们认为这件事是不太可能发生的,因此拒绝原假设,即得到结论:硬币不是均匀的。

那么,是否有什么简单的方法呢?

这时,编程方法就可以派上用场了,我们只需要模拟一下:

结论:由于 p = 0.008,拒绝原假设,硬币不是均匀的。

简单来说,计算样本分布比较困难,但模拟样本分布很简单。

2、随机打乱

观察两组数据:

✖︎
84 72 81 69
57 46 74 61
63 76 56 87
99 91 69 65
66 44
62 69

★ 均值:73.5
✖︎ 均值:66.9
差异:6.6

问题来了:两组数据的差异 6.6 是统计显著的吗?

经典解法如下:

30

31

32

33

34

然后查表,可得:

39

由于 t = 0.932 > tcrit = 1.796,所以我们得到结论:在 p = 0.05 的水平上差异 6.6 不是显著的。

所以……,我们刚刚究竟都做了些什么?最大的问题,是我们在解题过程中已经忘了我们最初要回答的问题。

为什么不直接使用流行的编程方法来处理呢?比如:

这当然可以,但是……,我们正尝试回答的是什么问题呢?

让我们回到问题本身,上面的样本分布和抛硬币的原理其实是一样的,让我们用一个抽样方法来处理。和抛硬币不同,这儿我们没有生成器(模拟抛硬币的结果),但这不是问题,我们可以引入一个新的解决方案:随机打乱(Shuffling)。

过程如下图所示:

1

执行数千次,最后得到结论:

68

在 p = 0.05 的水平上,差异 6.6 不是显著的。

关于随机打乱需要注意的事:

– 只能在原假设认为两组相同时使用
– 和所有其他方法一样,只能在样本有代表性时使用,千万要注意选择偏差
– 要注意相关实验。这点在《Simon’s Resampling: The New Statistics》一书中有很好的讨论

3、亚特尔的乌龟塔

亚特尔的乌龟塔是一则童话寓言,讲述的是一只叫亚特尔的乌龟命令其他乌龟叠在一起成为高塔的故事,知名中文博主阮一峰曾经翻译过一个版本

72

假设我们观察到了 20 个亚特尔乌龟塔,高度分别为:

48 24 32 61 51 12 32 18 19 24
21 41 29 21 25 23 42 18 23 13

问题是:
– 亚特尔乌龟塔的平均高度是多少?
– 这个估值有多少偏差?

需要注意的是,这儿问的是全体亚特尔乌龟塔的平均高度,即可能存在成千上万个乌龟塔,我们只观察了其中 20 个样本,并对整体均值进行估计,而不是简单地问这 20 个样本的均值,所以才有第二个问题:这个估值有多少偏差。

经典解法如下:

74

那么,我们是否可以使用抽样方法来处理这个问题呢?和之前一样,我们的问题是没有生成器,这次的解决方案是自助重抽样法(Bootstrap Resampling)。如下图所示:

1

简单来说,就是从现有的 20 个样本中,随机抽取 20 个值(抽完后放回去,因此可能会抽到重复的值),然后计算新样本的均值。

重复这个步骤成千上万次,最后,我们得到下面的结果:

102

可以看到,与上面使用公式计算出的结果几乎一样。

自助重抽样法(Bootstrap Sampling)甚至还可以用在更复杂的统计上,同样的,也有一些注意点:

– 自助重抽样法(Bootstrap Sampling)被认真地研究过,有坚实的理论基础
– 自助重抽样法通常不太适用于基于排序的统计(如求最大值)
– 如果样本太小,效果会比较差(N > 20 比较好)
– 注意选择偏差以及非独立数据

4、交叉验证

最后,PPT 中还举了一个交叉验证的例子。大致思想是将样本随机分为两部分,各自算出需要的值,然后用另一份样本来校验当前样本的值。重复成千上万次,最后得到可信的结果。限于篇幅,这儿就不详细介绍了,有兴趣的同学可以直接看原 PPT

小结

相对抽象的统计学公式而言,抽样方法是一种更符合直觉的计算近似法。只要会写 for 循环,就可以做统计分析。

代码是如何被写短再写长的

需求是这样的:

写一个函数,根据结婚的次数给出婚假的天数。如果未婚,婚假天数当然是 0 天,如果是初婚,婚假天数为 15 天,如果是再婚或第 N 次结婚(N >= 2),则婚假天数为 3 天。

工作第一年

文件: main.js

很多人可能都写过这样的代码。可以看到,这个版本的代码非常简单,基本上就是需求的直接翻译,它没有错误,甚至性能也不错。很多时候这样的代码可能在某个系统中运行很多年,但也有些时候,由于需求变更,你或你的继任者不得不对其进行重构。

工作第三年

文件: main.js

工作一段时间,了解了更多语言上的技巧后,你可能会写出这样的代码。这个时期,你相信高手的标志之一是用最少的代码解决问题,比如能用一行代码解决的问题,绝对不用两行。

这个时间段你代码看起来非常精简,有时也不乏优美之作,但也有一些为了精简而抛弃了可读性,同时损失的还有代码的可维护性。比如,如果需求突然变为再婚有 10 天婚假,第三次或更多次结婚时才是 3 天婚假,这时你需要深入到 howLongMarryHolidays 方法的实现细节中,至少需要修改两处地方才可以完成需求。

工作第十年

文件: config.js

文件: main.js

在经历了无数次需求变更以及无数次熬夜加班之后,你逐渐意识到,写最少的代码并不能让你避免加班,代码的可维护性比精简长度更重要。经过分析,你发现这个简单的需求其实分为可变的配置项基本不会变的逻辑两部分,于是,代码也被自然地分解成了两个部分,——配置与逻辑进行分离。

现在,代码量的确有所增加,但收益是逻辑更清晰了,维护时的成本及出错率也都降低了。当需要改变婚假规则时,一般只要修改 config.js 中的配置即可,并且这个修改通常来说是安全的。

小结

这只是一个简单的例子,对这个小例子而言,三种方法没有太大的差异。但如果遇到更复杂的需求,也许可以像最后一个方法那样,将易变的部分与稳定的部分隔离开来。你的代码似乎变长了,但长期来看,你的工作变少了。

涂鸦之《年会绝技》

以前年会时,经常有吹气球比赛的环节,谁最先吹爆一个气球,谁就获得奖品。那时我常想,要是有人回去后苦练吹爆气球技能,那么下一年年会上这个环节的奖品大概就非他莫属了。不过,不知从什么时候起,这个环节似乎越来越少见了,今年我们部门的年会,干脆都没有吹气球,也没有奖品了,互动环节主要就是在手机上抢红包,于是便有了下面这个漫画。

吹气球

画完之后,老婆大人说故事感觉不是太好,于是我补充了另一个:

一指禅

欢迎转载。:)

SwitchHosts! 3

2011年时写了一个切换hosts的小工具SwitchHosts!,2012年时发布了第二版,一转眼3年过去了,在2015年即将结束之时,终于抽空编写了它的第三个版本

 

Logo with title

Capture 1

我在日常开发过程中经常会用到SwitchHosts!,第二版虽然能满足大部分需求,但还是有一些不方便的地方,第三版在功能上主要有如下变化:

允许方案多选

我们日常开发过程中,有时会有很多公用的hosts希望总是生效,第二版的处理方法是提出一个单独的公共方案,但在一些复杂组合的情况下,单独的公共方案常会显得力不从心,于是,这一次索性将方案改为允许多选。自然,这样也就不再需要一个单独的公共方案了。

点击行号快速切换某一行的注释

有时我们只时临时想关闭/开启某一条hosts规则,如果单独将它作为一个方案就有点太重了,手动在行首加“#”进行注释又太麻烦,因此有了这个功能,帮助用户快速切换某一行的注释状态,从而达到关闭/开启某一行的效果。如下图:

click to comment

 

除此之外,第三版的一个很大的变化是技术上放弃了Python + wxPython的架构,采用了MacGap来构建。

之所以放弃wxPython,主要是因为这个UI框架更新比较慢,社区也相对不活跃,我遇到不少问题都无从下手,比如第二版在Retina屏下有一些问题,同时Mac版的编辑区无法使用中文输入法等。最终,我决定在新版本中采用Web技术来开发界面。

一开始,我尝试了Electron,并且做出了一个版本。但Electron生成的程序实在是太大了,无论Mac上还是Windows上都超过了100M。SwitchHosts!只是一个轻巧的小工具,虽然现在用户电脑的硬盘越来越大,但这么一个小工具要占用100M+的空间还是让我觉得不太合适,于是我转而尝试了MacGap。好处是现在的程序只有1M多点,坏处是第三版目前只适用于Mac了,Windows用户暂时只能继续使用第二版。

目前这个版本仍有不少可提升的地方,不过既然它已经是一个可用的版本,那就让我遵循MVP(minimum viable product)原则,先发布,再根据反馈进行迭代完善吧。

最后,贴一下项目主页:https://oldj.github.io/SwitchHosts/。欢迎下载试用反馈建议

HTML5塔防游戏小更新

5年前写过一个HTML5塔防游戏,写完之后,对JavaScript有了一些新的认识,也因此而结识了不少新朋友。曾经想不断完善它,打造一个正儿八经的塔防游戏出来,不过后来半途而废了。一转眼,已经有超过4年没有再更新过它。

前几天突然想起,又翻出来玩了下。以现在的目光来看,这个游戏的代码自然存在很多不好的地方,比如说多个JS模块的组织方式就极为原始,——当时AMD、CommonJS等规范才刚刚兴起,twitter等还在使用LAB.js加载模块,CMD规范尚未被提出,而我想打造一个完全不依赖第三方库的东西。不过,这些实现其实并不影响游戏的体验,最让现在的我难以忍受的是它不支持Retina屏幕,——它是基于Canvas绘制的,不是矢量图,因此在Retina屏幕上打开它时,图象会显得很模糊。

于是抽空做了4年来的第一次更新,让它在Retina屏幕上能显示得更清晰。

如何让Canvas支持Retina屏幕呢?原理其实非常简单,绘制一个2×2倍大的Canvas,再将它缩放至原始大小即可。更严谨一些的方式是先读取一下window.devicePixelRatio的值,再决定绘制时使用的放大倍数。

现在,在Retina屏幕上,这个5年前的老游戏也终于清晰了。:) 这或许是它的最后一次更新。

另外,也顺便感慨一下摩尔定律:5年前,这个游戏在初代iPad上,只能跑到大约8帧/秒,现在,同样的代码,跑在2年前的iPad 4上已经毫无延迟。

10

一个简单的JavaScript模块加载器

大型网站项目中,JavaScript 按需加载是一个常见的需求。几年前,LABjs 曾经流行过一段时间,它的主要原理是创建一个 type="text/cache"script 标签,并在需要的时候将其更改为 type="text/javascript",从而动态并行地加载 JS 并控制其执行时间。

使用 LABjs 时,被引入的 JS 几乎不需要更改,使用非常方便。但它也有不足,最大的问题是它只是一个加载器,没有模块管理功能,而后者对大型前端项目非常重要。很快,随着 CommonJSAMDCMD 等规范的流行,Require.JSSeaJS 等兼顾了 JS 文件按需加载以及模块化的加载器占据了更大的市场。LABjs 也在两三年前宣布停止开发,后来又说还会维护,只是不再添加新功能。

CommonJS 规范主要在 Node.JS 环境中使用,当然,现在也有 browserifywebpack 等工具可以让浏览器端的 JS 直接使用 CommonJS 规范。它们的原理一般是分析依赖关系,然后将所有依赖的 JS 打包为一个文件。(webpack 也可以实现动态按需加载。)

AMD、CMD 规范则是完全为浏览器端 JS 设计的。它们的设计细节不同,不过最基本的原理一样:通过类似 JSONP 的方式加载 JS 并隔离不同模块的变量。当然,在具体实现过程中还有很多问题需要考虑,比如模块依赖关系等。另外,SeaJS 等还会用正则匹配出用户在代码中直接用 require 等“关键字”加载的模块并自动加入依赖。

下面是我参考 AMD 规范实现的一个极简的 JavaScript 模块加载器(源码),去掉注释和空行差不多100行的样子。

你可以在 GitHub 上查看它的源码及示例 。需要说明的是,它只实现了 AMD 规范的一个子集,并且把 require 改为了 myloader.use,同时对循环依赖等情况也没有做处理。不过除了这些,它已经是一个可以使用并且兼容各大浏览器的 JavaScript 模块加载器了。

最后,现在流行的各种 AMD、CMD 加载器,在不久的将来也会像 LABjs 一样被人慢慢忘记,因为它们都只是为了解决某一个特定历史阶段的某一类技术问题而诞生的,随着相关技术的发展,它们也将慢慢完成历史使命,退出前端舞台。比如,基于 jspm 等项目,我们现在已经可以使用 ES6 中的模块加载方法:

一道数学题及其证明

今天在朋友圈里看到一道数学题以及一个巧妙的证明方法。

题目是这样的:是否存在两个无理实数ab,使得a^b是有理数?

那个证明非常巧妙。

答案是存在。证明如下:

考察,它是个有理数吗?有两种情况:

情况一:是有理数,这种情况下问题就被解决了:a = 554330.pngb = 554330.png就是一组解。

情况二:不是有理数,那么,就令 a = b = 554330.png。此时,ab 都是无理数,但是有 155376.png,这是一个有理数。

这个证明的漂亮之处在于我们自始至终都不知道究竟是不是有理数,但是这并不妨碍我们得到最终结果。

907565.jpg

《代码英雄传》

曾经在网上看到过一个都市武侠的故事,篇幅不长,但很有趣,讲述了一群身怀绝技的普通人在现代都市中的生活。之后我就把这个故事忘了,直到很久以后突然想起,我问自己:如果我们生活在一个武侠世界,我们的生活可能会是什么样的?于是便有了《代码英雄传》这个故事。

故事不长,两万字左右,一般阅读速度约一小时能读完。内容纯属虚构,不过也有一些现实中的影子,比如故事中的三大诸侯分别以现实中的BAT三巨头为原型(当然,仅仅是原型,没有黑哪家的企图 ^_^)。

这是一个普通的武侠故事,当然,如果你了解一些编程,同时还对目前国内主要互联网公司有一定了解,或许会从中发现更多的笑(槽)点。

最后,地址如下:

1、简书地址

2、GitHub地址

3、PDF版下载地址

希望你喜欢这个故事,也欢迎吐槽指正。:)