Planet Emacs CN

April 19, 2017

子龙山人

以前写的诗

恰同学少年,意气风发,偶得诗词几首,今存档于此。

满江红

重九夜半,竟凭栏,触目断愁肠。晓忆婵娟,丝成万缕,欲剪还乱。 二三年情爱与恨,四五万字喜与忧。失此绝惠恩,空遗恨。

山水情,犹未叙。知己心,何时铭。风雨中,托云寄予锦书。 壮志饥餐文理肉,笑谈渴饮艺术血。待哪日,与君同坐谈,朝天阙。

钗头凤

做人难,人难做。生临此事谁的错?人成各,今非昨。一盏孤灯,几年离索。错!错!错!   

蛋炒饭,饭炒蛋,改头换面一个样。书声寒,心亦颤。泰山北斗,龙门客栈。散!散!散!

秋声赋

半分秋色,一杯黄土,乐岁终身苦。当年塞下羌笛故,明日黄花怎堪顾。

喧嚣声动,肆无忌惮,感人生苦短。可叹薄暮秋声寒,秋蝉声里斜阳残。

扬州幔

楚湘脚都,麓山南路,聚众稍释初缘。眼中朋友生面,恰憎雨纷纷。 自德智演出散后,冷落清场,犹恋周朗。诣黄昏,红罗裙帐,皆闹疯狂。

陈胜豪爽,一挥手,应者千万。奈组织涣散,士卒将相,各自寻欢。 旋转舞步飞快,一跌跤,猛觉醒来。看古今中外,嘻嘻何其类哉。

江城子

十年寒窗头悬梁,意气洒,豪情残。学识浅短,有幸入中南。多彩军旅苦与甘,潇洒过,回味长。

闲来幽步忽临场,绿军装,正狙枪。英雄无泪,唯有血满膛。料得年年情系处,湘江畔,岳麓山。

军训

军训日当午, 汗滴脚下土。 谁知训练场, 步步皆辛苦。

赠山人

老屈存钱欲买房, 忽闻长沙房价涨。湘江河水深千尺,不及两眼泪汪汪。

因为爱情

检点人生侥幸事,莫过于,流年未改旧相知。回顾旅程百事涌,比不及,岁月空逝人依旧。

雪中登麓山

大雪纷飞兮云飞扬,云飞扬兮登麓山,登麓山兮嗅梨香。

沁园春.情

麓山美景, 千年书院, 万世红枫. 看中南内外, 唯余莽莽, 大江上下, 顿失涛涛. 无惧末日, 勿需船票, 欲与乔公试比高. 假时日, 看子龙山人, 挥斥方遒。

美人如此多娇, 引无数英雄竞折腰, 惜越鱼汉雁, 略输灵巧, 东月唐花, 稍逊风骚, 一代女侠, 白发魔女, 一朝情伤白了头, 俱往矣, 吾尤怜之物, 还当红豆。

踏歌行

醉笑陪君三万场,不诉离殇。

劝君更尽一杯酒,西出长沙无故人。

莫愁前路无知已,天下谁人不识君。

April 19, 2017 06:19 AM

April 14, 2017

GeekPlux

文本数据可视化(上)——从 Wordle 谈起

本文首发于:GraphiCon 知乎专栏-文本数据可视化(上)——从 Wordle 谈起

看到题目,你可能一脸懵逼,什么是 wordle?下面这幅图就是:

是不是很熟悉?大到宣传海报,小到个人名片,Wordle 如今随处可见。它可以轻松的展示出一段文字的关键词,让我们对这段话的内容一目了然。其实这属于数据可视化中的文本内容可视化,常用于社交网络中的内容分析,还记得前几年微博有有个插件(现在似乎找不到了),用户可以一键生成自己的微博关键词,当时引起了大量转发,因为很多用户的关键词出乎意料,让自己都大吃一惊。本文就从最简单的 Wordle 说起,说说文本内容可视化,以窥数据可视化一隅。

为什么要有 Wordle?

其实要回答这个问题就要回答为什么要做数据可视化。我们先看下面这段话:

GraphiCon取GraphicsCon图形控之意(类比lolicon)。
我们会把我们觉得有趣的,好玩的,有用的图形学相关的技术,知识,想法,资讯放到GraphiCon这个小空间里。
GraphiCon的po主们虽然遍布天涯海角,在从事着不同的事,但都是痴迷计算机图形学的小伙伴们。
只要你也对计算机图形学感兴趣,或者喜欢好玩有趣狂拽酷炫的图形项目,那么你也是GraphiCon!

一眼扫过去,你可能能轻松地注意到 GraphiCon 出现了 4 次。而还有个关键词「图形」,虽然出现了 5 次,但显然没有 GraphiCon 醒目。平时阅读比较快的同学可能有一目十行的本领,其本质就是关键词提取。瞬间了解一段话的大意,进而判断要不要花时间去读。但这完全取决于你个人提取关键字的能力。如果换一种图形的形式,可能就会非常直观:

看这张图明显比看枯燥的文字要直观的多,而且还意外发现了「计算机图形学」这个词的频率竟然也挺高。它在原文中出现了两次,这是我们单纯看文字很难发现的。不过,这幅图还不是最好的效果,你可能也发现:除了词频高的词,其他关键词很小,根本看不清;而且整个布局很零散,空白太多,没有一种聚合的感觉。

这幅比上面的又稍美观了一点,同时对词的重要性也进行了重新统计,不再是单纯的根据词频。可能你觉得还不够美,那这样:



哈哈,还有很多好玩的,不过例子中这段话的篇幅太短,关键词太少,所以显得不是很紧凑,很多更酷炫的样式也不适用了。

至此,我们可以总结一下 Wordle 的作用:

  • 把枯燥的数据直观地呈现出来,使人更好的洞察数据
  • 更有利于数据分析(比如发现「图形」才是出现最多的词)

其实数据可视化就是把复杂的数据转化为直观的图形,方便人们洞悉。而且刚才这个例子还只是最简单的文本数据,如果是非常复杂的数据,普通人根本看不懂的那种,就更需要数据可视化为我们抽丝剥茧,完美呈现。关于可视化的必要性我会慢慢渗透到之后的每一篇文章中,接下来开始介绍 Wordle 的制作过程。

文本信息的提取

任何的数据可视化都离不开三大步骤[1]:分析、处理、生成,Wordle 亦然[2]。如何从一段文字变成一张优美的图片,我们大概要经历以下步骤:

  • 提取关键词(去掉冗余的文字)
  • 计算关键词权重(决定哪些词着重显示)
  • 布局(算出每个词摆放的位置)

第一步中,英文的分词相对中文来说简单的多(在创作本文的过程中,我几乎是找遍了中文标签云制作工具,没发现一款分词做的好的),把单词都分开后,去掉一些助词如 the、a、that 等,再把单词的时态语态还原就好了。第二步最常用的就是计算词频了,一个词出现的次数越多它的权重越大(Wordle 就是用了词频)。除此之外,还有用单词在句子中的成分来判断其重要性的、有用各种概率模型的,这涉及到自然语言处理和文本信息挖掘,总之方法多种多样。另外,文本数据挖掘一直也是热门的研究话题,尤其是中文处理这道难以逾越的鸿沟。大家有兴趣可以自行研究。

设计

把单词任意排列的表现形式,最早的灵感来自于排版印刷:

上图是《数据可视化之美》的配图,分别来自美国国家设计研究中心和古埃及草纸。95 年在动漫作品 EVA 中,词云的表现形式也有所体现[4]。

这种看似杂乱无章的排布,恰恰与人类的跳跃思维相契合,人脑的思绪随着视觉的跳动也跟着不断联想。

而在计算机上,最早的文字可视化其实是「标签云」。当年博客爆发的时代,几乎每个博客里都有一个这样的插件:

它用文字的大小和颜色的深浅来表达了文字在文本中的重要性,比你单纯看一段文字要直观的多。但它的缺点也很多:从美的角度来说,它同一行如果有一个词字体特别大则直接导致行距变大,不仅造成了空间的浪费,还让整体看起来非常不整齐、不协调;从信息展现的角度来说,字体的深浅大小不能更好的体现差异,比如上图中 good 的权重是 50,而 life 的权重是 20,但它俩看起来的差别并不大。

后来出现的 Wordle 针对这些缺点一一作了改进。首先它用字体的粗度来加深权重的展示,因为人的视觉对面积的感知比对饱和度的感知要强,所以加粗字体效果拔群;其次 Wordle 用紧凑的布局给人以美的享受,你甚至可以给定形状来生成不同的 Wordle。

Wordle 具体采用的算法是贪婪算法,最开始在给定区域内把最重要的单词先摆到某个位置(这个位置你可以指定,一般是中心线),然后用下个单词在它的旁边不停做交叠测试,直到没有重叠。依次迭代,直到每个单词都摆放好。

Wordle 有什么不足之处

虽然 Wordle 的设计已经很美观,但作为一个有批判性思维的少年,我们还是要对它批判一番:

  • Wordle 美观程度很大程度依赖于它所选的字体。相信你也见过宋体中文的 Wordle。。
  • 相同权重的单词,可能越长的显得权重越大,因为它占得面积更大。
  • 颜色意义不大。不过现在这个好像变成了优势,使它可以与不同图片结合。

除此之外

Wordle 算是文本内容可视化中最经典的形式,除此之外还有一些更有趣的,比如下图的 DocuBurst [4]。它用环形布局巧妙地展示了文本的层级关系,外圈的单词是内圈单词的下一层。

正所谓“一图胜千言”,文本可视化把枯燥的文字变成有趣的图片帮助人们加深理解,可以说是功不可没。如果有兴趣继续了解可视化相关的内容,敬请期待下一期。


本作品采用知识共享 署名-非商业性使用-禁止演绎 4.0 国际 许可协议进行许可。

April 14, 2017 03:06 AM

March 28, 2017

Redguardtoo

Auto-complete word in Emacs mini-buffer when using Evil

When using Evil I often input %s/old-keyword/new-keyword/g in Minibuffer.

The problem is auto completions of new-keyword using hippie-expand always fail.

It turns out that the character "/" is treated as Word constituent in minibuffer.

The solution is to re-define "/" as Punctuation characters:

(defun minibuffer-inactive-mode-hook-setup ()
  ;; make `try-expand-dabbrev' from `hippie-expand' work in mini-buffer
  ;; @see `he-dabbrev-beg', so we need re-define syntax for '/'
  (set-syntax-table (let* ((table (make-syntax-table)))
                      (modify-syntax-entry ?/ "." table)
                      table)))
(add-hook 'minibuffer-inactive-mode-hook 'minibuffer-inactive-mode-hook-setup)

by Chen Bin at March 28, 2017 12:16 PM

Indent JSX in Emacs

I find rjsx-mode annoying when it indents closed html tag with extra spaces.

It's based on js2-mode which is actually just calling Emacs v25 API js-jsx-indent-line.

So the solution is as simple as advice js-jsx-indent-line:

(defadvice js-jsx-indent-line (after js-jsx-indent-line-after-hack activate)
  "Workaround sgml-mode and follow airbnb component style."
  (let* ((cur-line (buffer-substring-no-properties
                    (line-beginning-position)
                    (line-end-position))))
    (if (string-match "^\\( +\\)\/?> *$" cur-line)
      (let* ((empty-spaces (match-string 1 cur-line)))
        (replace-regexp empty-spaces
                        (make-string (- (length empty-spaces) sgml-basic-offset) 32)
                        nil
                        (line-beginning-position) (line-end-position))))))

by Chen Bin at March 28, 2017 11:54 AM

March 07, 2017

Free Mind

Plotting 3D Surfaces in Python

最近需要 visualize 一些三维的曲面数据,于是简单调查了一下 Python 绘制三维曲面的一些常用的办法,贴在这里以免自己将来再需要用到的时候又想不起来了。比如我们想要画这个样子的 3D 曲面图。

方便起见,我们假造一个 user case,假设 \(x\)\(y\) 是两个 hyper parameter,现在你使用 grid search 的方式尝试了每个参数组合下的模型效果。

  1. import numpy as np
  2. def eval_params(x, y):
  3. # ...
  4. # Make data.
  5. X = np.arange(-5, 5, 0.25)
  6. Y = np.arange(-5, 5, 0.25)
  7. Z = np.zeros((len(X), len(Y)))
  8. for i in range(len(X)):
  9. for j in range(len(Y)):
  10. Z[i, j] = eval_params(X[i], Y[j])

现在我们希望把这个参数搜索的曲面画出来,看看最大值最小值、曲面的连续性之类的性质,所以我们需要画一个 3D surface 图。比较新版本的 Matplotlib 其实已经有挺好的 3D 绘图支持。上面的示例图其实就是在 Matplotlib 里画的。

  1. import matplotlib
  2. from mpl_toolkits.mplot3d import Axes3D
  3. import matplotlib.pyplot as plt
  4. from matplotlib import cm
  5. fig = plt.figure()
  6. ax = fig.gca(projection='3d')
  7. XX, YY = np.meshgrid(X, Y)
  8. # Plot the surface.
  9. surf = ax.plot_surface(XX, YY, Z, cmap=cm.coolwarm,
  10. linewidth=0, antialiased=True)
  11. # Add a color bar which maps values to colors.
  12. fig.colorbar(surf, shrink=0.5, aspect=5)
  13. plt.savefig('surface.pdf')

其中 import Axes3D 那句虽然没有直接用到,但是如果删掉的话在创建 3D projection 的 axes 的时候就会出错。plot_surface 这个函数负责绘制 3D surface,参数相当 straightforward。因为 Z 作为我们 evaluate 参数的结果是一个二维的矩阵,所以 plot_surface 需要的前两个参数也是二维矩阵,并且需要是和 Z 大小相同。

简单来说,对于一组 index (i, j)XX[i, j], YY[i, j]Z[i, j] 一起给出了一个位于曲面上的点的三维坐标。而 meshgrid 这个函数就是用于方便地根据原来的 XY 来创建这样的二维矩阵的。

到这里我们的目标其实已经达到了。如果把上面的 savefig 换成 plt.show() 的话,会弹出一个对话框来显示 3D 曲面图,好处在于可以对曲面进行旋转、缩放等交互操作,有时候对于看到曲面被挡住的部分来说非常有用。不过 Matplotlib 的 3D 操作交互反应速度相当缓慢,可能是没有用任何硬件加速之类的简单实现吧。如果你不能忍受那个缓慢的交互速度,或者需要将生成出来的图片发给别人看,而对方又不一定是能运行 Python 代码的,那还有一个选择就是生成内嵌 3D 对象的 PDF 文件。PDF 文件可以通过 PRC 格式内嵌 3D 图像模型,不过目前只有 Adobe 家的阅读器(比如 Adobe Reader 或者 Adobe Acrobat)可以查看这种格式的对象。在 Acrobat 里打开大概长下面这个样子,可以对 3D 曲面进行各种操作和检查。

不过 Matplotlib 本身似乎并不支持直接输出这种内嵌 3D 对象的 PDF 文件。这里我们可以借助另外一个叫做 Asymptote 的绘图工具。它其实是一种绘图语言,和 LaTeX 结合得比较紧密,但是和 PGF/TikZ 或者是 PSTricks 不一样的是,它并不是实现为 TeX 的宏包,所以它的语法并不是一些看起来像英语一样的很飘逸但是一不小心就会语法错误的 DSL,而是有点类似于 C 语言语法。很多年前用它画过一些 3D 的 vector field 之类的图,感觉比较适合画一些复杂的 scientific 的 illustration,可以看一下它的示例

当然我一般不会用它来 plot 数据图,不过这里可以借用一下的是它能输出内嵌 3D 对象的 PDF 文件的功能。Asymptote 画图的简单流程是创建一个 foo.asy 文件,把命令写在里面,然后执行

asy -f pdf -o foo.pdf foo.asy

就可以了。Asymptote 里构建一个 3D surface 可以通过一个叫做 surface 的命令来实现,它的第一个参数是一个描述 surface 的函数:输入是 \(i,j\) 两个 index,输出是一个三维坐标 \((x(i,j), y(i,j), z(i,j))\)。这个比较适合画一些 parametric function,不过这里我们是直接 plot 数据,其实只要做一个简单的矩阵查表函数就可以了。Asymptote 里最简单的读取数据的方法是读入空白分隔的文本文件,打开文件之后只要对一个数组或者矩阵进行赋值就可以读入一行或者一块数据:

  1. import graph3;
  2. import palette;
  3. size3(150, IgnoreAspect);
  4. // Open the data file
  5. file in=input("data.txt").line();
  6. // read file by assignment
  7. // this read a line of numbers as an array
  8. real[] x=in; // first line
  9. real[] y=in; // second line
  10. // read the rest of the file as a matrix
  11. // 0, 0 means do not restrict the number of
  12. // elements to read in either dimension
  13. real[][] z=in.dimension(0,0);
  14. triple f(pair t) {
  15. int i = round(t.x);
  16. int j = round(t.y);
  17. return (x[i], y[j], z[i][j]);
  18. }

数据文件格式非常简单,头两行分别是两个参数 \(x\)\(y\) 的值列表,然后剩下的行是整个数据矩阵 \(z\) 的一个表格。通过 Numpy 的 savetxt 函数很容易导出。在 Asymptote 里读入数据之后我们就可以构造查表函数 \(f\) 了,从上面可以看到 \(f\) 只是直接通过下标取得相应的坐标而已。接下来就可以直接构造 surface 并进行绘制了:

  1. surface s = surface(f, (0,0), (x.length-1, y.length-1), x.length-1, y.length-1);
  2. s.colors(palette(s.map(zpart), Rainbow()));
  3. draw(s, meshpen=blue);
  4. triple m=currentpicture.userMin();
  5. triple M=currentpicture.userMax();
  6. triple target=0.5*(m+M);
  7. currentprojection=perspective(camera=target+realmult(dir(60,210),M-m),
  8. target=target);
  9. xaxis3(Label("$\lambda_1$",position=MidPoint,align=-Y-Z),
  10. Bounds(),blue,OutTicks(Step=0.25,step=0.05));
  11. //Bounds(),blue,OutTicks(Label(align=-Y-X)));
  12. yaxis3(Label("$\lambda_2$",position=MidPoint,align=-5X),
  13. Bounds(),red,OutTicks(Step=0.25,step=0.05));
  14. zaxis3("Error",Bounds(),black,OutTicks(Step=0.25,step=0.05));

代码非常直接,唯一需要简单注意一下的是我们在调用 surface 函数的时候,传递了参数 (0,0)(x.length-1, y.length-1),这里对应 \(f\) 在查表时的输入序列。

这样就大功告成了,当然缺点就是 Adobe 家族以外的 PDF 阅读器都看不了。第三个对于 viewer 来说比较 user friendly 的选项就是通过 web 页面。现在在 webGL 之类的技术支持下,其实 web 页面里渲染 3D 对象的效果其实已经非常好了。

比如之前在网上看到这个 3D 的卡通画,简直太惊艳了,我觉得现在的各种 3D 游戏之类的都处于严重的 Uncanny Valley 之中,让我非常难以接受,相反若是做成这种风格,应该会(让我觉得)赏心悦目很多。然而虽然看起来只是简单的 line drawing 的结果,但是实际的制作过程还是要进行正儿八经的三维建模、渲染之类的,作者在这里给了制作过程介绍,看起来似乎还是相当费力的。另外还有这里也有一些非常漂亮的 3D 线条漫画,看起来都非常酷,而且都是直接在浏览器了渲染出来的,不需要额外安装什么很复杂的 viewer 。

就我所知范围,目前的绘图工具中似乎 plotly 算是对 webgl 支持比较好并且也简单易用的。它们的 3D 图大概长下面这个样子,这里有一个在线的例子可以直接进行交互,渲染和交互效果都挺好的。

例如直接用我们刚才的 Python 里的数据的话,用下面的代码可以直接生成 plotly 的图,结果保持成一个 HTML 文件。文件里内嵌的 viewer,应该用任意一个现代一点的浏览器都能打开预览和交互。

  1. import plotly
  2. import plotly.graph_objs as go
  3. data = [go.Surface(z=Z, x=XX, y=YY)]
  4. layout = go.Layout(title='Test Plot', autosize=True)
  5. fig = go.Figure(data=data, layout=layout)
  6. # save offline standalone HTML files
  7. plotly.offline.plot(fig, filename='test-plot')

唯一的缺点可能就是由于内嵌的 viewer,所以 HTML 文件还是比较大的,一个简单的 surface plot 大概也需要 2M 左右。另外 plotly 还能内嵌在 Jupyter Notebook 里预览交互图,它还提供收费的云服务可以在服务器上 host 你的 figure。

感觉以后的科技论文格式应该允许更多的交互内容,嵌入视频、音频、交互图表之类的,还有更全的 meta data,图表、公式、引用的预览、跳转等等。然而现实是目前很多地方还会要求 plot 必须是在黑白打印的情况下也能分辨的。

March 07, 2017 12:00 AM

February 26, 2017

Redguardtoo

Find the buffer with Chinese name

We can find the buffer with Chinese name efficiently by using the first character of pinyin.

Here is the code (ivy and pinyinlib is required):

(defun ivy-switch-buffer-matcher-pinyin (regexp candidates)
  (unless (featurep 'pinyinlib) (require 'pinyinlib))
  (let* ((pys (split-string regexp "[ \t]+"))
         (regexp (format ".*%s.*"
                         (mapconcat 'pinyinlib-build-regexp-string pys ".*"))))
    (ivy--switch-buffer-matcher regexp candidates)))

(defun ivy-switch-buffer-by-pinyin ()
  "Switch to another buffer."
  (interactive)
  (unless (featurep 'ivy) (require 'ivy))
  (let ((this-command 'ivy-switch-buffer))
    (ivy-read "Switch to buffer: " 'internal-complete-buffer
              :matcher #'ivy-switch-buffer-matcher-pinyin
              :preselect (buffer-name (other-buffer (current-buffer)))
              :action #'ivy--switch-buffer-action
              :keymap ivy-switch-buffer-map
              :caller 'ivy-switch-buffer)))

You can M-x ivy-switch-buffer-by-pinyin to switch buffer.

The algorithm of pinyinlib is simple. We build a big lookup table to convert the a plain English string into a regular expression containing Chinese characters.

You can apply the same algorithm to other non-English languages.

by Chen Bin at February 26, 2017 04:27 AM

February 22, 2017

GeekPlux

写文章的小技巧

既然是小技巧,那么就不敢班门弄斧,只简单说说自己平时遵循的几条写作原则:

尽量用短句

  • 写作以表达清晰为首要宗旨,所以 尽量用短句 。长句会增加理解成本,尤其是对于中文来说。中文的定语是放在被修饰词前面的,不像英语通常放在后面,用从句很容易扩展。中文你要用从句,还 不如直接另起一句话

而且能一句话说清楚的,不要绕来绕去解释。

  • 在句语句之间 多用连接词 ,一方面表明其联系,一方面增强连贯性。
  • 不要用晦涩难懂的词 。我们不是在写高考作文。
  • 对标点的使用要尽量规范。 尽量少用感叹号。

文章逻辑要清晰

分段、分点说明

众所周知,我们现在看到长篇大论的文章都难以下咽。但如果在文中加几个段落标题,效果就大大不同。
同样的,在遇到排列句的时候,每句单独成行,比一整段要清晰的多。

这些措施一方面是在排版上进行改进,提升了阅读体验。更重要的一方面是这样使你的文章逻辑更加清晰。
写文章和做数学证明题一模一样。每一段都证明一个小问题,下一段的证明,依赖于上一段的结论。 所以每段有个主题,就比胡乱写强很多。

要有重点

无论说话还是写作,都最忌没有重点。如果读完一篇文章,你不知道这篇文章的主旨是什么;读完一段话,你不知道这段话是在讲什么。那这篇文章就太失败了。
用粗体把重点标注出来,很容易吸引眼球。

随时记录

想题目恐怕是世界上最难的事情了。其实日常生活中,很多时刻会有想写的冲动。尤其是发呆的时候,脑子里估计都写了一大段了,但一被打断,又忘得一干二净。等真正要写文章的时候,只能拔剑四顾心茫然。
所以我们 平时一旦有点小思路,就拿手机记下来 。反正手机不离身,记东西很方便。不用记太细,记些关键字也行,整个过程耗时不会超过一分钟。

立即动笔(键盘)

题目定好以后,很多人会觉得,自己才想好了文章的前两点,后面都不知道怎么写。所以还是等彻底想好再提笔吧。
我以前也这样,后来发现自己真是大错特错,因为 写作可以帮助我们更好的思考 。为什么我们脑子里只想到一两点,因为我们的脑子暂时被这两点占满了。你想来想去,思路在脑子里兜圈子,怎么也绕不出这两点。
此时如果你写下来,把这两点「释放」到书面上,脑子放空之后自然能想到三四五点。如果把大脑比作 CPU 的话,文章就是缓存。我们把计算出的数据暂时存放在缓存中,才能更好的计算。

具体为什么写作能让你更好的思考,可参考这篇 书写是为了更好的思考 文章。

综上所述,想到一点就立即动笔。我这篇在写之前,就只想到了目前篇幅的三分之一。


好了,今天要说的就是这么多。另外有的童鞋说昨天的文章有种戛然而止的感觉,确实,主要是因为我在最后一段提了个问句的原因。所以今天这篇就算作昨天那个问题的补充吧。

欢迎关注我的公众号:haoqihaisimao_weixin


本作品采用知识共享 署名-非商业性使用-禁止演绎 4.0 国际 许可协议进行许可。

February 22, 2017 04:55 PM

我获取信息的渠道

互联网迅猛的浪潮抹平了信息的鸿沟,却也让优质的信息变得如沧海一粟。人们常言「信息不对等」,往往在感慨有价值的信息总是被人捷足先登。正因这些信息如此重要,催生了一代又一代、各式各样的信息获取工具。

我阅读工具的演化史

印象中,最早的信息聚合网站,应该是叫「黄页」,已经有 100 多年的历史,马云早期的创业项目就叫「中国黄页」。后来家里的 IE 首页变成了一些门户网站,这时候获取信息主要靠手动点击链接到各大网站。再之后,我接触到了 Google Reader,认识了 RSS,从此开启了一扇新的大门。

之前获取信息的方式是自己主动探索,而现在只需要维护好自己的 RSS 订阅列表,就会有感兴趣的信息源源不断地流进来。犹记得当时我的订阅列表很长,每天点开都是几百上千的未读,快速扫一遍,把感兴趣的随手添加到 pocket(因为 pocket 可以离线阅读),抽碎片时间打开 pocket 阅读,遇到好文章再收藏到 Evernote,这个阅读流一直沿用了一两年。即使后来 Google Reader 被 Google 遗弃我也坚持用 RSS,只是频率比以前低了不少。

再到后来,不少博客写手开始转战到了微信公众号,很多文章用微信直接阅读,体验也挺好,于是阅读公众号成了我当时新的阅读方式。然而好景不长,很快公众平台就被一堆营销号攻占,一些写手为了圈钱,写作质量也大幅下降。我开始慢慢取关公众号,并且意识到了一个问题:大多数的文章都是没有价值的,或者说远没有自己想象的有价值。很多文章都是东拼西凑来的,能引发思考的凤毛麟角。

别人的文章好似二手的知识,我们如果想获取一手的就要追根溯源,看他参考的文献、引用的书籍,与其这样,为什么不直接看书或看论文?想通这点之后,我就把阅读的绝大部分时间放在了看书上,看文章的时间可能一天只有不到半小时。

我现阶段的信息获取方式

我现在的信息渠道主要分四部分:

RSS

RSS 列表主要分两部分,一部分是个人博客,另一部分是少数几个信息聚合类网站和一些期刊、杂志。其中重点看个人博客,由于现在大家的博客更新都很慢,所以一般我一周没看也攒不了几篇。期刊杂志我也会留心,因为有一些和我研究方向——数据可视化有关的。

现在我的 RSS 软件主要用 inoreader,基本功能都有,也提供了夜间模式(对于夜猫子来说很重要),感觉还不错。

Mailing List

邮件列表也算是一种很古老的信息获取方式。我主要用它来做两件事:订阅一些 Google Group 来划划水;订阅一些日报和周报。

之所以选择邮件列表看周报,主要是基于以下两个原因:

  • 本质上日报或周报已经是别人筛选过一遍的文章,所以质量相对一些信息聚合网站也会高一点
  • 日报或周报降低了阅读的频次

日报一天一封,周报一周才一封,保证了你一周只会看这封邮件一次,这大大地节约了浪费在无效信息上的时间。很多网站还提供日报和周报的切换功能,非常方便。

我这里列一些我订阅的,期待大家能在文末评论(或发邮件给我),互通有无。

经常去逛的

人不能两耳不闻窗外事,总得适当的划水。我经常去逛的网站有

总结

任何事都是先做加法,再做减法的过程。我的 RSS 列表和邮件列表都曾经短期内迅速变长,后来又被我精心筛选到寥寥无几,不过我现在还是觉得有点多。

我用的工具可能都比较复古,但是经久不衰的东西才真正不会被淘汰。尤其是邮件,这个几乎是网络一出现就有的东西,现在、未来都会依旧重要。

最后还有个小技巧:文章是否值得读,不取决于题目是否夺人眼球,可能取决于你多个信息渠道中它重复出现的次数。

Update

既然本文是信息获取的渠道,那就不应该局限于阅读方面。除了阅读以为,Podcast 也是我特别喜爱的信息获取方式。我目前一共订阅了 54 个 Podcast,其中大部分是和科技相关,IPN 系列之前搞的很专业,但是现在不太行了。《锵锵三人行》是我逢人推荐的,从小听到大。听 Podcast 可以主观上缩短你的通勤时间,虽然没有阅读那么效率,但作为陶冶情操、增广见闻的手段还是可以的。


本作品采用知识共享 署名-非商业性使用-禁止演绎 4.0 国际 许可协议进行许可。

February 22, 2017 04:05 PM

February 18, 2017

Gih’s Blog

Facebook is boring!

If you can't get away from it, u r boring.

February 18, 2017 12:00 AM

February 16, 2017

包昊军的博客

January 16, 2017

人丑就该多读书

什么是「计算机科学」

什么是「计算机科学」

作为一个学了七年 CS 的学渣, 一直没去了解过 CS 到底意味着什么. Computer Science 与其它 Science 的本质区别在哪? 它与其它 Science 的共同点又在哪? 直到最近读了=Problem Solving with Algorithms and Data Structures= 这书后, 才开始去了解 CS 到底是什么鬼.

1 What is CS?

Computer Science 就是一门专门研究 ProblemsProblem-solving 的学科.

面对一个 问题, 一个 Computer Sciencist 的目标就是找出一个 算法 (即解决方案),明确指示出如何一步步地解决该问题.

当然, 这里还会涉及到 可计算性 的问题(P 与 NP), 面对不可计算的问题, 设计什么样的算法来满足解决问题的目标, 也是一个 CS 的研究内容.

一个计算机科学家在解决问题的过程中, 要多次进行抽象:

  • 首先是对 待解决的问题 进行一次抽象

现实世界里的问题一般都会包含比较多的条件, 把现实世界里的问题明确表示的过程中,不可避免地要进行一次抽象, 去掉无关的边缘条件, 保留该问题最核心的内容.

  • 其实是对 解决方案 的抽象

我们开车的时候, 踩一脚油门车就动了, 我们不需要去了解车是怎么打火的, 发动机是怎么运作的. 这种对解决方案的包装, 展示给最终用户的界面, CS 里叫 Interface, 现在互联网圈叫用户体验.

上述的过程, 可以用下图来表示:

cs.png

2 What is Programming?

编程 就是把 算法 (问题的解决方案) 用某一种编程语言给表述出来的过程.

也就是说, 编程的第一步是 你已有某一问题的解决方案在手 , 没有 算法 就没有程序.

CS 不是研究如何编程的学科, 但编程, 却是一个 Computer Scientist 工作中的一个重要组成. 毕竟, CS 目标是 解决问题 , 编程是把这个 问题的解决方案 给最终实现的基础操作, 重要性可想而知.

综合以上两部分, 总结一下计算机科学家的工作主要是:

  1. 把一个现实问题抽象, 用 Data 来表示
  2. 针对该问题, 设计出相应的解决方案 算法
  3. 把该 算法 用编程语言给表述出来 程序
  4. 计算机运行该 程序 , 从而解决现实问题

把上面的步骤用图形化表示就是:

programming.png

3 Programming Languages

编程语言提供了 Data StructureControl Structure.

3.1 Data Structure

计算机只认识 01.

相信每个 CS 都听过上面这句, 所有 Data 对应到计算机执行指令的时候都是 0 和 1, 但各个编程语言会对这些 01 串进行「包装」, 这样我们在使用这些语言的时候, 就可以从包装后的「砖块」层级来考虑如何把算法用该语言表述出来, 而不需要以 01 串的角度来思考.

各编程语言本身自带的这些对 01 串的「包装」叫做 Primitive Data Types. 可以理解为天赋属性. 语言好坏, 很多时候从这里就可以做一个区分了.

对于复杂的现实世界问题, 所有编程语言都不可能出厂时都「包装」好「砖块」让你砌墙. 所以后来才会有了 Abstract Data Type 的概念. 通过支持自定义类, 你可以自己去「包装」现实世界, 抽象数据类型出来, 想怎么玩就怎么玩.

3.2 Control Structure

有了数据结构, 我们可以表示现实世界中的问题了. 但仍需要更重要的一个东西来把我们的 算法 表述出来. 表述算法的语句就是 Control Structure.

语句控制可以大概分为三类:

  1. Sequential Processing (顺序执行)
  2. Selection (选择分支执行)
  3. Iteration (重复执行)

只要有了这三类语句控制, 我们就可以表述算法了. 剩下的工作就是把 算法 给翻译成这些语句的工作了, 也就是 编程.

4 Which language?

看完了上面的内容, 你还在纠结「哪个编程语言更好」吗?

January 16, 2017 12:00 AM

January 13, 2017

GeekPlux

数据可视化基础——可视化流程

本系列「数据可视化基础」文章共三篇,介绍可视化中最基础、最重要的一些概念、理论。这篇为第一篇,主要介绍可视化流程,另两篇则主讲数据模型视觉编码
原文地址:http://geekplux.com/2017/01/01/basics-of-data-visualization-the-process-model.html

很多人认为数据可视化非常简单,无非是输入几组数据,生成简单的条形图、直线图等等。然而,这未免有点管中窥豹。其实数据可视化大致可分为信息可视化科学可视化可视化分析三大类,刚才提到的简单图表只是信息可视化中最常见的几种。一旦数据量增大,可视化目标改变,可视化系统的复杂度可能就会超出我们的想象。本文中涉及到的可视化流程模型适用于信息可视化和科学可视化。可视化分析的流程模型略有不同,本文暂时不进行讨论。

通用的可视化流程

做任何事都有一个流程,可视化也不例外。

数据可视化流程总览

一图以蔽之。可视化整体可分为三步:分析-处理-生成

一、分析

进行一个可视化任务时,我们首当其冲的当然是要分析,分析又分为三部分:任务、数据、领域。

首先我们要分析我们这次可视化的出发点和目标是什么。我们遇到了什么问题、要展示什么信息、最后想得出什么结论、验证什么假说等等。数据承载的信息多种多样,不同的展示方式会使侧重点有天壤之别。只有想清楚以上问题,才能确定我们要过滤什么数据、用什么算法处理数据、用什么视觉通道编码等等。

其次我们要分析我们的数据,这是至关重要的一步。因为每次可视化任务拿到的数据都是不同的,数据类型、数据结构均有变化,数据的维度也可能成倍增加。抽象的数据类型如何对应现实中的概念,不同的数据类型如何进行视觉编码,这些我们在下一篇数据模型中进行介绍。

最后我们针对不同的领域,也要进行相应的分析。毕竟术业有专攻,可视化的侧重点要跟着领域做出相应的变化。

二、处理

处理可以分为两部分:对数据的处理对视觉编码的处理

1.数据处理

在可视化之前我们要对数据进行数据清洗、数据规范、数据分析。

数据清洗和规范是必不可少的步骤。首先把脏数据、敏感数据过滤掉,其次再剔除和我们目标无关的冗余数据,最后调整数据结构到我们系统能接受的方式。

数据分析中最简单的方法当然是一些基本的统计方法,如求和、中值、方差、期望等等;复杂的方法有数据挖掘种的各种算法,这是又一个领域了,在此不赘述。

最后的可视化结果中我们肯定不可能把所有的数据统统展示出来,于是又涉及到包括标准化(归一化)、采样、离散化、降维、聚类等数据处理的方法,这些概念之后可以单独写篇文章来介绍。

2.设计视觉编码

视觉编码的设计是指如何使用位置、尺寸、灰度值、纹理、色彩、方向、形状等视觉通道,以映射我们要展示的每个数据维度。这里看不懂没关系,第三篇视觉编码中会详细介绍。

三、生成

这个阶段基本上就是把之前的分析和设计付诸实践,在制作或写代码过程中,再不断调整需求、不断地迭代(有可能要重复前两步),最后产出我们想要的结果。

其它可视化流程

线性模型

1990 年 Robert B. Haber 和 David A. McNabb 提出的数据可视化流程已经非常先进:

Haber, R. B. and McNabb, D. A. Visualization idioms: A conceptual model for scientific visualization systems, 1990.

这个处理模型非常先进,整个流程是线性的。它把数据分成五大阶段,分别要经历四个流程,每个过程的输入是上一个过程的输出。从图上看非常直观,很好理解。

嵌套模型

另一种模型是嵌套模型:

Munzner T. A Nested Process Model for Visualization Design and Validation\[J\]. IEEE Transactions on Visualization & Computer Graphics, 2009, 15(6):921-8.

嵌套模型的上半部分基本上就是我们之前说的分析、处理两步,下半部分是对可视化结果的各类验证。本质上就是一个验证加迭代的过程。我们知道很多事都不是一蹴而就的,需要不断迭代,所以有人提出了循环模型。

循环模型

Stolte C, Hanrahan P. Polaris: a system for query, analysis and visualization of multi-dimensional relational databases[C]// IEEE Symposium on Information Visualization. 2000:5-14.

Wijk J J V. The Value of Visualization[C]// Visualization, 2005. VIS 05. IEEE. 2005:11-11.

循环模型的两张图其实都是把线性模型首尾连起来,从图上看一目了然。

目前应用最广的模型

Card S K, Mackinlay J D, Shneiderman B. Readings in information visualization: using vision to think[M]// Readings in information visualization :. Morgan Kaufmann Publishers, 1999:647-650.

对比之前的线性模型,其实也很类似,不过其在最后加入了用户交互的部分,且让每个步骤都变成了循环的。这是目前应用最广的可视化流程模型,后继几乎所有著名的信息可视化系统和工具都支持、兼容这个模型。

总结

纵观以上提到的各类模型,都非常类似,本质上还是离不开分析-处理-生成三步,所以掌握第一张图就可以了,以不变应万变。文中多次提到视觉编码、数据处理等概念,我们在接下来的两篇数据模型视觉编码介绍。欢迎各位在我博客文末留言讨论(如果看不到评论框可能是因为你没有科学上网)。

参考文献

  • [1]陈为 沈则潜 陶煜波. 数据可视化[M]. 电子工业出版社, 2013.
  • [2]浙江大学-陈为、巫英才数据可视化课程
  • [3]Haber, R. B. and McNabb, D. A. Visualization idioms: A conceptual model for scientific visualization systems, 1990.
  • [4]Munzner T. A Nested Process Model for Visualization Design and Validation[J]. IEEE Transactions on Visualization & Computer Graphics, 2009, 15(6):921-8.
  • [5]Stolte C, Hanrahan P. Polaris: a system for query, analysis and visualization of multi-dimensional relational databases[C]// IEEE Symposium on Information Visualization. 2000:5-14.
  • [6]Wijk J J V. The Value of Visualization[C]// Visualization, 2005. VIS 05. IEEE. 2005:11-11.
  • [7]Card S K, Mackinlay J D, Shneiderman B. Readings in information visualization: using vision to think[M]// Readings in information visualization :. Morgan Kaufmann Publishers, 1999:647-650.
  • [8]CSE512 Data Visualization (Spring 2016)

本作品采用知识共享 署名-非商业性使用-禁止演绎 4.0 国际 许可协议进行许可。

January 13, 2017 02:00 PM

January 10, 2017

GeekPlux

数据可视化基础——视觉编码

本系列「数据可视化基础」文章共三篇,介绍可视化中最基础、最重要的一些概念、理论。这篇为第三篇,主要介绍视觉编码,另两篇则主讲可视化流程数据模型,建议从可视化流程看起。
原文地址:http://geekplux.com/2017/01/03/basics-of-data-visualization-visual-encoding-principles.html

终于来到了最后一篇,前两篇的铺垫可能有点长,但是不种苗浇水怎能开枝散叶。可视化编码是可视化中的核心内容,本文会对其进行详细的讲解,尤其是视觉编码视觉通道两个概念,如果其中遇到晦涩之处,不要心急,可囫囵吞枣直接往下看。

什么是视觉编码(visual encoding)

很多人可能看到题目的时候就有这个疑问,到底什么是视觉编码。其实视觉编码很简单,用一句话就能概括:

视觉编码描述的是将数据映射到最终可视化结果上的过程。

这里的可视化结果可能是图片,也可能是一张网页等等。

编码二字,如果说是指设计、映射的过程,那么码呢?其实指的是一些图形符号。

图形能告诉我们什么

在介绍各类图形符号之前,我们先谈谈:图形能告诉我们什么。

仔细观察上方这个简单的图片,你能得得到什么信息?

  1. A B C 是不同的
  2. B 在 A 和 C 的中间
  3. BC 的长度是 AB 的大概两倍

得益于我们视觉系统的强大,这些信息不假思索就能得出。如果把上图想象成一个二维坐标系,则我们可能得出更多的结论。

“Resemblance, order and proportion are the three signfields in graphics.” - Bertin [1]

图形符号和信息间的映射关系使我们能迅速获取信息。所以我们可以把图片看成一组图形符号的组合,这些图形符号中携带了一些信息,我们称作它编码了一些信息。而当人们从这些符号中读取信息时,我们称作我们解码了一些信息。

我们人类解码信息靠的是我们的眼睛、我们的视觉系统。如果说图形符号是编码信息的工具或通道、那么我们的视觉就是解码信息的通道。因此,我们通常把这种图形符号<——>信息<——>视觉系统的对应称作视觉通道

至此算是把视觉通道视觉编码这两个概念讲清楚了。如果一个人说他想用四个通道来编码四个维度的数据,即可以翻译成他想用四种图形符号来对应这份数据表的四个列的信息。

这里举个例子(例子来自于 [2]陈为 沈则潜 陶煜波. 数据可视化[M]. 电子工业出版社, 2013.):

例子来自于陈为 沈则潜 陶煜波《数据可视化》

  • 上图中图 A 表示了三个不同班级的数学平均分,用柱状图表示,柱状图的高度作为一个视觉通道,编码了数学平均分的;柱状,这个形状作为一个视觉通道编码了数学平均分这一属性
  • 图 B 中,我们想在 A 的基础上多展示语文平均分这一项数据(即增加了一个数据维度),则选用点这个形状通道编码这两个属性;点的横坐标编码语文平均分的;点的纵坐标编码数学平均分的
  • 这时候发现图 B 中我们把班级这个数据维度给丢掉了,于是我们可以用颜色这一视觉通道来编码班级这个属性信息,如图 C。
  • 如果我们还想展示班级人数这一信息,则可以用尺寸这一视觉通道来编码,如图 D。

视觉编码中常用的视觉通道

1967 年,Jacques Bertin 初版的《Semiology of Graphics》一书提出了图形符号与信息的对应关系(就是本文上一节的内容),奠定了可视化编码的理论基础。

Bertin J. Semiology of graphics: diagrams[C]// Conference on Computer Networks. 1983.

如上图所示,书中把图形符号分为两种:

  • 位置变量:一般指二维坐标
  • 视网膜变量尺寸数值纹理颜色方向形状

以上基本的图形符号共有 7 种。将其映射到点、线、面之后,就相当于有 21 种编码可用的视觉通道。后来人们还又补充了几种其他的视觉通道:长度面积体积透明度模糊/聚焦动画等,所以可用的视觉通道其实太多了。

而一般一份可视化作品可用到的视觉通道要尽可能得少,因为太多了反而会造成我们视觉系统的混乱,使我们获取信息更难。于是这就涉及到了视觉通道的设计原则。

视觉编码设计原则

这一节其实可以单独再分一篇文章写,因为可视化编码设计实在是复杂:假设我们有 k 个视觉通道,有 n 个数据维度,则一共有 (n+1)^k 种编码方案……从中选出一种最佳方案难度可见一斑。

不过既然本文是讲解视觉编码相关,所以这个章节是逃不掉的,在此提纲挈领讲一下。如果想深入了解,可以阅读参考文献中提到的书籍。

视觉通道的三个性质

上一篇数据模型讲解了可视化中数据分为三类:类别型有序型数值型

  1. 定性性质(或叫分类性质)。适用于类别型数据。比如形状或颜色,这两个视觉通道,非常容易被人眼识别。从一堆正方形中识别出一个三角形,或看万绿丛中一点红,都是我们眼睛拿手好戏。
  2. 定量性质或定序性质。适用于有序型和类别型数据。比如长度、大小特别适合于编码数值/量的大小。
  3. 分组性质。具有相同视觉通道的数据,人眼也能很快识别出来,将其归为一组。

总结一下视觉通道与数据类型的对应关系,如下图所示:

视觉通道与数据类型的对应

视觉编码设计的两大原则

Mackinlay[4] 和 Tversky[5] 分别提出了两套可视化设计的原则,Mackinlay 强调表达性和有效性,Tversky 强调一致性和理解性。两者可以糅合起来:

  1. 表达性、一致性:可视化的结果应该充分表达了数据想要表达的信息,且没有多余。
  2. 有效性、理解性:可视化之后比前一种数据表达方案更加有效,更加容易让人理解。

下面这张图总结了视觉编码面对不同数据类型的优先级:

视觉编码面对不同数据类型的优先级

如果要具体展开每项视觉通道来说,未免有点太繁琐,而且设计可视化编码除了视觉通道还需要考虑:

  • 色彩搭配
  • 交互
  • 美学因素
  • 信息的密度
  • 直观映射、隐喻

以上每一项都很重要,之后有机会再写吧。这个可视化基础系列总算是完结了,文字虽然不多,但是搜索资料、读论文、总结等还是挺累的,希望你能有所收获。欢迎各位在我博客文末留言讨论(如果看不到评论框可能是因为你没有科学上网)。

参考文献


本作品采用知识共享 署名-非商业性使用-禁止演绎 4.0 国际 许可协议进行许可。

January 10, 2017 05:02 AM

数据可视化基础——数据模型

本系列「数据可视化基础」文章共三篇,介绍可视化中最基础、最重要的一些概念、理论。这篇为第二篇,主要介绍数据模型,另两篇则主讲可视化流程视觉编码,建议从可视化流程看起。
原文地址:http://geekplux.com/2017/01/02/basics-of-data-visualization-data-model.html

数据说白了就是一组可定性或可量化的值。随着计算机存储能力的大幅提高,人们对于数据的关注与日俱增,「大数据」一词近几年来也被人们频频提及。而数据可视化的主要任务是将数据转换为易于感知的图形。因此,为了更准确更形象的表达数据,我们需要了解一些数据相关的概念。

数据模型与概念模型

为什么数据能代表我们的世界?要回答这个问题,我们得先了解数据和概念两个模型。

数据模型是一组数字或符号的组合,它包含数据的定义、类型等,可以进行各类数学操作等。概念模型描述的是事物的语义或状态行为等。

现实 => 概念 => 数据

现实世界可以用概念模型来描述,而概念模型又可以用数据模型来描述。经过两层抽象,数据便可以描述我们的现实生活中的方方面面。

数据类型

一个东西具体归为哪一类,取决于我们用什么标准划分,数据亦然。

从数据在计算机中的存储可分为浮点数、整数、字符等;从关系模型的角度分,数据又可以分为实体和关系两类;从数据的结构来分,可以分为一维、二维、三维、多维、时间序列、空间序列、树型、图型等等[3];还有很多的分类方法,我们暂时先不讨论,把关注点聚焦到和数据可视化有关的分类方法上。

按照测量标度来分,数据一般被分为四类:类别型有序型区间型比值型

  • 类别型数据用于区分事物。例如,人可以分为男女,水果能分为苹果香蕉等。
  • 有序型用来表示对象间的顺序关系。例如,我们的身高可以从矮到高,学生的成绩可以从低到高排列等。
  • 区间型用于对象间的定量比较。例如,身高 160cm 与身高 170cm 相差 10cm,而 170cm 与 180cm 也相差 10cm,它们俩的差值是相等的。由此可见,区间型数据基于任意的起始点,所以它只能衡量对象间的相对差别。
  • 比值型用于比较数值间的比例关系。例如,体重 80kg 是体重 40kg 的两倍。

不同的数据类型适用于不同的操作[1]:

数据类型 操作 集合操作 统计操作
类别型 =、≠ 互换元素位置 类别、模式、列联相关
有序型 =、≠、>、< 计算元素单调递增(减) 中值、百分位数
区间型 =、≠、>、<、+、- 元素间线性加(减) 平均值、标准方差、等级相关、积差相关
比值型 =、≠、>、<、+、-、×、÷ 元素间相似度 变异系数

不过,在数据可视化中,我们通常不特别区分区间型和比值型,将其统称为数值型。进而可将数据类型进一步精简为三种:类别型有序型数值型。具体为什么要分为这三类,我相信你看完下一篇视觉编码之后会完全明白。

例子

说了那么多,都比较抽象,不如直接来看个例子。下面是一个简单的数据表,每一行通常称作一条记录,每一列称作一个字段,共有几个字段,则通常就说这份数据有几个维度

id 类型 款式 尺码 销量 年增长
1 男款 上衣 L 50 10%
2 女款 上衣 S 35 5%
3 女款 裤子 M 40 20%
4 男款 上衣 XL 30 15%

对照我们上文的概念,不难判断出上表中:

  • 类型、款式为类别型数据;
  • id、尺码为有序型数据;
  • 销量和年增长为数值型数据。

总结

至此,其实本文的任务就已经完成了。通篇传递的最重要的知识就是数据可视化中的三大数据类型,消化了这点,下一篇视觉编码就能更好的理解。欢迎各位在我博客文末留言讨论(如果看不到评论框可能是因为你没有科学上网)。

参考文献


本作品采用知识共享 署名-非商业性使用-禁止演绎 4.0 国际 许可协议进行许可。

January 10, 2017 05:01 AM

December 31, 2016

Free Mind

2016 无限大

那一年留在膝盖粉红的伤疤 还刻着
那些在学会奔跑疼痛的过程 都过了
好几年终于碰到肩膀的长发 刺刺的
我要的 爱情在距离永恒最靠近那次 毁灭了
像一篇还没懂得分辨谎言的 笨心得

朋友们开始问起我今年准备用哪一首歌的时候,我发现 2016 年又到了尾巴上。现在发在博客上的文章数量越来越少,以至于都快要只剩下年度总结了,不过至少年度总结是要认真写的。我想以一张今年的照片开头,但是好像虽然一年只在一转眼,要用单单一张照片来总结也还是不容易,于是就随便找一次喜欢的经历吧:深秋踩着冰凉的浪在沙滩上疯跑,裤子湿透了,爬满沙子。

接下来是正题,我想这次分主题来回顾一下:正经事(科研、学习、开源),不正经事(游玩、画画、音乐、读书、动漫、博客、电影、锻炼、游戏、感情)。既然已经是假期了,就先说不正经的事吧。

除了例行回国以外,今年欧洲主要跑了西班牙(巴塞罗那、马德里),剩下就是在美国,远一点的去了加州、Minnesota、芝加哥。近处主要就是一些短程的像 Cape Code、女巫镇世界尽头之类的。

西班牙是第一次去,在巴塞罗那刚下飞机,出租车司机就说,你是巴萨球迷吧,我一看你的围巾就知道了。我才意识到自己戴的格兰芬多红黄相间的围巾似乎也是巴萨客场球衣的颜色。不过可惜我并不是球迷,否则也许这次西班牙之旅还会有更多乐趣,不过即便如此也已经很好玩了。

巴塞罗那是海滨城市、旅游城市、气候温暖,市区有些地方走走就能到沙滩上。我去的时候已经是冬天了,水还是很冷的,不过可以看到很多穿着全身保暖服装冲浪的人。网上视频里看到的冲浪都是很酷的乘风破浪,现场倒是能看到更多现实:几秒钟的滑翔之后得费很大劲从岸边游回睡深处,再等待合适的浪头,如此不断重复。简直就是人生的剪影,所以看他们不断地重复倒挺励志的。

在海边的另一个好处就是海鲜。我一个人旅行的时候通常不会太花时间考虑吃什么,不过这次我有幸尝试了一个比较 hardcore 的模式:跟 JJ 一起逛景点(意味着凡是比较有名的景点肯定都不会被错过)然后跟 WZ 一起吃饭(意味着凡是有稀奇古怪的当地食物肯定都会尝试)。

除了足球和美食,巴塞罗那的建筑也很有名,比如圣家堂,已经持续修建了一百多年的大教堂,古典和现代的结合,非常值得一看,官方说是希望在 2026 年高迪诞辰 100 年的时候把它建完,不知道是否能实现。除此之外还有各种高迪的其他建筑,比如米拉之家巴特略之家桂尔宫等等,而且正巧那个时候我正好在读一本关于建筑师的书 The Fountainhead(后面会讲)。走到老城区,会看见各种广场、小巷、游客、街头艺人……总之是闲逛就很舒适的地方。虽然在去之前几乎所有人都警告我注意小偷,但是最后我并没有被偷什么东西,也没有觉得哪里有不太安全的感觉,也许是运气比较好?😛

我比较喜欢的博物馆有毕加索博物馆和加泰罗尼亚艺术博物馆 (Museu Nacional d’Art de Catalunya)。毕加索年轻的时候在这里待过,博物馆里有一系列按照时间顺序索引的他的作品,可以看到他幼年时期绘画水平就已经非常厉害,到后来逐渐成熟,最终走火入魔开始探索一些独特的东西,形成到后来常见的毕加索作品的典型风格。我去的时候还看到一个关于 Las Meninas 的展品集(不记得是不是临时展了)。Las Meninas 是西班牙伟大艺术家 Diego Velázquez 的一幅名画,毕加索非常喜欢,晚年的时候画了 58 幅同人恶搞图解构分析的画作:毕加索版 Las Meninas。在网上可以找到一些图,比如这里这里。不过我也是不太看得懂毕加索后期的画,后来我去马德里在普拉多博物馆看到了 Velázquez 的原画,觉得很有意思。

其他大部分巴塞罗那的博物馆感觉还是以现代艺术为主。跟着 JJ 逛了好几个现代艺术馆,看到不少稀奇古怪的东西,大部分都不太能理解。我比较喜欢的有两个展品,一个是用一整面墙作为投影幕布循环播放猫咪喝牛奶的视频……另外一个是一间屋子里按顺序挂了约一百张从出生不久到 100 岁的人的照片。

期间似乎还有巴萨和皇马的一场球赛,不过显然我没有注意到这个,而是去加泰罗尼亚音乐宫听了柴可夫斯基 D 大调小提琴协奏曲(Alexandra Soumm 演奏、Tomàs Grau 指挥)。不得不感叹欧洲听古典乐好便宜啊。对比起来后面看的 Flemanco 表演就性价比有点低。作为吉他的发源地,我倒挺想在西班牙听一场古典吉他的表演的,在网上听过 Recuerdos de la Alhambra 之后就挺着迷这种吉他轮指多声部的演奏。

过去收到过许多欧洲寄过来的明信片。从一个地方回来之后会翻出来看看上面的文字,自己走过看过之后再和单凭读别人的描述对比起来总是会有很多不一样的理解。我觉得旅行是一件很麻烦的事情,而且一点也不放松,每次一想到要计划出行之类的就会觉得很麻烦,但是当我在飞机上看到连绵的雪山、静止的海洋时,就会觉得跑出来真好!看过越多的地方就越觉得世界好大,特别遗憾的是许许多多的地方大概一辈子最多只有机会去一次。

巴塞罗那之后去了马德里,由于西班牙只给了我 15 天签证,所以就挑了近一点的地方玩。马德里是首都,不过经济上好像不如巴塞罗那好,英语普及率也相应更低一些。感觉法国人是假装不会英文,西班牙人是真心不会英文,包括许多看上去很年轻的人。我对马德里的第一印象是人好多!特别是几个广场以及连接的街道,简直是人挤人,有种到了中国的感觉。不知道是不是碰上了什么节假日还是平时就这样。

实验室有一位在巴塞罗那长大的学姐,我跟她讲我要去马德里的时候她一脸震惊,感觉也许对当地人来说马德里和巴塞罗那的旅游价值可能差别很大,不过马德里的特色是博物馆资源很好。而且奇怪的是街上那么多人却没有在任何一个博物馆碰到有排很长的队。并且许多博物馆都有每周或者甚至每天的免费时间段。所以我买的博物馆通票完全没有什么用处。

如果说巴塞罗那是高迪的天下的话,感觉马德里就是戈雅的地盘。Francisco José de Goya y Lucientes(1746年3月30日-1828年4月15日)一生风格奇异多变,从巴洛克风格到类似表现主义,不断地在探索和自我超越,并且对后期的现实主义、浪漫主义、印象派等都产生了重要影响。

在马德里普拉多博物馆里展出的最有名的两幅戈雅的画就是下面的“裸体的马哈”和“穿衣的马哈”两幅。当然下图右边的描述 1808 年的半岛战争中西班牙反抗人民北法国军队枪杀的 The Third of May 1808 也很重要,对后世的许多名画都产生了影响,比如印象派画家 Manet 的 The Execution of Emperor Maximilian 系列画作,以及毕加索描写西班牙内战的格尔尼卡 (也在马德里,藏于索菲娅王后国家艺术中心博物馆)。在普拉多博物馆还能看到戈雅晚年风格迥异的“Black Paintings”。

除了普拉多之外,在马德里大大小小的各种私人或者公共馆藏里基本上都可以找到戈雅的大作。当然西班牙还有许多其他很厉害的画家。比如 Diego VelázquezJoaquín Sorolla 等等。

我个人觉得马德里除了几个广场、皇宫(有历史上最伟大的弦乐器制造师之一的安东尼奥·斯特拉迪瓦里的一只中提琴、一只大提琴和两个小提琴)、大教堂之类的逛一逛之外,剩下的就是泡博物馆啦。其中主打普拉多博物馆 (Museo Nacional del Prado),是西班牙最大的美术馆,据说 19 世纪末是世界四大美术馆之一。我从 10 点开门一直逛到晚上 7 点,后来临走前又趁免费的时间段又去了一次。实在是很喜欢,不过不能拍照,所以走的时候只好扛了一本跟我其他行李一样重的 The Prado Masterpieces 回来。不过我还是偷偷拍了一些照片啦,感觉就像在玩红白机游戏:观察 NPC 工作人员走来走去的规律,然后找一个周围的工作人员的共同死角区域和时段,迅速果断拍一张。😝

虽然我一般对文艺复兴及以前的画不太感冒,不过似乎普拉多有不少拉斐尔的画。另外有一幅 1490~1510 年间由荷兰画家耶罗尼米斯·博斯所画的人间乐园 (Tuin der lusten)实在是非常超前和震撼的。

普拉多旁边还有一个 Thyssen-Bornemisza Museum 也是非常不错,我去的时候还刚好碰到 Renoir 的主题特展。另外刚才提到的索菲娅王后国家艺术中心博物馆)也在附近,不过主打现代艺术,比较难懂。

除此之外我超喜欢的一个小馆就是 El Museo Sorolla,其实应该就是画家 Sorolla 他家,离主流景点稍微远一点。最开始在普拉多看到他那幅 Boys on the Beach 的时候就很喜欢,但是因为之前并不了解西班牙的艺术所以没有注意到画家是谁。等到我走进他家的时候几乎是眼睛一亮。感觉他对光和影的描绘超级赞,他早期的作品感觉是 Social Realism 风格,主要描绘社会中大众人们的生活,比如那幅藏于普拉多的 And They Still Say Fish are Expensive! 就非常典型。后来他画了很多海滨的场景,特别喜欢他对于湿漉漉的皮肤和水的描绘,总之不知道怎么形容了,反正就是瞬间变粉丝就是啦。

其他的一些我比较喜欢的小馆还有 Real Academia de Bellas Artes de San Fernando 和 Museo Lázaro Galdiano。后者的布景很有意思,在一堆古典艺术品中时常会冷不丁出现一个现代艺术的展品,某个厅的顶画上还有“穿衣的马哈”和一大堆其他人物包括戈雅自己的自画像的混杂,没找到解说不知道是谁画的哈哈。最后偶然在某个小广告上看到 Fundación MAPFRE 有一个野兽派发展历程的特展,也是非常赞。非艺术类的航海博物馆也比较有意思,但是还有其他一些私人展比如 Museo Nacional del Romanticismo、Museo Cerralbo 我就觉得一般般,主要就是看别人当时有多有钱了。

至于美国,湾区去了两次,一次是去开会,时间比较短,印象比较深的是蹭到了神雕侠侣的神雕 winsty 和 wistone 的飞机,于是第一次见到金门大桥居然是俯瞰。第二次是暑假过去实习,呆得比较久。其实来美国这么久今年才第一次去湾区,去之前所听到的关于那边的言论完全是两极分化:有的人说那里是人间天堂,有的人说那里简直没法呆。我自己的感觉是并没有喜欢那里,天气虽然好,但是天天都是晴天也会很单调啊,偶尔也想看看雨发发疯啊。西海岸的自然风光资源似乎比东海岸要丰富很多,但是暑假也比较懒基本上没怎么探索过,所以也没有太大的感觉。其他好像没有什么了,感觉湾区最不缺的就是工作机会,然后其他就什么都没有了。中餐也很多,但是每家店都要开半小时车再排半小时队也是很崩溃的。

总而言之湾区给我的感觉就是超级大农村。以前我觉得界定农村和城市的主要因素是人口,到了美国才知道有“大农村”这种概念。感觉美国社会很多地方也蛮奇怪的,在科研各方面无疑都是世界领先的,但是日常生活中的许多东西都是很陈旧的技术和系统,比如无力吐槽的铁路系统,再比如近两年才开始用的芯片信用卡,刷卡每次都要等几十秒,前几日还被刚来波士顿的朋友吐槽说你们这里的地铁居然要等红灯的。当然很多都可以归结为历史原因,但是最源头的原因是什么呢?是不是这边早期汽车太发达加上太强调 DIY 了,凡是都自家搞定,于是就没有形成很好的社会分工和基础设施之类的。感觉湾区就是这样的典型,大家只是房子一家一家地挨在一起,放眼望去,全是住宅,特别是坐在神雕飞机上往下看的时候。但是除了住宅什么都没有,大家并没有构成一个相互分工合作的有机社会体,出现武器店、杂货店、药品店、客栈、游击士协会、钓公师团广场、公园、博物馆、电影院、音乐会馆之类的,如果哪一天大地震,把所有的房子自己互相隔开十公里,大概并不会对这个群体产生什么影响吧。

Minnesota 也是因为有事情过去的,并没有专门玩,印象就是并不是想象中的冰天雪地(毕竟去的时候是初夏)。之后去了一趟芝加哥,很不错,有点像波士顿的风格,不过密歇根湖和摩天大楼结合在一起的风光感觉是要比波士顿的查尔斯河要更胜一筹的,湖边骑车感觉很舒服。还有 Art Institute of Chicago 也很赞,有很多很厉害的藏画,印象中好像还在里面看到高更、梵高和梵高的耳朵之间的纠葛😃。芝加哥交响乐团也是世界上水准最高的交响乐团之一,不过那个时候我还没有很入交响乐的坑,所以没有去听。

剩下的就是波士顿周边游了。似乎东海岸这边能游览的自然景观确实不多。一次是去 Cape Cod,一次是去女巫镇世界尽头。都是一日往返,比较赶,但是却玩得很开心。在 Cape Cod 的海滩上直接疯掉了,踩着浪跑呀跑,裤子全湿透了。后来又发现一块超级重的木头,不知道从哪里来的,那密度感觉像里面藏了黄金,大家费了九牛二虎之力把它立起来,然后怕会砸伤路人,就又把它推倒了。总之就是做一些很没有意义的事情但是好开心……在女巫镇更北边的 Halibut Point State Park 的时候天黑了(冬天天黑得好早),黑漆漆的树林里树枝们全都摆出很戏剧性的造型,一队人在里面抹黑走,很有魔幻童话的感觉。女巫镇数年前去过一次,记得那个时候还在墓地了读了好久的墓碑。

好吧游记先到此打住。现在本科生们已经结束了期末考,进入学生假期,学校周边就一下子冷清起来,临近年末,甚至各种店铺也都陆续关门了。我晃悠到 Food Court 去觅食,结果连 Food Court 也不开了,于是顺势进了旁边的 Coop 书店,看到一本白色封皮很漂亮的书叫做 Absolutely on Music,讲一个作家和一个音乐家关于音乐的对话。在排队等待付款的时候上网查了一下亚马逊上的价格,果然网上买要便宜十块,不过节假日快递寄过来不知道都要多少天以后了,在物流这方面美国跟国内发达地区比差距还是相当大的,不过我想说的是在亚马逊页面上看到推荐了许多其他很眼熟的书,才发现原来作者是村上春树。我之前很少看他的书,不过这本我一口气在年末结束之内看完了,结果成了今年唯一看完的一本实体书。

数了一下,2016 年我读了共计 29 本中文书,10 本英文书,3 本日文书,几乎全都是以电子书的形式阅读的(对比起来去年是 42 本中文书,2 本英文书,1 本日文书,其中有不少是实体书)。今年我按照自己的喜好来大致排列一下,并尽量给一个豆瓣链接,以方便感兴趣的朋友。当然很多书(特别是翻译版)都是有很多不同版本的,我并没有特别深究。部分简介可能会有轻微剧透,我尽量不透露会影响阅读乐趣的关键剧情。实际上我感觉除了某些剧情有重要伏笔剧透以后就没法看的书之外,有很多写得好的书读起来的趣味性并不会因为剧透而受影响,特别是有些值得读好几遍的书 🙂:

非专业类

  • 月亮与六便士》:英国作家毛姆笔下的名作。很多人即使没有看过小说,应该也听过这个 quote:If you look on the ground in search of a sixpence, you don’t look up, and so miss the moon。据说这是小说标题的解读,因为小说的内容跟月亮或者便士都没有什么具体的关系。小说的主角是一个画家,其原型是著名的后印象派画家高更。⑴ 我个人很喜欢这本小说,不论行文还是人物的塑造,都非常厉害,当然我也喜欢文中的主角,简单来讲就是一个纯粹的、义无反顾的、没心没肺的人。当然“喜欢”并不代表我赞成或者不赞成主角的处事方式,我觉得自己是不可能做得到那样的义无反顾的,所以就不用费力去考虑赞成与否了。实际上我觉得自己的性格也许恰恰相反,太拖泥带水,一直将自己 trap 在许许多多难以舍弃的东西里吧。⑵ 客观来讲,处在社会中,人们往往不得不做出各种各样的妥协,要做一个纯粹的人,不仅自己会付出很大的代价,身边在意你的人也会做出很大的牺牲,最终往往会造成各种争议吧。一方面这是一个很大很难的话题,另一方面其实也是与我们息息相关,人要如何对待自己和身边的人?人与社会是什么样的关系?所谓朋友是什么? ⑶ 不知道现实中的高更是什么样一个人,他的画非常容易被辨认出来,在波士顿的 MFA 有他的名作《Where Do We Come From? What Are We? Where Are We Going?》。之前在布拉格的慕夏画室也看到过高更在那里弹琴的照片;在芝加哥艺术博物馆还看到一段关于高更与梵高的轶事:大概 1888 年梵高在法国南部租了一个小屋,邀请高更过去一起共创美好艺术新天地,在热切地期待高更到来的过程中,梵高灵感大发,为了装饰 studio,创作了许多后世名作,后来高更加入,他们还互赠自画像之类的,然而两个月之后两人关系破裂,高更离开,梵高精神失常,割掉了自己的左耳,而高更则好像什么事都没发生一样,还把梵高送他的自画像拿去卖了…这样的故事好像确实很像是《月亮与六便士》里有可能会发生的情景哈哈。
  • Absolutely on Music:村上春树对于小澤征爾(著名指挥家,在波士顿交响乐团担任了近 30 年音乐总监)的关于古典音乐的访谈,或者更像是朋友之间的讨论。村上春树并不是音乐家,但是他从小就听很多古典和爵士,通过小澤征爾的女儿(作家)相识,后来两人决定做这样一个访谈项目。全书主要以对话的形式呈现(没有任何乐谱出现),主要话题围绕协奏曲、交响乐、歌剧和室内乐展开,除了小澤征爾对音乐的看法和态度,还涉及到其他一些音乐人,例如古尔德伯恩斯坦马勒卡拉扬等等。有一些趣闻轶事(比如伯恩斯坦在一次和古尔德合作勃拉姆斯第一钢琴协奏曲的时候对观众说,接下来你们将要听到的是古尔德对于勃拉姆斯的解读,虽然我并不持相同的观点。)以及一些小知识(比如协奏曲中独奏演奏家和指挥谁做主导、指挥和乐团之间如何沟通以及都沟通些什么,指挥在一个交响乐团中到底起到什么样的作用,像古典音乐这样几乎所有东西都写在乐谱里了那指挥还如何做不同的“解读”),感觉更多的是两个人(一个音乐家和一个外行音乐爱好者)对于音乐的一些理解。

    我觉得特别是像古典音乐这种要真正理解还有一定门槛的东西,能有机会听到真正的音乐人自己对它的理解和看法还是挺难得的,感觉通常他们都不会太 care 或者没有时间与大众来交流这些东西,而本书写作的契机也正是小澤征爾因为动手术之后需要进行身体恢复不能继续指挥,突然有了一大段空闲的时间,才让村上春树抓住了这个机会两人聊了很多。我觉得这比那些“二手”的来自音乐评论家们的解读或者甚至是知乎上的评论更加珍贵一些。

  • 光明王》:蒸汽朋克与科幻魔幻的结合,非常有意思的设定,简单来讲所谓的“神”就是在基因或者科学技术上占有绝对优势的一部分人相对于普通人的存在。类似的设定在其他一些作品中也出现过,比如阿莫西夫的《基地》系列中有讲到在宇宙荒漠建立新的文明的过程中,先进的科技以宗教的形式存在和传播;而卡德的《天贼》虽然没有很强调,但是也基于这样的大背景下讲述了新文明的建立。如果你处在这样的“神”的位置,而“神性”是可以通过教育和知识传播的方式授予“凡人”的,那你是会严格限制神性的传播,建立阶级统治,还是会广播神性,让世界平等?如果你要传播神性,又需要怎样具体去实施呢?《天贼》里有一段给处于蒙昧状态的人类社会灌输“民主”思想的尝试,而这里的光明王则用了完全不同的方式。
  • 天贼》:《安德的游戏》的作者卡德的科幻处女作。前半和后半故事风格迥异,让人不禁觉得是不是两部完全不相干的作品,然而事实上只是两种不同的人类社会的形态。书中有许多有趣的内容,比如这本写于 1979 年的科幻小说成功预测了未来社会里大红大紫的“直播”文化,而关于新文明的成长和建立的探讨在关于《光明王》的讨论中已经提到了。看完的时候正好碰到《文明 VI》的发布,当时就很有兴致想看一看这个游戏关于一个文明的建立和发展是如何解读的,不过一直都没有时间去玩。

  • The Fountainhead》:Ayn Rand 在 1943 年写的一本关于建筑师的小说,似乎 Ayn 本身所持的观点和这本书都是非常有争议的存在,而且这书的影响也一直持续存在,根据 Wikipedia 的资料,到目前为止在全世界卖出了 650 万册。主要争议的地方在于书中所持的对于“个人主义”和“集体主义”或者说“利他主义”之间的观点。当然并不是讲政治,而是讲人自己的本性。对“自我中心”的刻画以及高度理想化人物的描绘,导致它和上面的《月亮与六便士》很相似。事实上正是在我看完《月亮》之后 HY 给我推荐了 Ayn。当然实际上是很不一样的书,最明显的就是一个很短,一个很长很长……而且我觉得两者所要表达的观点也并不一样,《月亮》讲自我价值的实现,而《源泉》虽然是讲自我主义,要表达的却是人类社会整体价值的实现。

    我觉得 Ayn 的观点是,最大的“个人主义”就是完全无视别人,只在乎自己的价值实现,比如书中主角设计建造廉价出租房的目的是最大限度地实践自己的极简主义建筑风格,同浮华的古典建筑形成对比,每一块砖的存在都是有其功能上的作用和意义;相反他完全不在乎贫穷的人是否租得起房子之类的事情。而最大的“利他主义”则是凡事都以别人的 concern 为基础。

    个人主义是社会进步的基石,而利他主义是导致社会消亡的毒药。咋一看好像搞反了,但是其实也挺有道理。在自己所喜欢和追求的东西上面,人们才会拼尽全力,才会创新和突破。另一方面,极端的“利他主义”则完全不知道自己真正想要的是什么,一切都取决于“他人”的看法,他们并不会去真正认真做一件事情,而是只是让自己在别人眼中看起来好像在认真做一件事情。引用一些句子:“A truly selfish man cannot be affected by the approval of others. He doesn’t need it.”,“I think the only cardinal evil on earth is that of placing your prime concern within other men…. If one doesn’t respect oneself one can have neither love nor respect for others.”,而相反的一面,“They have no concern for facts, ideas, work. They’re concerned only with people. They don’t ask: ‘Is this true?’ They ask: ‘Is this what others think is true?’”,“Not to do, but to give the impression of doing. Not creation, but show.”,“What would happen to the world without those who do, think, work, produce?”,“He’s not really struggling even for material wealth, but for the second-hander’s delusion—prestige.”,“What was his aim in life? Greatness—in other people’s eyes. Fame, admiration, envy—all that which comes from others. Others dictated his convictions, which he did not hold, but he was satisfied that others believed he held them. Others were his motive power and his prime concern. He didn’t want to be great, but to be thought great. He didn’t want to build, but to be admired as a builder. He borrowed from others in order to make an impression on others. There’s your actual selflessness. It’s his ego that he’s betrayed and given up. But everybody calls him selfish.”。

    语言上来讲还是很有趣的一本书,大概书中的人物性格太强硬怪异了,所以有些地方经常看到忍不住笑出来。不过真的有点太长了,实体书好像是有 1000 页左右。并且虽然知道是故意要描写单纯的典型人物,但是有些地方还是觉得有点怪怪的啦。至于书中的哲学,我比较同意个人主义会是社会前进的动力或者甚至是主要动力,更确切的说,我觉得社会既不可能是完全建立在所有人都为他人考虑的基础上,也不可能建立在所有人都是 adversarial,即便不利己也要损人,最后达到的一种平衡之上;而是到达一种个人利益和集体利益有重叠的状态所达到的平衡点,因为若非如此,大概人类最初就不会群居并形成社会吧。最后,关于书中所说的 second-handed life,固然是不太好,但是凡事都以自己独立的想法为前提好像也并非就是好事,毕竟并不是每个人都能总是能像小说中的人物那样总是能迅速洞察到事情的本质。

    最后感叹一下建筑真是比其他艺术更难的一个职业啊。像画画啥的,如果能有办法糊口不饿死,再搞点钱买画材的话,基本上还是可以自由自在地作画的。而建筑除了等 commission 之外,完全没有办法,空有一身本事,没人找你修楼的话连自娱自乐都办不到。

  • 无人生还》: 阿加莎·克里斯蒂的经典侦探小说之一,主打逻辑和人性。15 年底英国拍摄的三集同名电视剧也挺不错的,可以一起享用。
  • 东方快车谋杀案》:阿加莎·克里斯蒂的另一本经典。侦探之外,同时也在讨论一些诸如公正之类的大概永远也没有正确答案的问题。总之好像稍微多说一些就有可能会剧透了。
  • Harry potter and the cursed child》:JK 罗琳的新作,一个舞台剧的剧本,故事主要发生在哈利波特他们下一代的身上,当然哈利他们也有挺多戏份。实际上很大一部分笔墨描写的是哈利父子之间的爱与矛盾、误解、冲突与无助。有一个很有名的老爹,也许对一些人来说是一件幸运的事,而对另一些人却不是。总而言之感觉是很细致的描写。不过像这种“外传”或者“前传”、“后传”之类的作品里最让我喜欢的还是视角的改变:还是同样的 Harry, Ron 和 Hermione,但是当视角转换,他们变成别人的故事里的辅助角色的时候,我们看到很不一样的画面,并不是说作者改变了她笔下的角色,而是展现了事物的多面性,还有我们自己的 cognitive bias。实际上类似的事情在现实生活中也时常发生,有一些事件,时隔很久之后再以一种更加客观的旁观者的身份回忆起来,就会发现和当时其中时所看到的东西和现在很不一样。
  • 安德的游戏》:主线似乎是讲主角 Ender 如何在残酷的环境下生存并战胜困难,他所处的战斗学校是地球联军为了对抗虫族而建立的针对神童们说进行的心理和战术训练基地。而忽隐忽现的暗线则是讲述两个无法互相理解的文明之间相遇之后的种种。这似乎也是一个经常被讨论的话题,比如《三体》系列里有著名的“黑暗森林法则”,再比如《Stories of your life and others》里两个文明在文字和语言上的沟通和尝试。总之感觉很好看,故事发展也很紧凑。不过虽然已经习惯了日本动漫里中学生拯救世界的设定,但是在科幻小说里看到神童们作为主角却还是有点不习惯。
  • Stories of your life and others》:这是一个短篇科幻故事集,同名的短篇在 16 年底被拍成了电影,叫做“Arrival”,电影我就不多评价了,感觉在一些基本剧情上做了改动,变得更像伦理片而不是科幻片了。但是电影的生动形象无疑对于理解文中要表达的意思是很有帮助的。除了同名短篇以外,还有另一个叫做《liking what you see: a documentary》的短篇我比较喜欢,讲的是“看脸的社会”发展到最后的样子,以及人们采取的措施和所带来的一些其他问题,另外行文以 documentary 的形式给出,也比较有意思。其他的几个短篇也并不难看了,我想也许对短篇的要求会更高一些,除非是非常拍案叫绝的作品,否则好像对我来说留给读者大量的想象空间似乎并没有看作者精妙地展开故事发展和人物个性来得享受一些。毕竟如果不是写得非常好,要勾起读者足够的想象力也是蛮难的。
  • 秘密》:东野圭吾的小说,这本书相对于推理,更偏伦理和感情;相对于黑暗系,应该也更偏治愈系一点。感觉对于父女和夫妻之间的感情描写得很细致入微。不过缺点就是主线逻辑有点模糊不清,经常看着看着有点搞不清楚到底要讲什么。据说关于最终的“秘密”可以有三种不同的解读方式,在读完之后如果没有理解的话可以去网上搜一下解读。

  • いちご同盟》:基本上是为了《四月是你的谎言》而去看的这本书,顺便发现了在亚马逊上买电子书这一方便的日文书渠道。在《四月》中有一处宮園薰问有馬公生要不要跟她一起殉情。公生困扰了很久之后后来对薰说,君は王女さまじゃないよ。カヌレが好きなケーキ屋さんの子で小説のヒロインじゃない。僕はラヴェルなんて絶対弾かない。(你并不是公主殿下,也不是什么小说的女主角,只是一个喜欢吃カヌレ的蛋糕店家的女儿。我是绝对不会弹拉威尔的。)这些全都是来自《一五同盟》里的剧情。动漫里有一段渡亮太给住院的宮園薰从学校借了很多书来,其中有一本就是《一五同盟》,她在书里的借阅记录里看到了公生的名字,知道他看过了这里的故事,才跟他说那句“あたしと心中しない”(和我一起殉情吗)。看书的话,会发现《四月》跟《一五同盟》在剧情上有很多相似和关联的地方,同时也都是基于钢琴展开。总之作为《四月》与钢琴的粉丝,我还是挺喜欢这本书的。
  • 《旅人-柏舟》《旅人-怀人》:看过好多遍了,《旅人》一直是九州奇幻里我非常喜欢的一个系列。不过现在回想起来,好像前几次翻出来看都是一样在自己比较脆弱的时候。也许是作为类似心灵避难所一样的存在吧。
  • 遗落的南境》三部曲:击败《三体》拿走“星云奖”的小说,感觉是有点另类的科幻,有许多诡异和悬疑的地方比较吸引人,第一部比较好看。第二部似乎是想讲述权利斗争,但是讲得不好。第三部就不知道要讲什么了,仿佛是要把前面的一些线索联系起来,但是仍旧讲得不明不白,感觉要制造一个开放式结局,但是我感觉有些虚幻缥缈。比如像《The Fountainhead》那样的故事,讲到最后它想要表达的东西已经全部说出来了,真正故事的结局已经不太重要了,倒是比较适合开放式结局的,但是这个就完全云里雾里。我觉得大概在第一册的时候完结是比较好的。
  • 巨人的陨落》三部曲:看到评分超高特地买来读的,不知道什么原因一直以为是魔幻巨著,第一册看完一半的时候才反应过来,原来是讲第一次世界大战的(可见我历史也是很差的)。情节和人物的编制都挺不错的,有种看武侠小说的感觉,特别是武侠里特有的一点就是:但凡剧情需要,角色们无论走到哪个国家,都是随时可能碰到的。书中描写的一战的触发和扩大都是由一些似乎不至于发展到世界大战这种规模的事件,但是历史上应该是有一些更加本质的冲突和利益吧,资本啊资源之类的。
  • 大秦帝国(第二部):国命纵横》:上下两册,主要讲苏秦张仪合纵连横的故事。那个时候的国家制度还蛮有意思的,在一个国家任职,还能在另一个国家兼职,像“挂六国相印”这样的事情,恐怕在现在的各个公司之间,也是很难做到。但是那个时代的国家之间的竞争似乎确实更像当代的公司之间的竞争,更多的是实力、技术、策略之类的良性竞争,而不是像历史上后来的许多战争那样是不同的民族或者宗教之间的的冲突,目的不是要让自己变得更好,而主要是要把别人(外族、异端)都弄死。
  • 大秦帝国(第三部):金戈铁马》:主要讲范雎的远交近攻和白起的故事。看到了好多之前听说过的故事,远交近攻、胡服骑射、负荆请罪、纸上谈兵……还有“锅盔”的来历。就内容和故事来讲感觉是前三部中最好看的一部。不过大秦帝国的坑真是好大,后面还有很多部,暂时不想看了。
  • 走れメロス》:奔跑吧,梅乐斯是太宰治的短篇小说。讲梅乐斯必须要一路奔跑在一定时限内赶到一个地方才能救他友人的性命,这中间的赶路的过程。很短,但是感觉对人心的描写还是蛮细致入微的。等待与被等待,究竟哪一方是更让人难耐的呢?
  • 时间简史(第一推动丛书)》:同那本很流行的画册相比这个有更多的文字和细节。不过这种科普看多了就觉得好像并没有什么颠覆三观的东西,大概翻来覆去都还是那些内容吧。其实看到最后还是蛮伤感的,像物理之类的自然科学到头来都不能证明什么,只能等着被证伪。不过可证伪性大概也是自然科学最重要的一个特征了——比如跟宗教的东西比起来。其实刚来美国头几年也还会对诸如“证明上帝存在”之类的讨论活动之类的有些兴趣,但是后来发现很多时候宗教的东西根本就是不能证伪的,如果你不避讳地问及一些敏感问题,大概最终会得到“不要去揣摩神的想法”之类的模板回复,所以后来我就对宗教完全失去兴趣了。
  • 玛格丽特小镇》:故事以一种很有意思的行文方式来描写女性的内心世界,从童年到青年,到叛逆期、成年再到老去。当然,书的主题是关于爱情。什么是爱情,答案隐藏在故事之中,当然这大概原本就是难以确切地回答的问题,所以只能这样了。
  • 音乐的历史》:号称是翻译自房龙的书,但是我找了一下并没有找到英文的版本或者在房龙的作品列表里找到类似的书。总之是讲音乐的发展和一些重要人物的经历介绍。感觉有一些故事和《古典音乐一本通》里有些出入,果然历史上的东西还是大家各自众说纷纭吧。
  • 古典音乐三巨头的账本》:比较有意思的一本小册子,分析了过去知名古典音乐家的职业状态。简单地来说就是如果你是名人的话还是非常有钱的。
  • 古典音乐一本通》:古典音乐家的简介和八卦合集,看完的感觉是伟大的作曲家基本上是:终身未娶,或者娶了一个很彪悍的老婆苦不堪言;英年早逝,或者晚年精神病,或者耳聋眼瞎手残之类的……书的话还行吧,名家介绍感觉主要还是集中在八卦轶事上,后面的作品赏析由于没有给标准的作品编号,对于外行来说查找起来也比较麻烦。
  • 《北京折叠》:今年雨果奖入围的中国短篇科幻。还不错,就是篇幅实在太短了,感觉故事完全没有展开。越来越觉得“越短越难写”这句话好像真是挺有道理的了。
  • 顾城作品精选》:大概是我看的第一本诗集,当时好像是在别处看到一段他的诗,“草在结它的种子,风在摇它的叶子。我们站着,不说话,就十分美好。”才去找来看的。不过事实证明我还是不太能看得懂诗的。
  • 心が叫びたがってるんだ》:同名动画片“心灵想要大声呼喊”的动画脚本改编的小说,之前本来以为是原著小说,看完之后发现几乎跟动画片一模一样,只稍微补全了一点点细节……才意识到是先有动画片。总的来说这种音乐占很重要部分内容的故事,无声的小说的表现力就不如动画片了,而且动画片的叙事线条也更喜欢一些。【言いたいことを聞いてくれる人がいる 。ちゃんと答えてくれる人がいる 。】确实是一件幸福的事情。
  • 夏与冬的奏鸣曲》:据说是备受争议的推理小说。我完全没看懂,不论情节还是人物都不是很喜欢,也没搞清楚要表达的东西。
  • 光与暗的生灵》:《光明王》作者的另一部经典,不过我完全不喜欢看这本,草草翻完了。
  • 英伦魔法师》第一册:评价很高,而且有尼尔盖曼作序,似乎是很厉害的样子,但是看起来情节非常拖沓,很多看起来无关紧要的事情和人物,猜测后期也许会再次联系在一起,但是由于没有什么扣人心弦的进展,所以看了第一册也没有想要看后面的了。“英伦”也许是很贴切,描写的人物和故事浓郁的迂腐英国风。

专业类

  • Effective Modern C++》:原 《Effective C++》的作者写的书,讲了 C++11 和 14 中的一些新的东西的用法和注意事项,有些地方讲得过于细节了,不过整体来讲是很不错的书。算是目前了解 C++ 新标准的一个很好的渠道。
  • A Tour of C++》:不错的小册子,用来复习 C++ 以及了解一下近几年新标准加入的一些新功能。
  • 《Practical C++ Metaprogramming》:O’Reilly 出的 50 页的小册子,主要讲 C++ 11 和 14 新标准改进之后的模板元编程。虽然新标准改进了地方,避免了各种奇怪的使用方式,但是模版元编程果然还是像一个晦涩的函数编程语言 dsl 小子集,尖括号代替圆括号,struct 代替 function,类型代替值。还是一套完整的 macro 系统更让人舒坦啊,不知道 C++ 还要统治系统编程多少年。
  • 《Why Rust》:O’Reilly 出的 60 页小册子,介绍了 Rust 管理内存的三大原则,ownership 的基本概念以及优点:避免内存错误,简化(?)多线程程序。Rust 的 ownership 系统作为现代编程语言中的独一无二的存在,固然是很有意思并且也很有野心,但是肯定也有它的 trade-off,如果书再多花一点篇幅讲一下 ownership system 带来的“坏处”,比如什么样的程序会变得难以编写或者非常麻烦之类的,就更诚恳啦。
  • 《C++ Today》:O’Reilly 出的 70 页的小册子,简要介绍了 C++ 标准化发展的历程和一些最近几年加入标准的新东西,还有对 C++ 17 的一点展望。
  • The Complete Idiot’s Guide to Music Theory》:书的内容覆盖比想象中全面,不过组织得一般吧,感觉更像是罗列表格和知识点的 reference book。

记得好像有句什么名言讲的是身体和灵魂总要有一个在路上,好像对于我来说经常是要么都在路上,要么都在家😅。因为主要的阅读时间都是在 travel 或者 commute 的过程中,其他就是吃饭的时候(我以前是比较反感吃饭的时候拿着手机看的,但是现在也渐渐养成了这样的习惯)或者是晚上失眠的时候(有时候是反过来,因为不小心看书看太晚了于是失眠了)之类的。当然偶尔也会特地抽出一段时间找个地方坐下来看书,不过一般是被情节吸引住了不得不往下看的时候。总而言之虽然我还是觉得看书的体验是实体书大于 Kindle 大于手机,但是琐碎和比较随机的阅读时机导致我虽然买了一些实体书,但是看的机会很少。比如我在三番很有名的城市之光书店买的一本《美国众神》,原本是很好看的,但是书很大一本,我不可能随身带着,基本上只有碰到回家吃晚饭的时候才有机会看,所以至今只看完了大概一半左右。

至于 Kindle,虽然比手机更不坏眼睛,但是由于现在电子书各大平台锁定用户,DRM 互不兼容,导致也基本上没法用。我看的书基本都是在多看平台上,从排版的质量上来讲,多看无疑是最好的,豆瓣正在改进但是差距也还很明显,而亚马逊的很多电子书基本上感觉就是用脚本转一下,连目录跳转都没有,还有一些直接有乱码,数量很多,质量却很差,经常准备买书的时候看到有更便宜的 Kindle 版都会点开预览看一下,然后吓得赶紧关掉。相比之下苹果的 iBook 最近好像很侧重在排版上,之前出了一款哈利波特的电子书,书里的插图就跟电影里的报纸一样是可以动的,非常有趣。多看的手机阅读器也很好用,并且对英文书的支持也很好(包括 hyphenation 和非常方便的词典支持,比 Kindle App 好用多了)。但是多看的 Kindle 系统对于现在比较新一点的 Kindle 设备都刷不了了,而且他家的书也没法去掉 DRM,所以目前我也只能在手机里看。希望电子书早日出现一个统一开放的版权解决方案,好让各个平台都能通用。

实际上关于为什么要看书,我跟朋友之间有过一些讨论。议题是“看书并不能起到放松和休息的作用”,特别是在看外文书的时候,cognitive load 有时会会相当高,更不用说有时候看上瘾了到深夜才睡觉导致接下来几天精神状态很差之类的。想来想去发现我没法反驳这一点,但是后来我发现自己看书(以及其他大部分兴趣相关的事情)的目的并不是“放松或者休息”,而是满足自己的某种“欲望”、好奇心之类的东西。

同样的理由也适用于旅行,特别是一个人背包旅行,简直就是一个不小的课题,要查阅景点、制定行程、订机票车票旅店、到了现场还会各种暴走到精疲力尽、在青旅因为室友鼾声如雷而睡不着、因为语言不通而迷路等等,总而言之是从熟悉的环境到未知的环境,有可能会碰到各种各样大的或者小的突发事件需要处理,如果是为了放松,简直找不到任何走出家门口的理由。

相关的另一个话题是关于“遗忘”,虽然没有专门跟别人讨论过,但是我有时会想这个问题,随着年龄的增长(也有可能是随着大脑内存的占用量增加),忘记东西的概率似乎越来越大,看过的书也好走过的地方也好,有时候会发现对一些细节变得完全没有印象。如果都会很快遗忘,那么是否还有必要去追寻呢?关于这个问题我并没有什么很好的答案,不过这也并没有让我很烦恼,因为这好像是在问,如果最终要死亡,是否还有必要努力地去生活一样吧?

某张照片 追究也太傻
某个情节 又何需惊讶
某些白发 真实的表达
某个以后 都是无限大
某个路人 又哭着回家
某个电台 用情歌惩罚
某次晚餐 只听见刀叉
某个转角 真的无限大

既然成功地生硬地插入了一段歌词,那么就来讲讲音乐吧。其实我一早想讲的,因为这基本上是这一年自己的一个重要改变。以往音乐在我生活中分量并不重,我本来是挺喜欢听歌的,比如学日语的最主要的原因就是为了日文歌。不过几年前开始长期耳鸣之后就听得比较少了。而 2016 年的重大改变就是我入了古典音乐这个坑,并且开始学习钢琴。

有时候我觉得我可以说在 2016 年谈了两场恋爱,一次非常非常长,一次非常非常短。长的这次就是跟古典音乐或者说是钢琴。为什么说是恋爱呢?因为我想来想去觉得只有这个形容最恰当啊,或者至少是可以称作是“单恋”的。一切都来得很突然,几乎是毫无征兆,在这之前所谓“古典音乐”在我的概念里跟“轻音乐”是混为一谈的,对音乐本身的认识也几乎为零,五线谱自不用说,给我简谱我也是唱不出来的,什么和弦、十二平均律之类的也都完全没概念,甚至连贝多芬和萧邦谁是谁都分不清,听得最多的古典乐大概就是卡农变奏曲的 George Winston 编排版本(虽然我之前并不知道这是古典乐以及“卡农”是什么意思)和月光曲(根据小学语文课本,我一直以为自己听的是贝多芬的月光奏鸣曲,直到今年我才意识到那其实是德彪西的月光 Clair de Lune)。

导火线无疑是一部叫做《四月是你的谎言》的动画片。我不知道是不是所有人在碰到有好感或者有种相见恨晚的感觉的人的时候都会去试图搜刮所有的蛛丝马迹想要去努力地了解和认识这个人。我感觉自己那个时候就是这种状态:我去找各种关于钢琴、古典音乐相关的书(见上面提到的那些,一开始的时候看不太懂乐理相关的东西,于是就看历史八卦类的)、电影、纪录片和动画片(心灵想要大声呼喊、交响情人梦第一季第二季第三季坂道上的阿波罗The Highest Level: Lang Lang with the Berliner Philharmoniker莫扎特传不能说的秘密钢琴家Le concert海上钢琴师等等),还会去下载各种相关的 app、去听学校的钢琴 recital 和乐团的表演、去 Youtube 上看乐器演奏的视频、甚至还去 Coursera 上看相关的课程(强烈推荐这位耶鲁的老爷爷开的 Introduction to Classical Music,穿插各种多媒体教学工具和实际演示,一点也不枯燥,他之前的一个叫做 Listening to Music with Craig Wright 也在 Youtube 上可以看到,更侧重音乐本身的角度而不是历史线条,此外比 Coursera 上的版本讲得稍微更深一些)。总之就是病入膏肓的即视感。而且有时候在从学校琴房回家的路上会走着走着就兴奋地跑起来跳起来,或者走路走着走着停下来了,因为试图用三拍同步双脚步子的二拍把自己搞晕卡死了(我节奏感不好),还有晚上会一直想着它翻来覆去睡不着,这难道不就是热恋中的迷失了心智的人吗?😝

几乎同时下的决定就是学习钢琴。对于一个毫无任何音乐和乐理知识,也不会任何乐器的人来说,确实有些突然。不过我当时想啊,电脑键盘上那么多键我都按熟了,钢琴不是差不多的吗?又不是像弦乐乐器那么复杂,只要按对键了就好了。当然事实证明自己当时的想法是多么 naive,不过幸好自己那个时候很 naive 啊。我先是借了 XH 的电子琴尝试了一下,当时碰巧碰到一个叫做 Yousician 的 app,可以通过识别你弹的音和节奏来帮助你学习和入门,我试了一下感觉非常好,就顺势买了一年的会员(当然后来我发现这个 app 主要是辅助作用,只靠它还是没法学习钢琴的),又花了两天了解了一下电子琴、midi 键盘和电钢琴的区别之后毅然(以给自己的生日礼物为借口)买了一个 Casio Privia PX-160 电钢琴(当然后来我其实主要是在学校琴房的 acoustic piano 上练习)……总而言之似乎是一连串的冲动而不太明智的决定,但是好处在于这样我就骑虎难下了。其实这样说也不太恰当,因为我中途并没有犹豫过,唯一的障碍大概就是耳鸣会加重(好听的音乐听久了也会加重,所以并不是因为我弹得太难听了,虽然我也确实弹得不好听哈哈哈)。在咨询过 XZ 之后找了老师 3 月 8 日第一次开始上课,也找到了学校的琴房。

除去暑假不在的几个月,到现在为止加在一起大概学了有六个月左右了吧。跟我之前(什么都不会)相比当然是进步很大,不过还有很长的路要走啦。关于这件事情其实在开始之前我就想的很清楚:到入门或者说能比较容易弹自己想弹的曲子为止可能需要数年或者更久的时间(毕竟我画了这么多年的画也还没有做到能随心所欲地画自己想表达的东西),但是我觉得“时间”正是大人相对于小孩的一个优势——仿佛上一次写年终总结还没多久,现在马上又过了一年,在大人的世界里时间似乎总是过得飞快,所以几年或者十几年对于小朋友来说很漫长,但是对于我们来说基本上就是眨眨眼吧——虽然不是什么值得自满的事情。😂

但凡是在学校期间,我的 schedule 大致是每周上一次课,45 分钟,然后每天花大约一小时左右练琴,主要是完成老师布置的作业。前期主要是用的“小汤普森”和 Piano Adventures 这两本教材,最近刚刚开始小汤第四册和车尔尼 599。其实上课的时候相对于老师讲解和示范,更多的时间是我在弹上一周的作业,然后老师给反馈,但是我觉得上课比自己学要有用很多。一方面是进度控制(看我暑假时候的状态就知道了),更重要的是提供各种反馈,特别是初期可能会养成一些不良习惯之类的到后面可能很难改过来,而且很多东西自己基本上都不太会考虑到的。即使是像小汤普森里这种没有什么技术含量的两行小乐谱,老师弹出来也比我弹出来好听百倍,如果只是让我自己不断地去听两个版本对比来自己领悟究竟要怎样弹才能弹得好听的话,估计要花上十倍的时间吧。

我的老师是 XZ 介绍的,是河对岸 BU 的 Doctor of Musical Arts (Piano Performance) 在读,估计年纪实际上跟我差不多,但是不知道为什么我好像不太擅长跟她以朋友的方式相处,虽然她其实是个相当 nice 的人,大概是我心里有种老师的敬畏感吧,亦或者每次见面的时间都是要收钱的,不太适合闲聊太多啊哈哈哈。总之我觉得她是相当厉害的,之前去听过她的期末 Concerto Recital。不过我总感觉到这个行业对新人大概还是相当不友好的吧。之前在波士顿交响乐团听一个莫扎特的钢琴协奏曲,是由一位 93 岁的老爷爷来演奏的。古典音乐界对于钢琴家的需求量估计并不是很大,而且已经成名的音乐家应该会长期占着坑使得整个行业长期处于饱和状态使得新人们得到的机会非常少。这样想想好像咱们读 PhD 的要搞科研但是好的学校的 Professor 的坑又不多的情况好像也并没有差太多哦?

不过不论是艺术还是科研,为之献身的人们我都是很敬佩的。暑假在 Google 实习期间我无意中发现有两个组,一个是 Google Culture Institute,它们在做一个叫做 Arts and Culture 的 app,基本上就是我心中一直在想的一个东西,差不多就是把世界各地的博物馆电子化。刚看到这个的时候心里一阵激动,每次在世界各地的博物馆徘徊的时候我都会冒出这样的想法——当然电子化的目的并不是代替博物馆本身,因为在博物馆看原画和拍下来的照片的体验差别还是相当大的,但是我们需要一个方便的索引,帮助我回忆起在某个博物馆看到了哪些画、或者找到哪幅画在哪里馆里能看得到,或者一些扩展功能比如相同、相似类型的画,不同的流派、风格、发展等等。另一个项目组是 Project Magenta,基本上就是机器学习跟音乐组合。我感兴趣的技术和艺术之间结合的项目,从某种意义上来说也许是非常适合我的地方,但是兴奋之余似乎自己心里也清楚,自己大概会一直避免把艺术变成自己的主业吧。其实我不太清楚自己在追寻什么,有时候觉得需要好好了解一下自己,但是从来也没有真正认真思考过的感觉。

回到音乐的话题上,我发现在多少了解了一点乐理知识之后,自己也渐渐开始对交响乐之类的大型曲目开始感兴趣了(之前是肯定听睡着的),不过开始去 BSO 听音乐会也是冬天的事情,想想在波士顿能有这么好的资源,并且学生票只要 7 块钱就能听一年这种福利不好好利用真是太浪费了。难以想象我在刚来的第一年就买过一张 BSO 的学生年票,然而因为要去拿票跑两趟最终一次都没有去听过就过期了。不过开始关注之后就发现其实周围挺多人会弹钢琴或者甚至弹得很好的,也有许多人对古典音乐很热衷的。

钢琴的故事才刚刚开始,前面的路还有很长,希望自己能一直 enjoy 这个新的爱好,同时画画的事情也不能落下。2016 年的绘画旅程大概可以总结为“买买买”“尝试了很多不同的画法”吧。比如以下依次是:中性笔肖像(在家的时间段通过豆瓣的一个上传照片画肖像的活动画了很多人脸);丙烯画星空(画到后面直接用手了,全是颜料);Wacom 板子用黑色画布和白色画笔学习透光效果(个人很喜欢这张);纯色块无线条风格;粉彩棒买来一直不太会用,画的人脸;天鹅堡(Wacom 加仿真画笔);宫园薰(马克笔,买了很久了第一次认真用,后来脸被我涂破了于是用笔挡了一下)。

另外我还稍微尝试了一下逐帧动画,为了简化工作量,我想做成前阵子网上很流行的那种动态照片,就是大部分内容是静止的而有一些细节如台灯啊窗帘啊之类的在动。结果并不是特别成功,我发现光画一个数秒钟的窗帘飘动的动画工作量都相当大的样子,以后有机会应该会再尝试的。后半年某天一冲动就买了个超大号的 iPad Pro,特别喜欢,看论文、做笔记、画画都很好用。更冲动的是后面我还订阅了 Adobe Creative Cloud 全套件,人生第一次用上了正版的 PhotoShop。下面几乎都是用 Apple Pencil 画的,最后一张则是学校的 Student Art Association 在 Student Center 的画室。去年在 MFA 学习丙烯画我觉得挺不错的,后来知道学校也有绘画课之后就觉得 MFA 跑过去好远啊,所以 XJJ 跟我提起 SAA 的课的时候我立马就报名了,虽然这边只有油画课得重新买颜料之类的,不过尝试一下油画也挺好的啊。我觉得我画画挺心血来潮的,有时候想画的东西,拖一阵子就一点兴趣都没有了,所以基本上如果不一次画完的话后面就不会跟下去了,而是会去画别的东西(比如下面那幅线稿,其实是上了一半色没上完,就干脆留成线稿了)。然而油画这个东西干得特别慢,除非就是故意要混成一团,否则黏黏的一层颜料的时候基本上很难在上面画东西,我想如果没有每周例行课程这种东西的话,我大概是没有办法完成任何一幅油画的了吧。

不过学校这边的油画课感觉跟我之前上的丙烯课很不一样,老师基本上不教任何技术,唯一一次示范就是教大家怎样把颜料挤到调色板上……感觉主要还是激发兴趣,鼓励大家自己尝试为主。然后就依次跟大家聊天,不论你画什么老师都会觉得很好……我觉得我问她最多的一个问题就是“黑色要怎么混来着?”😂。从这方面来讲学费的性价比不太高,所以有点小犹豫下学期还要不要选这个课。不过每次一想到快要毕业了,以后说不定就没有什么机会上这样的课了也不太找得到小伙伴一起画了。不禁想起暑假在 G 实习的时候,找到一个叫做 Sketch Friday 的活动,每周五午饭的一小时时间,5 分钟一幅画做快速 sketching,主要是练习速写、动作之类的,有一些网站有很大的模特图库,并且有定时功能,比如 SketchDaily。活动内容虽然很有意思,但是我第一次去的时候加上我只有两个人去了约定的地点,其他人都通过远程视频会议连过来,第二次就只有我一个人了。原本就没有几个人参与,还全部都通过视频会议参加,画画过程中也没有什么交流,总之感觉怪怪的,跟自己一个人画好像没太大区别。

反正我现在从各方面都有点抗拒毕业啊,到了这个时间段,同年级的小伙伴们大家都多多少少到了不得不对未来做抉择的时候,不论是像我这样急着不想毕业的,还是像 SW 那样急着想要毕业的,好像大家的心态也都比前几年更加 unsettle 了。一个很明显的特征就是今年特别是下半年的集体活动频率大幅增加,基本上每周都会有聚餐或者桌游、电影之类的活动。直接导致我今年去电影院的频率大幅增加。2016 年比较喜欢的电影(包括在家补的以前的电影)和电视主要有:疯狂动物城神奇动物在哪里权利的游戏第六季莫扎特传The Highest Level: Lang Lang with the Berliner Philharmoniker蝙蝠侠:黑暗骑士机械姬奇异博士指环王1:魔戒再现无人生还星球大战外传:侠盗一号让子弹飞湄公河行动东方快车谋杀案

相比之下我今年看的动画片数量似乎跟去年相比又进一步缩水了,特别是新番基本上很少有我喜欢看的,感觉现在日本动漫似乎商业化越来越严重了,各种后宫各种套路,各种卖肉各种卖腐,不知道是现在市场太好了大家都冲着最大化赚钱的路子走还是说现在市场太不好了,不走套路认认真真做独立的动漫很难存活。反正结果是我把许多收藏了很多年的箱底片都翻出来看了。

  • 四月是你的谎言:这个是两年前的片,当时就听到好评如潮,但是不知道为什么我一直没有看,也忘记了是什么契机让我突然把它翻出来看的。然而不可否认的是这个动画片现在成为我人生中的一个重要转折点,即使抛开这一点,客观来讲,画风、配乐、人物刻画、感情描写各方面都是很棒的。我也就看了大概四遍吧。年底的时候日本上映了一个真人版电影,虽然据说差评如潮,但是还是很想看,可惜一直都没有资源。
  • 交响情人梦第一季第二季第三季,也是若干年前的老片子,蛮好看的,属于轻松愉快搞笑型,三季按顺序可以看出制作到后面变得越细致了:特别是演奏、指挥的画面从静止幻灯片变成了真正的动画。突然感觉音乐类的动画片画起来还蛮难的,比如弹个钢琴、拉个小提琴啥的,动作也不能乱画,还得和音乐配合得上,导致也不能重复利用(像交响情人梦第一季里翻来覆去用同一个小提琴的动画镜头,看着就特别糟糕),制作成本就上去了。像《四月》这样的精良制作里一些不太重要的配角在演奏的时候也是会用幻灯片风格的。
  • 红猪:宫崎骏的经典,并且好多宫崎骏爱好者都跟我表示过这一部是最爱,确实是非常好看。
  • 千年女优:“追逐着你,即便已经不再记得你的样子。因为我喜欢那样的自己。”也是一部经典好片,来自今敏,叙事风格很有意思,现实和回忆重叠。
  • 灰与幻想的格林姆迦尔:今年的新番,画面唯美,水彩风格,没有什么剧情,当然其实是有剧情的,大概就是类似于刀剑神域那种人被 trap 到了一个游戏世界里,但是剧情实在太过拖沓了,所以结局了之后我是直到下一个星期发现这一集是不是已经看过了,才意识到哦原来结局了啊。😓
  • 心灵想要大声呼喊:本来是看到宣传海报很喜欢画风跑去看的,结果被音乐惊艳到了。讲一个说话的能力被封印起来的女孩通过唱歌来表达内心的故事,全剧的高潮是音乐剧的演出,绿袖子改编的那一段歌『わたしの声』超好听,特别是在动画片里这一段登场的方式非常触动人。末尾的悲怆奏鸣曲Over the Rainbow 的双声部合唱同时展开音乐剧的两种不同的结局,也让人耳目一新。
  • 亚人第一季第二季:分别是今年的一月和十月新番。故事背景是人类中偶尔会出现一些被称为“亚人”的人,其主要的特殊能力就是死了之后能复活,由于在社会上不太被接纳,而且政府也有一些秘密机构用亚人来做活体药物、武器实验之类的。故事则主要以两个意见相左的亚人为中心展开。感觉是很好的设定,不过似乎并没有能很好地探讨政治、社会方面的问题,不过许多战斗还是挺吸引人的,虽然介于 2D 和 3D 之间的神秘生硬动作的画风让人有点受不了。
  • 你的名字:新海诚的新作,在网上找的低画质版本,看低画质的新海诚有点暴殄天物。剧情还不错,觉得比他以前的好看,感觉偏搞笑一点,感觉男主丧心病狂。我个人觉得不太好的地方一个是女主说服她爸的过程直接被省略了,会让人有点觉得是编剧没有能力把这一段重要的细节描绘出来而耍了个小 trick;另一个是最后结局要是留成开放式,在两人即将回头的时候掐掉就好了。
  • 大鱼海棠:剧情无力吐槽但是画面很美,然而我看的是针孔摄像头偷拍版……
  • 其他还有只有我不存在的城市(新番,评分很高,但是我一直不太抓得住主题,看到后面忘记了,完结的时候补了一下结局)、传颂之物 虚伪的假面(新番,画风比较喜欢,剧情比较拖沓,看了一阵子就忘记了)、恋爱随意链接(12 年的动画,中文标题翻译比较自作主张,感觉设定和故事 idea 很好,development 却很烂,铺出了很多很难的关于人心和人性的探讨,但是每次都莫名其妙就结束了)。还有就是由于对武侠的爱好一直断断续续地看的两部国产动画秦时明月(N 个月随机出一集)和画江湖(每周出但是每集大概只有 5 分钟的剧情,剩下都是广告以及片头片尾)。

漫画看得比较少,基本上就看了一点《亚人》、补了一次《热血江湖》的最新进展、还有愛瑪儂系列的后续,以及在图书馆看到终于购入了新世纪福音战士漫画的最后一册顺便补了一下。至于游戏,大概真正认真玩过的主要就是 Transistor 了,很不错的游戏,就是剧情有点短。Don’t Starve 尝试玩了一阵子,还蛮好玩的,就是太难了又不能存档,基本上就是各种花样死法,捅马蜂窝被蛰死,生吃蘑菇被毒死或者被饿死、冻死……文明 6 出来的时候一直想玩,但是后来还是忍住了,感觉自己会一下子玩几天几夜直到猝死为止……Pokemon Go 出来的时候本来也没有在玩,但是到后来几乎成了你不玩都跟大家没有任何共同话题的时候还是玩了一阵,那个时候我的座位上坐着就能够得到两个补给点,于是抓了很多小鸟一个星期就到了 20 级,不过后来还是觉得比较无聊就没玩了。暑假心情抑郁的时候在公司的游戏机房随便玩了一些 random 的游戏,也不是很有兴趣。我发现现在的大型游戏都好复杂,而且欧美游戏那种画风过于写实化的游戏像古墓丽影这种我玩起来内心是有点受不了的……

锻炼差不多就只是不定期去游泳,偶尔实验室一个意大利人会叫我打乒乓球,总结起来就是比较缺乏锻炼。原本我觉得今年的睡眠质量下降了许多,不过看了下 Sleep Cycle 记录的数据,似乎跟去年差不多,而且似乎一年的睡眠质量有个很一致的 pattern 啊:差不多就是暑假睡得很舒服,9 月一开学就完蛋了,当然 Sleep Cycle 的数据也不知道到底有多准了。我自己觉得今年是有经常失眠的(很多时候只是由于弹钢琴弹得太兴奋了),不过也许这说明其实去年也经常失眠吧,哈哈哈。

博客显然处于荒废状态,我想我以后的目标是不要首页全是年终总结……不过其实偶尔会起个小头,但是后来又弃掉,也许自己要求越来越高,同时花在上面的时间也越来越少两方面的原因都有吧。也许明年可以尝试一下多写一点更加随意的东西。

最后是感情生活。如我前面提到,除了跟钢琴的这段“恋爱”之外,今年暑假期间其实有一段真实的感情经历,虽然开始和结束都异常地迅速,但是对我打击还是挺大的,所以我暑假过得比较抑郁,那阵子特别想家,也许对湾区的印象较差也有一部分原因来自于此。大概自己对于许多事情还是太过于天真和草率了。感情这种事情好像越是经历越觉得难以捉摸,我在想也许我就是个不太适合谈恋爱的人,作茧自缚也束缚了他人。除非一定要跟谁过不去,否则如果是能做朋友的人的话,还是应该好好珍惜认认真真做朋友比较好吧。比较有趣的是那阵子太难过了于是渐渐开始用微信朋友圈了,而之所以“开始用”正是因为两年前在感情受挫的时候关闭了朋友圈模块,所以说,真是神奇……😅幸运的是假期结束开学之后一大波聚餐电影桌游火锅 KTV 以及游山玩水活动来袭,很快就恢复了正常的心态,显然这是恢复最迅速的一次,当然有些东西大概是没法轻易遗忘的吧,所以 FF 提起陈奕迅的“一丝不挂”歌词里前后两段微妙的变化的时候才会觉得深有感触。我想成长对于记忆中伤口的作用并不是更快地遗忘,而是让它们慢慢变成你可以去正常面对和接受的东西。毕竟没有什么结是解不开的,等到大家都牙齿掉光了,所有的过去都不过是一时的年少轻狂吧。昨天收到信用卡公司寄来的新卡,原来自己这几年一直在用的两张卡都马上就过期了,打开信封发现各自同时还寄来了一张副卡,才想起当时申请的时候看到可以免费加副卡就顺便办了。现在看着卡上这个曾经很熟悉的名字,不能说心里没有任何触动,但是并不是什么很复杂的情感。

其实这两年单身期间时不时亲朋好友之类的会给我介绍女生认识,即便是在诸如地域、圈子等客观条件绝对不现实的情况我都有很认真地去对待,原本的想法是即使不会有过多的发展认识新的朋友也是挺好的。但是后来我发现好像这样交朋友的方式就是不太现实,原本就是极端微妙的 context,有时候好像怎样去 behave 都有点不太合适,即便抛开这些问题,没有合适的契机好像也是很难发展出持久的友情的。其实我有时候会想为什么大家都会觉得越长大越难以交到新朋友,也许根本原因并不是人越长大人心越复杂从而导致没法交朋友,正好相反,复杂的内心在引起共鸣的时候才会产生更加坚固的羁绊吧。我想建立深厚的友谊的先决条件一方面你们要有共同的地方,能够互相吸引,另一方面是你们要有一个交流、磨合和相互了解的过程,说得玄乎一点大概是要触及各自灵魂深处的共鸣点。基于这样的假设,年轻的时候容易交朋友的一大原因在于学校这个环境强迫一群原本不相关的人凑到一起,并且共同去完成许多事情,这给大家提供了很多共同经历和互相了解的机会,从中小学早操上课晚自习全都在一起,到大学自由选课,再到 graduate school 你的研究课题的细节可能在世界上没有第二个人可以讨论,这样的“强制社交”的环境是越来越弱的。那工作以后呢?按理说同一个项目组的人每天都在一起做同一件事情,为什么还会有人说工作以后交不到朋友呢?我想也许一个原因是工作上的同事构成比较单一,都是通过该工作为 criterion 过滤之后留下的人,所以不像学生时代那样只是由于地域之类的原因聚在一起的一堆人那么多样化?总之只是我的一些无聊时的胡思乱想。

好像扯得太远了,其实关于恋爱观,我也有考虑过一点。也许将来的想法会改变,不过现在我的态度是恋爱婚姻这些东西并不是人生目标甚至不是人生必须要经历的的一个 milestone,所以我大概不太会刻意去追求,当然也并不持反对态度。对我来说一个人和两个人大概就是不同的生活方式吧,一个选择问题,孰优孰劣无法定论。客观来讲我一个人就能开开心心地过,事实上我的生活看起来好像是非常充实的,甚至有时候非常亲密的朋友也会觉得我好像就不可能有空虚无聊的时候,然而事实并非如此,我想这大概就好比你再有钱也会有买不起东西的时候吧?——不同的人会有不同的烦恼。同理,两个人的生活也是有取有舍吧,我一点也不否认爱情的强大魔力显然也是这宇宙中独一无二的一种存在。

闲话说太多了,讲点正经事好了。今年是 PhD 第 4 学年到第 5 学年的过渡,课程方面早就已经没有任何需要处理的了,不过我一直觉得我很喜欢学校的一个原因就是可以不停地学各种东西,简而言之就是一种满足大脑的求知欲的精神毒品吧(大概跟人们会享受刷知乎了解各种五花八门的知识类似吧)。不过今年似乎悄无声息地发生了一点点转变,春学期的时候我只上了一门课,到秋学期我居然史无前例地一门课都没有修,甚至连旁听都没有。也许我终于转型到 graduate student 的状态,认识到应该把重心花在科研而不是课程上?或者说也许我突然到了该毕业的时间了?——虽然我心里挺抗拒毕业的,也许是我的学生生活过得太舒服了,但也许也只是我害怕这个转变吧:学生时代的结束,拖延了这么久还是无法回避的一件事。我在想也许我并不是害怕这件具体的事情,而只是对于“这是一个不可逆的过程”这个属性感到抵触,可想而知再过一些年月当我意识到我快死的时候肯定也会很不好受,毕竟那是个更加不可逆的过程。

科研方面差不多还是老样子,没有什么成果的时候就会觉得自己究竟在做什么,莫非整天在 lab 无所事事?偶尔有一些成果的话就会很欣喜,但是过一阵子又会觉得好像什么都没有做,渐渐就习惯了,好像科研的常态就是如此。暑假在 Google Brain 实习,虽然因为个人原因过得不开心并且导致有点回避各种社交活动,但是工作上还是比较顺利的,感觉自己这次能跟到很好的 host 也是非常幸运的一件事情,做的东西个人也觉得比较有意思。此外偶尔会抽空做一点开源的东西(上图是 github 一年 commit 频率概要),主要是 MXNet 吧,基本上是一阵一阵的。MXNet 最近发展得挺好的,感觉身在这样一个团队里是一件挺幸运的事情。

其他一些杂七杂八的事情:2016 年第一次开始用机械键盘。开始几乎每天喝咖啡。第一次坐小飞机(感谢飞行员 winsty)和帆船(感谢船长 XJJ)。第一次一个人租车开车(快要把自己吓哭了,我真不太适合开车)。继续玩了一阵子 postcrossing,寄了不少明信片(包括一些自己手绘的)也收到了一些 lovely 的明信片,打开信箱的时候偶尔发现有东西还是非常惊喜的,不过后来由于 travel 中断了之后就没有继续玩了,好像有时候跟陌生人写明信片的时候有点不知道要说些什么。第一次收到陪审团传唤,虽然我并不是美国公民(感觉像是政府在骗钱,因为如果你在多少天内不回复说你不是公民不能参加陪审团的话就要罚款几千块)。第一次做证婚人。第一次去警察局报案(虽然不是我丢东西),还有第一次收到警察的邮件、电话、甚至上门访问的留言纸条等等(一个素未谋面的人不知道什么原因被警察逮捕了需要保释,然后他托警察通过 Google 搜索找到了我的联系方式……让我第一次认识到也许要开始注意互联网上的隐私了)。第一次 Career Fair 没有拿任何 free T-shirt,也许等剩下的这几件穿完我就会彻底告别 free t-shirt 了。买了降噪耳机,觉得很好用,开起来之后除了自己的耳鸣什么都听不见了,对于冰箱、空调之类的噪音很有效果,猜想飞机上应该也会很好用,不知道有生之年神经科学和脑机接口能否发展到可以对耳鸣进行降噪呢?第一次分清了村上春树 vs 春上村树以及郎朗 vs 朗朗。第一次简单做了一下个人十年总结,觉得自己变化好大,十年之后自己又是什么样子,完全想象不出来。

哪时候开始学会微笑不回答 都忍着
谁说的那些疯狂年纪不同了 非忘了
规则都只是神话
气象总是会偏差
冷一点 暖一点 也不过这样

2017,新年快乐!

December 31, 2016 12:00 AM

December 13, 2016

包昊军的博客

December 11, 2016

包昊军的博客

December 10, 2016

包昊军的博客

December 05, 2016

Redguardtoo

Use Perforce inside Emacs

CREATED: <2016-12-05>

UPDATED: <2017-02-08 Wed>

Perforce is a proprietary VCS which is not as powerful as Git so it takes extra effort to use it with Emacs.

For example, git can display the detailed history of a single file using git log -p file-path. There is no way you can do this in perforce. Combination of perforce cli like p4 changes file-path | awk '{print $2}' | xargs -i p4 describe -du {} displays all the files of related changes. You have to turn to Emacs Lisp to clean the output of p4.

1 Perforce workflow

  • p4 set to set up
  • p4 login to login
  • p4 client creates a new work space to tell server the directories/files to check out or to ignore
  • p4 sync //depot/url/... to actually checkout
  • Files are read-only by default. You need p4 edit file to make files writable before editing
  • p4 add files to add new files. p4 revert to revert edited file to it original status and lose the local changes
  • p4 change to create a pending change. Then p4 submit -c #changelist to actually submit code to main branch. Pending change gives you a chance to tweak the change before submit
  • Or p4 submit -d"description" file to submit a single file directly

2 My solution

Perforce cygwin portable is not recommended.

I suggest using executable from Perforce Windows native version which works on both Cygwin and Windows.

Perforce server has one unique URL for every physical file on the disk. If I only feed p4 that URL, the operation is always successful.

2.1 Emacs

I provide p4edit, p4revert, p4submit, p4diff, and p4history,

;; {{ perforce utilities
(defvar p4-file-to-url '("" "")
  "(car p4-file-to-url) is the original file prefix
(cadr p4-file-to-url) is the url prefix")

(defun p4-current-file-url ()
  (replace-regexp-in-string (car p4-file-to-url)
                            (cadr p4-file-to-url)
                            buffer-file-name))

(defun p4-generate-cmd (opts)
  (format "p4 %s %s" opts (p4-current-file-url)))

(defun p4edit ()
  "p4 edit current file."
  (interactive)
  (shell-command (p4-generate-cmd "edit"))
  (read-only-mode -1))

(defun p4submit (&optional file-opened)
  "p4 submit current file.
If FILE-OPENED, current file is still opened."
  (interactive "P")
  (let* ((msg (read-string "Say (ENTER to abort):"))
         (open-opts (if file-opened "-f leaveunchanged+reopen -r" ""))
         (full-opts (format "submit -d '%s' %s" msg open-opts)))
    ;; (message "(p4-generate-cmd full-opts)=%s" (p4-generate-cmd full-opts))
    (if (string= "" msg)
        (message "Abort submit.")
      (shell-command (p4-generate-cmd full-opts))
      (unless file-opened (read-only-mode 1))
      (message (format "%s submitted."
                       (file-name-nondirectory buffer-file-name))))))

(defun p4revert ()
  "p4 revert current file."
  (interactive)
  (shell-command (p4-generate-cmd "revert"))
  (read-only-mode 1))

(defun p4-show-changelist-patch (line)
  (let* ((chg (nth 1 (split-string line "[\t ]+")))
         (url (p4-current-file-url))
         (pattern "^==== //.*====$")
         sep
         seps
         (start 0)
         (original (if chg (shell-command-to-string (format "p4 describe -du %s" chg)) ""))
         rlt)

    (while (setq sep (string-match pattern original start))
      (let* ((str (match-string 0 original)))
        (setq start (+ sep (length str)))
        (add-to-list 'seps (list sep str) t)))
    (setq rlt (substring original 0 (car (nth 0 seps))))
    (let* ((i 0) found)
      (while (and (not found)
                  (< i (length seps)))
        (when (string-match url (cadr (nth i seps)))
          (setq rlt (concat rlt (substring original
                                           (car (nth i seps))
                                           (if (= i (- (length seps) 1))
                                               (length original)
                                             (car (nth (+ 1 i) seps))))))
          ;; out of loop now since current file patch found
          (setq found t))
        (setq i (+ 1 i))))

    ;; remove p4 verbose bullshit
    (setq rlt (replace-regexp-in-string "^\\(Affected\\|Moved\\) files \.\.\.[\r\n]+\\(\.\.\. .*[\r\n]+\\)+"
                                        ""
                                        rlt))
    (setq rlt (replace-regexp-in-string "Differences \.\.\.[\r\n]+" "" rlt))
    ;; one line short description of change list
    (setq rlt (replace-regexp-in-string "Change \\([0-9]+\\) by \\([^ @]+\\)@[^ @]+ on \\([^ \r\n]*\\).*[\r\n \t]+\\([^ \t].*\\)" "\\1 by \\2@\\3 \\4" rlt))
    rlt))

(defun p4--create-buffer (buf-name content &optional enable-imenu)
  (let* (rlt-buf)
    (if (get-buffer buf-name)
        (kill-buffer buf-name))
    (setq rlt-buf (get-buffer-create buf-name))
    (save-current-buffer
      (switch-to-buffer-other-window rlt-buf)
      (set-buffer rlt-buf)
      (erase-buffer)
      (insert content)
      (diff-mode)
      (goto-char (point-min))
      ;; nice imenu output
      (if enable-imenu
          (setq imenu-create-index-function
                (lambda ()
                  (save-excursion
                    (imenu--generic-function '((nil "^[0-9]+ by .*" 0)))))))
      ;; quit easily in evil-mode
      (evil-local-set-key 'normal "q" (lambda () (interactive) (quit-window t))))))

(defun p4diff ()
  "Show diff of current file like `git diff'."
  (interactive)
  (let* ((content (shell-command-to-string (p4-generate-cmd "diff -du -db"))))
    (p4--create-buffer "*p4diff*" content)))

(defun p4history ()
  "Show history of current file like `git log -p'."
  (interactive)
  (let* ((changes (split-string (shell-command-to-string (p4-generate-cmd "changes")) "\n"))
         (content (mapconcat 'p4-show-changelist-patch
                             changes
                             "\n\n")))
    (p4--create-buffer "*p4log*" content t)))
;; }}

As a bonus tip, if you use find-file-in-project, insert below code into prog-mode-hook to view any perforce change inside Emacs,

(setq-local ffip-diff-backends
            '((ivy-read "p4 change to show:"
                        (split-string (shell-command-to-string "p4 changes //depot/development/DIR/PROJ1/...")
                                      "\n")
                        :action (lambda (i)
                                  (if (string-match "^ Change \\([0-9]*\\)" i)
                                      (shell-command-to-string (format "p4 describe -du -db %s"
                                                                       (match-string 1 i))))))
              "p4 diff -du -db //depot/development/DIR/PROJ1/..."))

2.2 Bash Shell

Other operations are finished in Bash Shell,

# {{ Perforce, I hope I will never use it
if [ "$OS_NAME" = "CYGWIN" ]; then
    function p4() {
        export PWD=`cygpath -wa .`
        /cygdrive/c/Program\ Files/Perforce/p4.exe $@
    }
fi

# p4 workflow:
#
#   # basic setup
#   p4 set P4CLIENT=clientname  # set your default client
#   p4 set P4PORT=SERVER:1666
#   p4 set P4USER=username
#   p4 client # create/edit client, client views selected files
#
#   # checkout code
#   p4 sync [-f] //depot/project-name/path/...
#   p4 edit file[s]
#   ... do some editing ...
#
#   # submit code
#   either `p4 submit -d"say hi" file` or `p4 change`
#
#   I recommend `p4 change` because you can edit files list before submit happens.
#   After `p4 change`,  `p4 submit -c changelist#` to actually submit change.
#
alias p4clr='p4 diff -sr | p4 -x - revert' # like `git reset HEAD`
alias p4blame='p4 annotate -c -db ' # could add -a see deleted lines
alias p4cr='p4 submit -f leaveunchanged+reopen -r'
alias reviewcl='ccollab addchangelist new'
alias p4pending='p4 changes -s pending' # add ... for current directory
alias p4untrack='find . -type f| p4 -x - fstat >/dev/null'
alias p4v='p4 resolve' # after `p4 sync ...`, maybe resolve
alias p4r='p4 revert' # discard changes
alias p4e='p4 edit'
alias p4s='p4 submit'
alias p4sr='p4 submit -f submitunchanged+reopen' #submit&reopen
alias p4up='p4 sync ...' # synchronize from current directory
alias p4o='p4 opened' # list opened files
alias p4c='p4 changes' # create a new pending change
alias p4chg='p4 change' # create a pending change
alias p4d='p4 diff -du -db'
alias p4ds='p4 diff -du -db | lsdiff' # diff summary, patchutils required
alias p4i='p4 integrate'
alias p4unsh='p4 unshelve -s' # Usage: p4unsh changelist#, like `git stash apply`
alias p4h='p4 changes -m 1 ...' # show the head change

function p4mypending {
    local P4USERNAME="`p4 user -o | grep '^User:\s' | sed 's/User:\s\([a-bA-B0-9]*\)/\1/g'`"
    p4 changes -s pending -u $P4USERNAME
}

function p4shelved {
    local P4USERNAME="`p4 user -o | grep '^User:\s' | sed 's/User:\s\([a-bA-B0-9]*\)/\1/g'`"
    p4 changes -s shelved -u $P4USERNAME # add ... for current directory
}

function p4cmp {
    if [ -z "$1" ]; then
        echo "Usage: p4cmp changelist-number changelist-number"
    else
        p4 diff2 -dub -q -u ...@$1 ...@$2
    fi
}

function p4dl {
    # git diff
    p4 diff -du -db $@ | vim -c "set syntax=diff" -R -
}
function p4sh(){
    # show specific change or the latest change
    if [ -z "$1" ]; then
        p4 changes | python ~/bin/percol.py | awk '{print $2}' | xargs -i p4 describe -du {} | vim -c "set syntax=diff" -R -
    else
        p4 describe -du -db $@ | vim -c "set syntax=diff" -R -
    fi
}

function p4lp {
    #like `git log -p`
    p4 changes $@ | awk '{print $2}' | xargs -i p4 describe -du {} | less -F
}

function p4mlp {
    #like `git log -p`
    p4 changes -u $P4USERNAME $@ | awk '{print $2}' | xargs -i p4 describe -du {} | less -F
}

function p4adddir(){
    if [ -z "$1" ]; then
        echo "Usage: p4adddir directory"
    else
        find $1 -type f -print | p4 -x - add
    fi
}

# p4's suggestion,http://kb.perforce.com/article/27/creating-release-notes
# @google "assing variable from bash to perl in a bash script"
function p4l(){
    # p4 log
    if [ -z "$1" ]; then
        # show the full log
        p4 changes -l ... | less
    else
        # p4log since-changelist-number
        p4 changes -l ...@$1,#head|perl -pe "if(\$_=~/$1/){ last;};"
    fi
}

function p4ml(){
    # my p4 log
    if [ -z "$1" ]; then
        # show the full log
        p4 changes -l -u $P4USERNAME ... | less
    else
        # p4log since-changelist-number
        p4 changes -l -u $P4USERNAME ...@$1,#head|perl -pe "if(\$_=~/$1/){ last;};"
    fi
}
# }}

by Chen Bin at December 05, 2016 11:51 AM

December 04, 2016

GeekPlux

为什么要用 Emacs

本文仅从作者个人感受上谈一下自己使用 Emacs 的理由和感受,通篇无干货。

Emacs 和 Vim 这两大编辑器,一直让前赴后继的工具党难以取舍。到底学哪一个?哪一个学了之后能如虎添翼?最初的我也是纠结了许久,仿佛本科时纠结到底该主学 C++ 还是主学 Java 一样。而事实上正如前辈们说的:不要纠结学哪个,反正迟早都要学

使用 Emacs 的若干好处

从我个人的角度讲,我认为学习和使用 Emacs 有以下几个好处:

  • 掌握了一个几乎能完成任何事,又可以随心所欲修改的编辑器
  • 入了 lisp 的门
  • 混进了一个牛人众多的社区

搜索过 Emacs 的人对前两点应该都有所耳闻,但百闻不如一见,真正使用之后才知道 Emacs 多强大。就我个人来说,除了将 Emacs 作为多种编程语言的编辑器,还用来管理自己的学习笔记、To-Do List。社区里很多道友还用它来记账、管理文献、写论文等等(大家都用 Emacs 做什么?)。如果你对 Emacs 没兴趣,那么至少应该了解一下 Org-Mode,由于 Org-Mode 的强大,有人甚至用它来管理自己的一生类似《奇特的一生》中提到的方法)。Org 其实就像 MarkDown 一样,是一种标记语言,想想能用纯文本去管理自己的一生,是不是还有点小激动。

用 Emacs 查看天气

由于 Emacs 的配置都是用 elisp 这个 lisp 的方言去写的,所以学习 lisp 终于有了用武之地。不过 lisp 属于「规则简单,威力无穷」,学习它其实不会花太多时间,但要理解它的精髓和使用好它真的很难。

除了以上两点,还有一个最关键的好处是:认识了很多牛人。不得不说,Emacs 的高门槛和小众,决定了其使用者的质量。很多 Emacs 道友来自非计算机行业,有的学化学,有的做木工,非常有趣。而且大家都特别有钻研精神,这可能也是所有 Emacs 玩家的共同点。

我是如何中了 Emacs 的毒

其实最早听说 Emacs 是在本科的时候。下载下来拨弄一番后发现难点在于记忆快捷键,实现一个自己想要的操作可能需要按一系列按键,有点像记忆菜单栏快捷键的感觉(现在不这么认为了,现在完成一个操作我可能都是直接调用 Emacs 的函数,因为 Emacs 中任何一个操作其实都是一个函数)。每一个快捷键组合打开一个「子菜单」,最终要实现的操作有可能在三级子菜单中,所以感觉相当繁琐。后来每每雄心壮志重新开始学习,都会被记忆快捷键这个拦路虎挡住。

当然,学习 Vim 也如此。在不知道多少次折腾后,我还是先掌握了 Vim,学习历程和方式参见这篇 Vim - 适合自己的,才是最好的。Vim 的哲学和 Emacs 不同,它把输入分成 纯输入操作 两种模式(个人理解)。纯输入就是单纯的输入字符,需要换行、移动光标、复制粘贴时则需要「操作」。我觉得这种很符合大脑的思考方式,输入时单纯想着内容就好,需要「整理」内容时再操作。

但是我想用 Emacs 之心不死,当时主要因为 Org-Mode 对我的吸引力很大。在听说 Emacs 有 evil 之后,果断转了过去,瞬间觉得学习曲线变得顺滑了。再之后发现了颜值颇高的 Spacemacs,于是更加爱不释手。Spacemacs 的文档非常详尽,还提供了如何从 Vim 迁移的方案,对我这种菜鸟真的是非常友好。

从此,便算是入了坑,之后的填坑之路也是漫长,比如如何配置,如何使用 Org-Mode,如何寻找适合自己的最佳实践,每部分都能长篇大论一番。

如果你有兴趣,还可以去 来聊聊大家是怎么入 Emacs 这个大坑的吧 看看其他人入坑的经历。

关于 Emacs 的学习曲线

有的人推崇从最原生的 Emacs 学起,自己一步步配置,有的人推崇直接用社区大牛写好的配置,我显然是后者。Spacemacs 就是一款对新手和 Vim 党非常友好的配置,而且文档写的特别详细,只要你耐心读,绝对是可以从容掌握它的,实在遇到困难可以在社区正确提问,只要你问题不是很让人反感,都会有很多大牛热心帮你(这里我不禁要说一句,如果你是伸手党,那么 Emacs 是真的不适合你)。

关于使用 Emacs 太折腾

刚开始确实花很多时间在折腾上,但是现在不会了。因为我发现,对工具「折腾」的越多,定制越深,就会越依赖这个工具。一旦需要在别的电脑上编程,双手都感觉不是自己的了。同时,折腾后的「效率提升」其实我们自己都知道效果并不明显,有的需求完全是伪需求。强迫自己适应一个新的操作方式,虽然对自己是一个训练,但不需要以提升效率为借口。所以我现在基本上除了一些影响使用上的改动外,基本上维持默认配置,强迫自己适应默认配置,适应才是对效率的最大提升。

不过,一旦开始折腾,很容易停不下来,你懂的。。。一下午甚至一天的时间一瞬间就没了。

延伸阅读


本作品采用知识共享 署名-非商业性使用-禁止演绎 4.0 国际 许可协议进行许可。

December 04, 2016 07:25 AM

December 03, 2016

GeekPlux

Vega-Lite: A Grammar of Interactive Graphics

在 InfoVis 2016 上,UW 交互数据实验室 提出了一种新的交互数据可视化语法——Vega-Lite,获得了今年的 best paper,本文将根据其论文从多个角度介绍 Vega-Lite。论文地址

什么是 Vega-Lite

简而言之,Vega-Lite 是一种数据可视化的高级语法,能够快速定义一些基本的交互式数据可视化。

如果你听说过 Vega,那么光看 Vega-Lite 的名字就不难想到它们的关系。Vega-Lite 就是编译成 Vega 的更高级图形语法。

如下图所示,只要右边寥寥数行代码,就能定义一个散点图:

为什么要提出 Vega-Lite

首先,Vega-Lite 的目标是:

  • 通过一种规范来快速的表达你的可视化设计。
  • 提供了简洁的语法和图元便于你快速切换设计。

以前的东西做不到吗?其实也不是做不到,只是在有些方面还有很大的提升空间,Vega-Lite 就是在这个方向上很大的一步棋:

  • 低等级的可视化语法有更高的自由度,但往往高等级的可视化语言更受偏爱,主要因为其简洁,能快速做出东西,而且能生成低等级的语法供二次开发(Vega-Lite 可以编译成 Vega,而且所有代码都是 JSON 格式)。
  • 低等级的可视化语言没法提供现成的可视化方案(得你自己设计),而高等级的可以直接搜索或推断出适合你的可视化方案,支持在线查看效果(比如用 vega-lite 配套的 Voyager)。
  • 从交互方面来说,现有的高级语法在交互方面比较局限,而低等级语法可能为了定制化还得自己去处理事件回调(例如 D3),这对非专家很不友好且很容易出错。而 Vega-Lite 直接提供更简洁更具表现力的交互(一两行代码就能定义一个交互操作)。

为了实现以上的这些愿景,Vega-Lite 主要通过以下方式做出努力:

  • Algebra 用于将单个 view 合成多 view
  • Selection 用于交互选择判定
  • Transform 对数据、交互操作转换
  • Compiler 编译成 vega 语言,可供二次开发

接下来说说它怎么具体实现上面这四个概念的。

Vega-Lite 的具体语法

可以从两个方面分开介绍,分别是图形方面和交互方面。

Vega-Lite 的具体语法——图形方面

View 视图

首先,Vega-Lite 定义一个图形为一个 Unit,所以 Unit 是 Vega-Lite 里图形的最小单位。Unit 的定义是:

unit := (data, transforms, mark-type, encodings)
  • data 用来说明数据的来源,支持JSON格式和CSV格式
  • transform 定义了如何对原始输入数据进行处理
  • mark 指定了可视化图形
  • encoding 定义数据到可视化图形的映射规则

可视化中视觉通道的设计非常重要,所以其中,encoding 是比较重要的。

encoding := (channel, field, data-type, value, functions, scale, guide)

每个 Unit 可以通过一些 operators 变成 View,而 View 又可以通过一些 operators 变成复合的 View。

所以可以这么理解,你每个 operator 的单位是一个视图(可以是 Unit,也可以是 View),而一个 operator 的输出又可以作为下一个 operator 的输入,进而可以形成一个嵌套视图。

Operators 操作

刚才提到了 operators,你可以把一个 operator 理解成该以何种方式来整合视图。Vega-Lite 一共提供了四种 operators,分别是 Layer、Concatenation、Facet、Repeat。

Layer

Layer 很好理解,就是字面意思,将每个 View 重叠;

layer([unit1, unit2, ...], resolve)

Concatenation

Concatenation是将多个单视图水平放置或垂直放置;

hconcat([view1, view2, ...], resolve)
vconcat([view1, view2, ...], resolve)

Facet

Facet 是将多个单视图根据数据中的某个 field 进行排布;

facet(channel, data, field, view, scale, axis, resolve)

Repeat

Repeat 也很好理解,重复放置视图。

repeat(channel, values, scale, axis, view, resolve)

Vega-Lite 的具体语法——交互方面

任何交互中,选择一直是最重要的一个概念,所以 Vega-Lite 的所有交互也都围绕一个概念 —— Selection。首先看 Selection 的定义:

selection := (name, type, predicate, domain|range, event, init, transforms, resolve)
  • name: 属性名
  • type: point, list, intervel
  • predicate: 决定符合条件的最小集合(判定哪些东西被选进来)
  • event: 事件如何定义
  • transform: 操作已选择的元素

其中 transform 又最为重要,它定义了已选择之后的操作。这些操作可以进行随机的组合,且用户不需要定义操作之间的先后顺序,因为具体的顺序由编译器定义。

Vega-Lite 目前提供的交互操作一共有 5 种,分别是:project(fields, channels)、toggle(event)、translate(events, by)、zoom(event, factor)、nearest()。

Project

Project 用来重定义判定函数 predicate。

Toggle

Toggle 表示按下shift键,可以在之前交互结果上,继续进行交互。

Translate

Translate 用于改变交互事件的判断

Zoom 和 Nearest
  • Zoom操作主要用户视图的缩放
  • Nearest操作,会将整个视图根据元素的位置分割成Voronoi图,然后将距当前交互的元素最近的元素选择出来

其他交互

if-then-else

简单的条件判断逻辑。

把选择的数据作为另一个 view 的输入

上文也说到过,可以理解成视图的嵌套,也可以用作多视图协作。

根据选择的数据设置的比例尺

结合上一点,就可以把一个视图作为另一个视图的拓展。例如下图的 Overview + Detail 模式。

以上三个交互情形都支持与或非逻辑

多视图交互中的歧义

单视图中的交互,很可能在多视图中引发歧义。例如,在散点图矩阵中,如果在一个矩阵中进行 brush,其他的矩阵怎么配合协作?于是 Vega-Lite 又定义了四种交互协作模式,分别是 single、independent、union、intersect。

  • 默认是 single,用户在某个视图中进行交互,其他视图不会做出响应。
  • 其次是 independent,每个视图中的交互互不影响。
  • union 求并集,是指只要在多视图中任意一个子视图选中的部分,就被算作选中。
  • intersect 求交集,是指只有在多视图中都选中的部分,才被算作选中。

Vega-Lite 编译器

Vega-Lite 虽然也是用 JSON 写,但它可以编译成更低级的 Vega。其中它的编译器面临两个难点:

两个难点

  • 数据结构不对应
  • 由于vega-lite省略了很多细节设定,所以得编译器自己计算

四个步骤

编译器用四个步骤解决了以上两个难点。

  • 语法分析,消除歧义
  • 建立 vega-lite 和 vega 数据结构间的联系
  • 组合、优化数据结构,去除冗余
  • 汇编所有的元素

Vega-Lite 局限性

Vega-Lite 目前虽然已经发布,但依旧在紧锣密鼓的开发,主要是因其现在还没有达到其理想的效果,在以下两方面还有局限性:

  • 生成的可视化结果依赖于当前 Vega-Lite 的实现(未来可能会通过解释器层面解决,而不是编译器)
  • 本身固有的模式(通过 predicate function 抽象来解决)

总结

可视化在时下越来越重要,越来越多的行业需要对数据进行展示,而可视化的专家又少之又少,所以很需要一款像 Vega-Lite 一样,简单,智能的系统快速地实现可视化。这可能是可视化工具未来发展的方向 —— 快速实现,快速替换可视化方案,快速展示,接下来再进行二次开发,进而多次迭代。

由此可见 Vega-Lite 前景很大,不过可能还需要再沉淀、开发一段时间,我们拭目以待吧。

参考文献

  • Satyanarayan, A., Moritz, D., Wongsuphasawat, K., & Heer, J. (2016). Vega-Lite: A Grammar of Interactive Graphics. IEEE Transactions on Visualization and Computer Graphics, 2626(c), 1–1. http://doi.org/10.1109/TVCG.2016.2599030

本作品采用知识共享 署名-非商业性使用-禁止演绎 4.0 国际 许可协议进行许可。

December 03, 2016 10:29 AM

Vim - 适合自己的,才是最好的

Vim 被称为编辑器之神,是我用过之后才体会到的,用之前实在不敢对它做出什么评价。在大学时代,Vim 的大名就已如雷贯耳,但由于它陡峭的学习曲线,一直望而却步。等真正开始学习之后,发现并没有想象中的复杂,也没有所谓的瓶颈,只要在实际写代码中强迫自己使用就可以了,无形中就会形成习惯。最初的不适,换来的是效率的飞升。这和我当初学习双拼的感觉一样。下图是我的 Vim 界面:

我的 Vim 界面

学习方式

我一开始也是看了很多教程,这里我就不说具体的学习方法了,因为 Google 上一搜一大堆。

我只想谈一点:很多「过来人」告诫新手,一开始使用 Vim 一定不能使用插件,要从最纯净的 Vim 开始练习。他们认为一上手就使用别人的配置,很容易被别人影响,不能领会到自己配置 Vim,这种从无到有的感觉。虽然我也很喜欢折腾的感觉,但这对于学习、入门一个工具来说有点南辕北辙,我们学习一个工具就是为了用好它,或者用它来为我们服务。为什么要我们去适应它呢?也许这不符合 Vim 的哲学,但是我觉得:

  • Vim 存在这么多年,已经有很多优秀的 Vim 配置(比如:spf13-vim),可以为我们节省很多折腾的时间。不过如果你非常喜欢折(zuo)腾(si),那也可以从头开始。
  • 对于新手来说,自己的配置总是很不成熟,到头来还是得参考一些高手的配置。索性一开始用他们的,慢慢删改。
  • 从纯净版开始你会觉得很枯燥,Vim 远没别人口中、视频中所述的酷炫,效率不升反降。这很容易丧失进阶的兴趣。
  • 天下武功,唯快不破,这个时代求快。我不否认先夯实基础,再层层递进的学习方式,但针对不同的学习对象,不同的环境背景,我们还是应该采取最快、最有效的学习方式。

如果你学习 Vim 是为了体验学习的新鲜感,或者业余玩味,请忽略我上面的话。但如果你的最终目的是为了在实际中用到它,提升我们的工作效率,那你不妨和我一样,直接拉别人的配置下来,在 Shell 里输入 Vim 启动,开始写代码!

当时我找到了 k-vim,按照他的安装步骤,很简单就把 Vim 配置好了,启动 Vim,发现界面也很漂亮,嗯,这就是我要的效果。接着,我打开自己那两天正在写的项目,通过仅会的四个快捷键 HJKL 移动光标来查看文件。然后我仔细阅读了 k-vim 的 README 文件,把它提到的几个快捷键试了试,感觉很不错。接下来的几天,它的 README 网页我一直开着,遇到想要的快捷键一搜就搞定,虽然写代码的效率确实下降了很多,但对编辑器的使用越来越纯熟。一周之后我已经习惯用 Vim 来编程了。

接下来开始进一步研究 Vim,理解 Vim 的三种模式(正常模式、命令模式、视图模式),然后掌握如何配置插件快捷键就OK了。最关键一点就是要实战,强迫自己所有的操作只用键盘,强迫只用 Vim 作编辑器。

插件与快捷键

Vim 的插件可以通过 Vundle 来管理。(据说 vim-plug 也挺好用)

只需两步:

  • vimrc.bundles 文件中配置你想要的插件
  • 在 Vim 的命令模式中输入:BundleInstall

其他的命令有:

1
2
:BundleUpdate //更新插件
:BundleClean //删除插件

个人觉得必备的插件:

  • syntastic 多语言语法检查
  • YouCompleteMe 代码自动补全
  • ctrlp.vim 文件搜索,类似 Sublime Text 里面的 Cmd + P
  • vim-airline 状态栏增强
  • nerdtree目录树
  • vim-ctrlspace tab/buffer导航增强

而快捷键的学习方法,就是用到的时候去 Google,多用几次就记住了。如果它自带的快捷键用着不舒服,你完全可以自己重设,Vim 就是自由,不必拘泥条条框框。

哲学

非常推荐阅读 Stack Overflow 上的这篇回答:

What is your most productive shortcut with Vim?

这篇真正阐述了 Vim 作者当初设计 Vim 快捷键时的哲学,看懂这篇对 Vim 快捷键的掌握会更上一层。

感悟

在学习 Vim、使用 Vim 的过程中,我最大的感悟就是「适合自己的,才是最好的」

很多插件看起来很酷炫,快捷键几下就能实现很繁杂的操作,但是你不一定会有使用这个插件的需求,或者即使用也用的不多。有人总喜欢拿 IDE 和 Vim 比,我觉得这根本没有比较的必要,你两个都用也没什么问题。大的项目,复杂的文件结构和引用,你不用 IDE 而用 Vim,是浪费时间。而且一般 IDE 都提供了 Vim 模式,你仍可以在 IDE 中继续击键如飞。

用 Vim 体验的是一种轻便、自由、可塑的感觉。你可以根据自己的需求来培养 Vim,这就像恋(gao)爱(ji)一样是两个人互相适应的过程。互相习惯才能把效率最大化。


推荐链接


本作品采用知识共享 署名-非商业性使用-禁止演绎 4.0 国际 许可协议进行许可。

December 03, 2016 10:13 AM

November 16, 2016

子龙山人

21 天学会 Emacs(下)

TL;DR

21 天学会 Emacs 系列完美收官,你们再也不用追网剧了,我再也不用被人追着更新啦!

引子

去年十月份开始,我断断续续地录制了 10 集 Spacemacs Rocks,很快就收到了很多人的反馈,要求录制一些新手视频。(估计是我的 Emacs 操作闪瞎了很多人的双眼。。。)

我当时就立了一个 Flag,说在下一季视频中会有的。后面开始录第二季的第一集的时候,我又给自己挖了一个大坑,给这个系列取了一个很“响亮”的名字:21 天学会 Emacs。(其实我根本不需要录制 21 集这么多,10 集足够了。。唉,失策。。。)

5 个月后,21 天系列终于被我憋出来了。虽然视频是全部录制完成了,但是由于录制时间的仓促和准备时间不足,所以难免存在一些缺陷和不足。比如对新手不够友好,视频时间顺序安排不够合理,每集视频的时长安排也不够合理等等。

这篇文章,我准备写一个总结,给非 Vim/Emacs 党,Vim 党和 Emacs 新手党提供一些观看视频的指导。

也算是亡羊补牢吧。

但是,在学习之前,你一定要有学习的欲望,所以强烈推荐大家观看我录制的 Spacemacs Rocks 第一季的视频。

视频地址: http://list.youku.com/albumlist/show?id=26137579&ascending=1&page=1

非 Vim/Emacs 党学习路径

这里的“非 Vim/Emacs 党”,我指的是使用 IDE 和其它一些文本编辑器(Atom,Sublime,Notepad,VS Code)写代码的程序员。

这些同学,其实很多人已经对 IDE 依赖很强了,突然要学习一个上古神器的使用,其实还是有一点压力和难度的。

推荐这些同学,先花一周时间学习一点 Vim 的基础知识和命令行操作(git,shell等)。先理解 Window/Buffer等概念,因为在 Vim 和 Emacs 里面,这些概念跟你平时使用的其它编辑器是有差别的。(我读书少,你别骗我。。我是来学习 Emacs 的。。)

Vim 的学习曲线一开始是非常陡的,建议一周时间学习完 Vimtutor,能够使用 Vim 在终端里面打开一个文件,编辑,保存,然后正确退出即可。

学习 Vim 有助于你在今后学习 Emacs 的时候理解一些基本概念,因为两个神器出现的时期都差不多,有不少概念还是相通的。

之后,可以观看第二季视频的:1-7,如果你不想使用 Vim 的按键,第 7 个视频也可以不看。之后,你可以观看 18-21 的视频即可。

如果你和我一样也喜欢 Vim 的按键,推荐你从头到尾观看完所有的视频。(第 8 天的视频除外)

这里面有1个误区,因为视频已经全部录制完成了,所以你很容易一天看好几个视频。其实这样对你帮助并不大,你需要在观看完一集视频之后,跟着视频去动手操作,另外,需要认真阅读我在视频里面推荐的延伸阅读材料。切记不要一口气看完前 10 集,然后感觉啥也没学到。。。

Vim 党学习路径

如果你是一个资深的 Vim 党,学习 Emacs 也是手到擒来的。因为我本人就是一个有着 5 年 Vim 使用经验的资深 Vim 党。

对于 Vim 党而言,最高效的学习方式是观看我的视频 1-7(这里也可以不看视频,直接看社区维护的电子书,目前只更新到了第 7 天)。然后阅读完 Emacs Tutor,Learn x in y minutes: elisp 教程和 Evil 的 manual。

此时,你对于 Emacs 和 Evil 已经有一定的了解了,可以开始尝试把配置全部迁移到 Spacemacs,然后熟悉 Spacemacs 的按键和配置。这时候,你的手上已经有一个更好用的 Vim 了,你只需要每天不断地使用,然后遇到不爽的地方稍微 hack 一下,或者去论坛提问跟大家交流。

最后,你可以把视频的 18-21 的视频看完即可。

对于 Vim 党要学习 Emacs,强烈推荐使用 Spacemacs。而且要认真阅读 Spacemacs 自带的文档和 FAQ,这个对于学习怎么使用 Spacemacs 来说太重要了。

Emacs 新手党学习路径

这里的 “Emacs 新手党”,我指的是那些学习之,放弃之,再学习之,再放弃之的同学,还有学了一点点皮毛而且工作中也不怎么使用 Emacs 的同学。

这部分同学对 Emacs 可以说是“想说爱你不容易”。。。我最早开始学习 Emacs 的时候也有一段比较长的平台期,感觉迟迟没有入门。

这都是正常的,放宽心态即可。使用 Emacs 要达到如鱼得水的地步,认真学习 Emacs Lisp 肯定是必须的,而且要对 Lisp 写代码有激情。

推荐这部分同学,可以暂时不用看我的视频,认真去研习 Introduction To Elisp 内容,至少先看完 1-5 章。

如果有兴趣学习 Spacemacs,可以观看一下我的视频的第 11-14 集,也可以直接阅读 Spacemacs 的官方文档。因为此时看视频,可能对于已经有经验的 Emacs 党来说,时间投入产出比不是很高。但这也不意味着看我的视频完全没有任何帮助。要看你是一个主动学习型,还是一个被动学习型的人啦。

Emacs 新手党需要一份高手的配置,需要一点耐心去学习 Elisp,然后在平时不断地使用 Emacs(写代码,写文档,写博客,GTD 等)。

总结

最后,我在我的博客上会把 21 天视频的每一集的大纲简单说明一下,大家可以点击 LearnEmacs 版块去查看,这样有助于大家提前知道视频的主要内容,不致于浪费时间观看一些对自己价值不大的内容。

最后的最后,欢迎大家加入 emacs-china 社区,多多参与 21 天学会 Emacs 的电子书的编写,为今后的同学学习 Emacs 提供一些帮助。

这样我们这个世界又会因为你的一个小小的举动变得更加美好啦。

November 16, 2016 09:31 AM

优化 Cocos2d-x 游戏性能

这篇文章从理论的角度和大家一起来探讨一下如何优化 Cocos2d-x 游戏的性能,这里面提供的优化技巧并不局限于使用 Cocos2d-x 引擎制作的游戏,也适用于其它任何引擎制作的游戏。

Golden rules

查找游戏性能瓶颈,然后优化瓶颈

当我们在做任何性能优化之前,请牢记这条。造成你的系统性能瓶颈的代码通常只有那 20%的代码,切莫胡乱优化。

总是使用工具来查找性能瓶颈,而不是靠猜

目前社区里有许多工具可以用来查找图形应用的性能问题。

查找 GPU 性能瓶颈的工具

  1. 使用 Xcode OpenGL ES Profiler。

    文档链接地址: https://github.com/rstrahl/rudistrahl.me/blob/master/entries/Debugging-OpenGL-ES-With-Xcode-Profile-Tools.md

    这里是苹果官方的参考文档: https://developer.apple.com/library/ios/documentation/3DDrawing/Conceptual/OpenGLES_ProgrammingGuide/ToolsOverview/ToolsOverview.html

    如果你想 profiling 特定 GPU 的移动设备的图形性能,我们可以使用这些 GPU 制造商提供的工具:

  2. 对于 ARM Mali GPU,可以使用 mali graphics debugger: http://malideveloper.arm.com/resources/tools/mali-graphics-debugger/
  3. 对于 Imagination PowerVR GPU,可以使用 PVRTune: https://community.imgtec.com/developers/powervr/tools/pvrtune/
  4. 对于 Qualcomm Adreno GPU,可以使用 adreno GPU profiler: https://developer.qualcomm.com/software/adreno-gpu-profiler

使用这些工具可以让你更清楚地知道你的图形渲染管线哪个阶段遇到瓶颈了,是顶点处理阶段,还是像素着色阶段。

但是,请记住,一般你的游戏的性能问题可能并不在 GPU,而在 CPU

查找 CPU 性能瓶颈的工具

  1. Mac 平台可以使用 Xcode 的 Time Profiler 工具: https://developer.apple.com/library/ios/documentation/DeveloperTools/Conceptual/InstrumentsUserGuide/MeasuringCPUUse.html
  2. Windows 平台可以使用 Visual Studio 的 CPU profiler: https://blogs.msdn.microsoft.com/visualstudioalm/2015/10/29/profile-your-cpu-in-the-debugger-in-visual-studio-2015/
  3. Cocos2d-html5 和 Cocos Creator 的用户,可以使用 Chrome(或者 Firefox) 自带的 timeline 工具和 CPU profile 工具。

熟悉你的移动设备和你使用的游戏引擎

熟悉你的移动设备使用的 GPU 和 CPU 的型号, Android 手机可以安装一个应用“GPU-Z”可以非常方便地查看到这些信息,而到目前为止iOS 设备统一使用的都是 PowerVR 的 GPU。如果你在测试游戏的过程中,发现其它手机都没有问题,但是某些具有同种类型的 GPU 的设备性能表现都不佳,此时你可能需要留意一下针对特定 GPU 的优化技巧了。

同样的,了解你所使用的游戏引擎的局限也是非常重要的。你需要清楚地知道你所使用的游戏引擎是如何组织图形渲染命令的,这些命令又是如何处理 Batch Draw 的。以及,我们需要如何组织我们的纹理和游戏节点对象,这样才能最大限度地利用引擎提供的自动批处理功能。

如果你知道这些内容,那么你就可以避免一些常见的性能瓶颈。

记住"足够好"原则

什么是“足够好“原则?如果游戏玩家的眼睛不能区分不同质量的渲染结果,那么总是使用更省资源和计算更简单的实现方法。

我们都知道,一张像素格式为 RGBA4444 的图片在显示的时候,显示效果肯定是不如一张像素格式为 RGBA8888 的图片的。但是,如果我们使用 RGBA4444 加上“抖动”算法,那么玩家一般是很难分辨出来的,特别是如果图片比较小,或者设备分辨率比较低的时候。

同样的原则也适用于声音,比如低采样率的声音比高采样率的声音更省内存和解码资源,如果玩家分辨不出差别,那么也要选择更低采样率的声音资源,对于单通道和双通道声音也同理。

Common Bottlenecks

按照经验来说,一般你的游戏的性能瓶颈都是出现在 CPU 而不是 GPU 上面。

CPU 瓶颈通常跟 Draw call 数量和你的游戏循环的复杂度相关

所以,你需要尽可能地降低你的游戏的 Draw call 数量,最大限度地利用批次渲染来减少 Draw call 数量。 Cocos2d-x 3.x 包含了自动批处理功能,但是它需要你合图,并且生成的图形渲染命令必须相邻,且有相同的 material id。

对于游戏中出现的大量对象,比如子弹,鱼群等游戏对象,需要使用内存池技术来避免在游戏循环中产生大量的 IO 操作。同时,对于游戏中需要用到的外部资源,比如纹理图集,声音文件和 TTF 文件等,尽量采用预先加载的方式来处理。

同时也千万要避免在游戏循环里面做非常复杂的计算,因为游戏循环每帧都会执行,所以这些耗时的操作很可能让你的游戏的 FPS大大降低。

GPU 瓶颈通常局限于 Fillrate(Overdraw) 和 Bandwidth

如果你使用 Cocos2d-x 制作 2D 游戏,你一般不会编写复杂的 Shader,所以通常你不太会遇到 GPU 相关的性能问题。

但是,Overdraw 这个问题会导致你的 GPU 很容易碰到带宽的瓶颈,从而降低你的图形性能。所谓 Overdraw,指的是在图形渲染管线中,很多像素的着色对于最终显示在屏幕上的颜色没有帮助,这些多余的计算和处理是浪费的,最重要的就是浪费带宽,因为它们需要从主存中采样纹理坐标。

尽管,现代的移动端 GPU 都有实现 TBDR(Tile-based Defered Rendering),但是只有 PowerVR的 HSR(隐藏表面剔除)可以极大地解决 Overdraw 的问题。其它的 GPU 厂商都只实现了 TBDR + Early-Z,如果你按照从前往后的顺序提交不透明几何图元给 GPU 处理,那么这些 GPU 的 Overdraw 问题也会减少。

但是,我们知道 Cocos2d-x 引擎总是按照从后往前的顺序去提交图形渲染命令的,因为在 2D 里面,大量的图片都是带有透明像素的,为了保证 blending 的正确性,就必须保持这种顺序的渲染命令提交。即使按照这种顺序去提交渲染命令,PowerVR 的 HSR 也可以在片断着色之前剔除掉不需要计算的像素。这也是为什么同样的 Cocos2d-x 游戏在很垃圾的 iPod 上面性能也不错,但是在某些 Android 旗舰机上面性能却表现得一团糟的原因。

注意: 通过使用工具, 预先将 2D 图片三角化,可以提高 Fillrate。

具体做法可以参考 TexturePacker 作者写的文章: https://www.codeandweb.com/texturepacker/tutorials/cocos2d-x-performance-optimization

Simple checklist to make your Cocos2d-x game faster

  1. 尽可能地使用批次渲染(Batch Draw)
  2. 按照经验,尽可能把你的 Draw 数量控制在 50 以下
  3. 减少 32 位未压缩纹理的使用,尽量使用 16 位且压缩过的纹理格式。
  4. 尽可能地使用支持硬件解码的压缩纹理:比如 iOS 平台使用 PVRTC 纹理, 在安卓平台上面使用 ETC格式的纹理。目前所有的 Android 设备都是支持 ETC1 格式的纹理的,但是此纹理格式不支持 Alpha,所以你需要修改一下引擎以使用 ETC1 格式的纹理。
  5. 不要使用系统字体来动态显示你的游戏中的分数等信息,请使用 BMFont 字体。
  6. 请使用对象池和预加载技术来避免临时卡顿。
  7. 使用 armeabi-v7a 架构来编译 Android 的 SO,因为在此架构下面 Cocos2d-x 会启用 neon 指令集,矩阵运算的效率会大大提高。
  8. 不要使用动态光照,尽量使用 bake 光照。
  9. 避免在 pixel shader 里面做非常复杂的计算
  10. 避免在 pixel shader 里面使用 discard 和 alpha test,因为这样会破坏 GPU 自身的 depth testing 优化,比如 PowerVR 的 HSR。

November 16, 2016 09:31 AM

21天学会 Emacs(上)

经过了漫长的 3个月等待,《21 天学会 Emacs 系列》的上半部分总算是完结了。本来打算每周一更的视频,也由于各种原因未能及时更新,不过好在 Emacs 党的热情不减,我终于还是不负众望,坚持下来了。

如果你现在想学习 Emacs,而且不知道怎么入门,也没人带,如果你还没有观看过我的视频,那么赶紧点击我的网站首页的LearnEmacs 版块开始观看视频吧。

Happy Hacking!

2016-06-12-mastering-emacs-in-21-days.jpg

为什么我要录制这个系列视频

如果你有听过我在“代码时间”的采访: http://codetimecn.com/2016/05/01/spacemacs/ , 那么你应该可以了解一些原因。

Emacs 和 Vim 一直在国内被妖魔化,也常被人说成是装逼利器。Vim 社区近年来在国内发展的还不错,因为服务端程序员,Ruby 程序员和一些 Linux 程序员的努力, Vim 社区变得越来越好。再加上《Practical Vim》一书的出版,更是让 Vim 的学习热情居高不下。

但是 Emacs 却一直很小众,国内惟一的 Emacs 社区是水木清华社区,而这个社区也不是很活跃。有不少 Emacs 民间高手,但是他们也只钟情于写 Package。

Emacs 大神陈斌 写了《一年成为 Emacs 高手》, 这应该是中文社区最好的Emacs学习方法论了,没有之一。我本人也从这篇文章中获益良多,再次强烈推荐。

但是,《一年成为 Emacs 高手》毕竟是以方法论为主,针对具体实际操作的指导非常少。而新手在学习 Emacs 的过程中,可能最难的一步就是入门。如果入门都搞不定,这些方法论也很难发挥作用。

我录制的这个系列视频就是要弥补这个空白,打造一套亲民的 Emacs 入门资料。(相信不会让大家失望)

我真的可以 21 天学会 Emacs 吗?

答案是 NO!如果你妄想通过观看 21 集视频就能学会 Emacs,这是不可能的。你甚至都不可能通过观看 21 个 Javascript 视频就学会 Javascript!

如果你已经参加工作了,平时工作也比较忙,那么一年学会Emacs 是一个合理的目标。如果你是一个学生党,平时有非常多的时间去折腾,那我相信只要方法得当 2-3 个月学会 Emacs 也是有可能的。

在有了合理的目标之后,你就不会在学习21天后变成“从入门到放弃”了。

本系列视频旨在降低 Emacs 的入门门槛,同时指明入门后学习的方向。你除了认真观看视频并实践视频中的内容之外,对于视频里面提到的链接和文章,都需要花大量的时间去研习,去实践。

21 天只是一个噱头,背后需要你付出大量的努力,但是,最后一旦你学会 Emacs,你会发现这一切都是值得的。

给 Emacs 新手的建议

  1. 先存活下来,这个非常重要,一定要把 Emacs 当作你日常使用的文本编辑器
  2. 在学习的过程,请不要只看不动手,实践出真知
  3. 对于学习过程中遇到的问题,可以去 emacs-china.org 去提问。
  4. 对于视频中提到的链接和文章, 一定要抽时间去看,不要执着于“抄配置”,更不要配置一些自己用不到的东西。(这会浪费你大量的时间)
  5. 本系列视频是有对应电子版的,这是一本社区维护的电子书, 之前一直由li-xinyang 同学在维护,作为新手,你也应该当作学习笔记去贡献内容。
  6. 有时间多读读 An Introduction to Programming in Emacs Lisp ,你会学到很多东西的。

后续学习建议

  1. 阅读《一年成为 Emacs》高手,实践里面的方法论
  2. 积极融入社区,emacs-china.org, reddit,github
  3. 观看后续 Spacemacs 视频
  4. 阅读高手的 Emacs 配置
  5. 阅读一些常用的 package 的源码和 wiki

结语

最后祝大家早日驾驭 Emacs,Happy Hacking !

November 16, 2016 09:31 AM

拥抱 HTTPS 和 HTTP/2

为了方便国内的 Emacs 党交流,前段时间跟社区里面几个小伙伴一起弄了一个discourse论坛,也就是现在的emacs-china.org 。当时有位 xuchunyang 同学建议论坛应该要支持 HTTPS,其实之前我使用 Spacemacs 的时候,就有人在 Reddit 里面建议从 Melpa 安装 Package 的时候应该要用 HTTPS,否则你的 Emacs 可能被人“下毒”。但是由于国内墙的原因,我当时把 HTTPS 禁用了。

现在,xuchunyang 同学搭建了一个国内的 ELPA 镜像,我也在上周末抽空让它支持了 HTTPS,欢迎 Emacser 试用。

我为什么要使用 HTTPS

因为 HTTP 请求是没有加密的,而我在上网的过程中,我的隐私可能会被窃取。另外,我访问的网站没有完整性验证,我可能会访问一些钓鱼网站,或者我访问的网站会被劫持,然后给我推送一些广告,一般运营商和 XX 智能路由器特别喜欢干这种事情。。。

HTTPS 是互联网下一个阶段的重要进化,所有的网站都应该上 HTTPS

关于为什么要使用 HTTPS 的更多理由,推荐阅读:https://https.cio.gov/everything/

你为什么不用 HTTPS

我总结了一下,可能有以下 3 个原因:

  1. 担心 HTTPS 的维护成本过高,HTTPS 证书需要花钱买
  2. 担心 HTTPS 会让你的网站访问速度变慢
  3. 破罐破摔,我一个静态博客,也没啥值钱的东西,用不用 HTTPS 都无所谓

其实这种观点是错误的,因为现在采用开源技术让你的网站支持 HTTPS 是很简单的,而且完全免费,维护成本也非常之低。

另外,开启 HTTPS 后,就可以进一步开启 HTTP/2 的支持了,虽然 HTTPS 会比 HTPP/1.1 多一次验证的过程,但是 HTTP/2 可以弥补这些损耗,让你的网站访问速度更快。另外,谷歌和火狐浏览器都声称只采用 TLS 的方式支持 HTTP/2。

关于 HTTP/2的更多信息,推荐阅读:https://http2.github.io/faq/

如何为自己的网站添加 HTTPS

由于我的个人博客是静态网站,Web 服务器使用的是 nginx,所以我这里提供的方法对于nginx 的用户可能比较适用。但是,我相信其它 web 服务器应该是也很容易配置的。

首先,为了支持 HTTP/2,你的 nginx 的版本需要升级到 1.9.5+ 版本,我默认升级到了最新版。

其次,你需要有一个 HTTPS 的证书,我这里使用的是开源免费的 letencrypt,关于 ubuntu 怎么配置使用 letencrypt,可以参考本文Reference列举的文章,这里就不细讲了。

最后,你需要配置你的 nginx,注意把 HTTP 的请求都通过 301 重定向给 HTTPS

遇到的一些问题和解决方案

  1. 由于我的网站之前采用七牛CDN来托管博客上面的图片,但是由于七牛CDN使用的是 HTTP 协议,这样会导致我的网站不是“full site https”,在浏览器的地址栏会看到一个警告,显示我使用的是 Mixed content。虽然七牛 CDN 也支持 HTTPS,但是需要域名备案,另外 letencrypt的 HTTPS 证书它貌似并不支持,所以作罢。我后来全部改成用我自己的服务器来托管图片了,反正不限流量,我这个个人博客访问量不高,还是能够接受的。
  2. 之前的评论系统我使用的是“多说”,但是由于多说对于 HTTPS 支持不是很好,而且经常抽风,我现在果断换了 disqus 了。 (更新2016-10-15:多说又支持 https,所以我又换回来了,disqus 访问速度实在是太慢了。。。)
  3. 因为很多静态博客的主题都是使用的 Google 字体,由于国内访问速度会受到影响,我换成了 360 的 Google 字体镜像,http://libs.useso.com/ 但是,这个镜像不支持 Https,所以我又使用了科大博客提供的镜像: https://servers.ustclug.org/2014/06/blog-googlefonts-speedup/

结语

网站添加 HTTPS 以后,可以用专业的网站安全性评测工具评测一下,就像刚安装完系统要给系统打分一下。

打分网站: https://www.ssllabs.com/ssltest/index.html

2016-06-08-embrace-https-and-http2_rate-https-site.png

让我们一起拥抱 HTTPS 和 HTTP/2吧!

November 16, 2016 09:31 AM

理解 Javascript This 关键字

在我最早接触 Javascript 的时候,This 关键字着实让我摸不着头脑。还有与 This 相关的一些函数,比如 bind,call 和 apply 也是难以掌握。本文试图用几个简单的例子来理解 This 关键字。

本文内容大纲:

  1. This 绑定的内容与函数无关,而与函数的执行环境有关。
  2. 函数的 This 绑定的内容可以通过 bind,apply 和 call 函数来动态进行修改。
  3. 巧用闭包可以消除不必要的 This 动态绑定,提高代码的可读性。

This 绑定内容与函数无关,而与执行环境有关

上篇文章 中,我们提到了,一个函数在调用时会创建一个活动对象,而此活动对象中还包含一个非常重要的信息,即 This 变量。

我们先看下面这个例子:

var name = "zilong";

var myfunc = function() {
    console.log(this.name);
}

myfunc();

当我们调用 myfunc 函数时,Js 引擎会创建一个执行上下文,同时会初始化一个作用域链,此作用域链为:

[[scope chain]] = [
{
    Active Object {
         arguments: ...
         this: [global Object],
         ...
    },
     global Object: {
          name: 'zilong'
          ...
     }
}
]

所以,当我们执行 console.log(this.name)的时候,this 绑定的是全局对象,而我们之前定义的 name 就是属于全局变量。

我们再看一下下面这个例子:

var name = "zilong";
var sex = "man";

var myfunc = function(name) {
    this.name = name;
}

myfunc.prototype.print = function() {
    console.log(this.name);
    console.log(this.sex);
    console.log(sex);
}

var obj = new myfunc("hello");
obj.print();

RESULTS:

hello
undefined
man

当我们执行 obj 对象的 print 函数的时候,我们的执行上下文的作用域链是这样的:

[[scope chain]] = [
{
    Active Object {
         arguments: ...
         this: obj,
         ...
    },
     global Object: {
          name: 'zilong',
          sex: 'man',
          ...
     }
}
]

从这个作用域链,我们可以很清楚地知道上面代码的输出结果。

This 绑定的内容可以被动态修改

同样的是上面那个例子,我们稍微修改一下:

var name = "zilong";
var sex = "man";

var myfunc = function(name) {
    this.name = name;
}

myfunc.prototype.print = function() {
    console.log(this.name);
    console.log(this.sex);
    console.log(sex);
}.bind(this);

var obj = new myfunc("hello");
obj.print();

RESULTS:

zilong
man
man

我们通过给 myfunc.prototype.print 函数添加了 bind 的调用,我们发现输出的结果完全不同了。因为此时的 this 绑定的是 global 对象了。

同样的,如果我们把代码改成下面的样子:

var name = "zilong";
var sex = "man";

var myfunc = function(name) {
    this.name = name;
}

myfunc.prototype.print = function() {
    console.log(this.name);
    console.log(this.sex);
    console.log(sex);
};

var obj = new myfunc("hello");
myfunc.prototype.print.call(obj, "hello");

RESULTS:

hello
undefined
man

这个输出结果与我们直接调用 obj.print 是一样的。

但是如果我们改成:

  var obj = new myfunc("hello");
  myfunc.prototype.print.call(this, "hello");
// 下面的 window 和 this 是等价的。
//  myfunc.prototype.print.call(window, "hello");

输出结果会是:

RESULTS:

zilongshanren
man
man

使用 bind 可以显式指定函数调用时的 this 绑定,而使用 call 可以指定 this 对象的指向,另外,还可以使用 apply 来修改 this 的绑定。call 和 apply 的区别就是 call 后面传参使用的是逗号分隔的参数,而 apply 传递的则是一个参数数组。

巧用闭包消除 This 动态绑定,提高代码可读性

假设我们要把一个外部环境的 this 变量传递到一个内部函数去使用,一般我们会这么做:

var a = 10;
var obj = {
    a : 1,
    b : 2,

    sum : function() {
        var addA = function(a) {
            return  this.a + a;
        }.bind(this);

        return addA(this.b);
    }
}

console.log(obj.sum());

RESULTS:

3

在声明 addA 的时候,我们使用了 bind(this),那么 addA 函数内部的 this.a 指向的是 obj 对象的 a 变量。如果我们不使用 bind 的话, this 默认指向的是 window 对象,那么输出的结果就是 12 了。

一般情况下面,此时 sum 函数里面的 3 个 this 就容易把人搞晕了,我们通常会通过添加一个 self 或者 that 局部变量来增加代码的可读性,同时也不用手动去调用 bind 函数。

var a = 10;
var obj = {
    a : 1,
    b : 2,

    sum : function() {
        var self = this;

        var addA = function(a) {
            return  self.a + a;
        };

        return addA(this.b);
    }
}

console.log(obj.sum());

RESULTS:

3

这里面的 self 变量利用了闭包的特性,同时让代码更加具有可读性,也消除了不必要的 bind(this)调用。

小结

其实 This 并不复杂,只是跟我们熟悉的面向对象语言有差异罢了,理解了执行上下文,作用域链和活动对象的概念,This 也就明了了。

November 16, 2016 09:31 AM

理解 Javascript 作用域和作用域链

上篇文章 在介绍 Javascript 闭包的时候提到了“闭包创建时所处的环境信息”,但是并没有说明这些信息到底是什么。

也多亏了读者的提醒,我对于 Js 闭包的理解还是太肤浅了。这篇文章除了介绍 Js 的作用域和作用域链外,我还会讨论变量提升(var hositing) 这个问题。

Javascript 作用域

在 Javascript 中,只有局部作用域和全局作用域。而只有函数可以创建局部作用域,像 if,for 或者 while 这种块语句是没办法创建作用域的。 (当然 ES6 提供了 let 关键字可以创建块作用域。)

Javascript 的这种特性导致 for 循环里面创建闭包时会产生让人意想不到的结果。比如下面这个例子:

var i = 20;
var makeLogger = function() {
    var funcs = [];
    for(var i = 0; i < 10; ++i){
        funcs[i] = function() {
            console.log(i);
        }
    }
    return funcs;
}

var loggers = makeLogger();
for(var i = 0; i < 10; ++i){
    loggers[i]();
}

RESULTS:

10
10
10
10
10
10
10
10
10
10

上面的输出结果,大致原因就是 for 循环里面的变量的作用域是整个函数的,循环内部创建的一系列闭包引用的是同一个变量 i,而在 for 循环结束后,这个 i 的值变成了 10。所以当我们调用这些内部函数的时候,就会输出 10 了。

现在这样讲可能还是不够清楚,在我们了解作用域链和 Javascript 的执行原理后,就更容易理解了。

Javascript 作用域链

  1. 当 Js 里面 声明 一个函数的时候,会给该函数对象创建一个 scope 属性,该属性指向当前作用域链对象。
  2. 当 Js 里面 调用 一个函数的时候,会创建一个执行上下文,这个执行上下文定义了函数解释执行时的环境信息。每个执行上下文都有自己的作用域链,主要用于变量标识符的解析。
  3. 在 Js 引擎运行一个函数的时候,它首先会把该函数的 scope 属性添加到执行上下文的作用域链上面,然后再创建一个 活动对象 添加到此作用域顶端共同组成了新的作用域链。活动对象包含了该函数的所有的形参,arguments 对象,所有的局变变量等信息。
  4. 当解释执行函数的每一条语句的时,会依据这个执行上下文的作用域链来查找标识符,如果在一个作用域对象上面没有找到标识符,则会沿着作用链一直向上查找,这一点类似于 Js 的原型继承的属性查找机制。

让我们来看几个具体的例子:

var name = 'zilongshanren';
function echo() {
    console.log(name);
    var name = 'hello';
    console.log(name);
}

echo();

RESULTS:

undefined
hello

要理解上面的代码的输出结果,我们可以按照上面提到的 4 点来解释:

  • 在声明 echo 函数时,此时的作用域链是(我们假设 scope chain 是一个作用域对象数组)
[[scope chain]] = [
{
     global Object: {
          name: 'zilongshanren'
          ...
     }
}
]

echo 函数的作用域属性指向此 scope chain 对象。

  • 当调用 echo 函数时,会创建一个执行上下文,同时把 echo 的作用域添加到执行上下文的作用域链上。同时创建一个活动对象并添加到该作用域链的顶端。此时的作用域链是:
[[scope chain]] = [
{
    Active Object {
         name: undefined,
         arguments: ...
         ...
    },
     global Object: {
          name: 'zilongshanren'
          ...
     }
}
]
  • 当解释执行函数的第一条语句的时候,查找 name 变量,在活动对象中找到了,于是输出 undefined。然后执行 var name = 'hello',此时变量 name 的值为 hello。最后解释执行 console.log(name)的时候就输出了 hello.

这个例子可能比较简单,因为它没有使用闭包。

我们接下来分解一下本文开头的例子。

  • 当定义 makeLogger 函数时,makeLogger 函数的作用域为:
[[scope chain]] = [
{
     global Object: {
          i: 20,
          ...
     }
}
]
  • 在 for 循环里面定义闭包函数的时候,此时的作用域链是:
[[scope chain]] = [
{
     makeLogger local scope object : {
        i: undefined,
        funcs: [],
     },
     global Object: {
          i: 20,
          ...
     }
}
]

并且此时 funcs…funcs的 scope 都指向该 scope chain。

  • 当调用 makeLogger 函数的时候,创建一个执行上下文。把 makeLogger 函数的作用域链加到执行上下文中,并且创建一个活动对象添加到作用域链的顶端,此时的 scope chain 为:
[[scope chain]] = [
{
     makeLogger active object: {
          funcs: undefined,
          i: undefined,
          arguments: ...
     },
     global Object: {
          i: 20,
          ...
     }
}
]
  • 当执行完 makeLogger 函数的时候,此时的作用域对象变成了:
[[scope chain]] = [
{
     makeLogger local scope object : {
        i: undefined,
        funcs: [function object ...],
     },
     global Object: {
          i: 20,
          ...
     }
}
]

这里的 funcs 函数还会生成闭包对象,它包含了 makeLogger 局部作用域的变量的值,即 i=10.

下图是 V8 引擎中 funcs 函数及其闭包的截图:

2016-03-13-understand-javascript-scope-and-scope-chain_closure-example.png

  • 最后遍历执行所有的 loggers 的时候,会依次为每一个 loggers 函数创建一个执行上下文,每一个执行上下文的作用域链为:
[[scope chain]] = [
{
     loggers function active object : {
         arguments: ...
     },
     makeLogger local scope object : {
        i: 10,
        funcs: [function object ...],
     },
     global Object: {
          i: 20,
          ...
     }
}
]

当执行 loggers 函数的 console.log(i)的时候,它会沿着此时的作用域链进行变量查找,于是找到了 i=10. 所以我们输出的结果就是 10.

变量提升

我们看一个例子:

var name = 'zilongshanren';
function echo() {
    name = "hello";
    console.log(name);
    var name;
    console.log(name);
}
console.log(name);

echo();

RESULTS:

zilongshanren
hello
hello

调用 echo 函数的第一行 name = "hello"时并不是对全局变量 name 进行重新赋值,而是对函数内部声明的变量 name 进行赋值。所以,在 echo 函数声明之后,调用 console.log(name)输出的还是 zilongshanren。

echo 函数内部的 name 变量“使用在前,而声明在后”,这就是所谓的变量提升。

如果从我们前面提到的变量作用域和作用域链来解释这个行为肯定是更容易理解的。

正因为函数内部的变量声明会发生“提升”副作用,所以,最好的做法就是把函数需要用到的局部变量都放在函数开头进行声明,避免产生不必要的混淆。

小结

JavaScript 中的函数运行在它们被定义的作用域里,而不是它们被执行的作用域里。理解作用域和作用域链对于理解闭包和变量提升这种奇葩特性非常有帮助。 本文可能有些地方讲的还不是非常清楚,读者可以读一读后面的参考链接,相信会有助于理解。

November 16, 2016 09:31 AM

理解 Javascript 的闭包

因为最近几个月一直在做 Cocos Creator 这个项目,大部分时间都在与 Javascript 打交道,所以接下来我有必要写几篇文章介绍一下 JS 里面几个比较让人迷惑的地方:闭包,变量作用域,变量提升和 this 绑定。

今天这篇文章我们来聊一聊闭包。

什么是闭包?

闭包是一个函数,它在函数内部创建,并且携带了自身创建时的所处环境信息(比如变量信息和其它函数信息)。

上面这段话是引用至 MDN,它很清楚地说明了什么是闭包。

闭包 = 函数内部创建的函数(或者简称内部函数) + 该函数创建时所处环境信息

所以闭包并不等于匿名函数,虽然也有人称这些在函数内部创建的函数为闭包函数,但是我觉得其实并不准确。

我们看一下下面这段代码:

function init() {
    var name = "Zilongshanren"; // name 是在 init 函数里面创建的变量
    // displayName() 是一个内部函数,即一个闭包。注意,它不是匿名的。
    function displayName() {
        console.log(name);
    }
    //当 displayName 函数返回后,这个函数还能访问 init 函数里面定义的变量。
    return displayName;
}
var closure = init();
closure();

RESULTS:

Zilongshanren

displayName 是一个在 init 函数内部创建的函数,它携带了 init 函数内部作用域的所有信息,比如这里的 name 变量。当 displayName 函数返回的时候,它本身携带了当时创建时的环境信息,即 init 函数里面的 name 变量。

闭包有什么作用?

在理解什么是闭包之后,接下来你可能会问:这东西这么难理解,它到底有什么用啊?

因为在 Js 里面是没有办法创建私有方法的,它不像 java 或者 C++有什么 private 关键字可以定义私有的属性和方法。 Js 里面只有函数可以创建出属于自身的作用域的对象,Js 并没有块作用域!这个我后面会再写一篇文章详细介绍。

编程老鸟都知道,程序写得好,封装和抽象要运用得好!不能定义私有的属性和方法,意味着封装和抽象根本没法用。。。

不能定义私有的东西,所有变量和函数都 public 显然有问题, Global is Evil!

闭包是我们的救星!

我们看一下下面这段代码:

var makeCounter = function() {
    var privateCounter = 0;
    function changeBy(val) {
        privateCounter += val;
    }
    return {
        increment: function() {
            changeBy(1);
        },
        decrement: function() {
            changeBy(-1);
        },
        value: function() {
            return privateCounter;
        }
    }
};

var counter1 = makeCounter();
var counter2 = makeCounter();
console.log(counter1.value()); /* Alerts 0 */
counter1.increment();
counter1.increment();
console.log(counter1.value()); /* Alerts 2 */
counter1.decrement();
console.log(counter1.value()); /* Alerts 1 */
console.log(counter2.value()); /* Alerts 0 */

RESULTS:

0
2
1
0

这里面的 privateCounter 变量和 changeBy 都是私有的,对于 makeCounter 函数外部是完全不可见的。这样我们通过 makeCounter 生成的对象就把自己的私有数据和私有方法全部隐藏起来了。

这里有没有让你想到点什么?

哈哈,这不就是 OO 么?封装数据和操作数据的方法,然后通过公共的接口调用来完成数据处理。

当然,你也许会说,我用原型继承也可以实现 OO 呀。没错,现在大部分人也正是这么干的,包括我们自己。不过继承这个东西,在理解起来总是非常困难的,因为要理解一段代码,你必须要理解它的所有继承链。如果一旦代码出 bug 了,这将是非常难调试的。

扯远了,接下来,让我们看看如何正确地使用闭包。

如何正确地使用闭包?

闭包会占用内存,也会影响 js 引擎的执行效率,所以,如果一段代码被频繁执行,那么要谨慎考虑在这段代码里面使用闭包。

让我们来看一个创建对象的函数:

function MyObject(name, message) {
    this.name = name.toString();
    this.message = message.toString();
    this.getName = function() {
        return this.name;
    };

    this.getMessage = function() {
        return this.message;
    };
}

var myobj = new MyObject();

var myobj = new MyObject(); 每一次被调用生成一个新对象的时候,都会生成两个闭包。如果你的程序里面有成千上万个这样的 MyObject 对象,那么会额外多出很多内存占用。

正确的做法应该是使用原型链:

function MyObject(name, message) {
    this.name = name.toString();
    this.message = message.toString();
}
MyObject.prototype.getName = function() {
    return this.name;
};
MyObject.prototype.getMessage = function() {
    return this.message;
};

var myobj = new MyObject();

现在 MyObject 原型上面定义了两个方法,当我们通过 new 去创建对象的时候,这两个方法只会在原型上面存有一份。

闭包的性能如何?

闭包也是一个函数,但是它存储了额外的环境信息,所以理论上它比纯函数占用更多的内存,而且 Js 引擎在解释执行闭包的时候消耗也更大。不过它们之间的性能差别在 3%和 5%之间(这是 Google 上得到的数据,可能不是太准确)。

但是,闭包的好处肯定是大大的。多使用闭包和无状态编程,让 Bug 从此远离我们。

小结

面向对象是穷人的闭包(OO is a poor man's closure.)

理解了闭包,你就能理解大部分 FP 范式的 Js 类库及其隐藏在背后的设计思想。当然仅有闭包还不够,你还需要被 FP 和无状态,lambda calculus 等概念洗脑。

November 16, 2016 09:31 AM

Spacemacs Rocks

Table of Contents

Spacemacs Rocks!

本文是对 Spacemacs Rocks 第一季系列视频的一个小节,同时打个广告,欢迎对 Emacs 和 Spacemacs 感兴趣的朋友加入 emacs-cn 的 slack 聊天组 https://slackin-emacs-cn.herokuapp.com/

前言

Spacemacs 这个社区维护的 Emacs 配置,集成了 Vim 和 Emacs 的优点,我已经用了整整一年了,非常好用,我已经迫不及待地想把它推荐给身边每一位朋友。

Spacemacs Rocks: 建议、意见和 Roadmap · Issue #5 · zilongshanren/Spacemacs-rocks

如果你是一个 IDE 党,那么你可有听过这句话,“世界上只有三种程序员,一种用 Vim,一种用 Emacs,剩下的全是第三种”?

如果你是个 Vim 党,那么恭喜你,学会 Spacemacs 是分分钟的事。(当然,你需要学习一点 elisp,同时你还需要 thinking in Emacs way)

如果你钟爱 Sublime Text 和 Atom,那么我建议你试试 Emacs,虽然说入门有一定难度,但是一旦掌握,将会终身受益。

如果你对我的安利不感冒,那么你也可以花一点点时间,看看我为 Spacemacs 录制的视频,看看这些能否激发你的学习欲望。

Spacemacs

你是否感觉每天时间不够用,你是否每天忙碌却又不知道在忙些什么,技术水平也长时间没什么长进?

试试 GTD 吧! (如果没听到 GTD 的同学,可以先 Google 一下)。

下面这个视频可以看看我是怎么使用 Org mode 来实践 GTD 的:

http://v.youku.com/v_show/id_XMTM2MjM5OTU5Ng==.html

Spacemacs 其实是一种生活方式,它让你的学习,生活和工作融为一体,让你可以达到一种 Flow 的状态。

http://v.youku.com/v_show/id_XMTQwNTg2NTkzMg==.html

想要用你最喜爱的编辑器来写博客,没问题。

http://v.youku.com/v_show/id_XMTM4NzIyMTc0MA==.html

不想每次换平台都换编辑器,受够了百度寻找各种破解码和注册机,不想为每一门编辑语言都学习一个 IDE,试试 Emacs 吧。

Emacs 打造成一个 C/C++ IDE:

http://v.youku.com/v_show/id_XMTM2OTM3MDkwMA==.html

Emacs 打造成一个 Javascript IDE:

http://v.youku.com/v_show/id_XMTQwNTg3MDY4NA==.html

想同时管理多个工作区间,希望在不同的文件之间快速切换,能够在不同的文件里面自由地搜索、查找与替换,看看 ninjia 是怎么做的。

http://v.youku.com/v_show/id_XMTM5NDI3NjQxMg==.html

http://v.youku.com/v_show/id_XMTM4MTI2NDQyOA==.html

Git 命令行效率不够高?IDE 又太重了?来试试世界上最好的 git 工具吧,magit 让你爽到爆。

http://v.youku.com/v_show/id_XMTM3NTI0ODYyMA==.html

如果你实在没兴趣,那么上面这些都可以无视。

Happy Hacking

November 16, 2016 09:31 AM

这些年伴随我的一些好习惯

本文我试图回顾一下这些年伴随我的一些好习惯,我会按照我认为的重要性来排序。坚持这些好习惯,让我在程序员生涯的开端保持不错的势头并且它们还将继续助力我不断进步。

每天坚持学习 1-2 小时

从毕业至今,工作之外,我几乎每天都会保持 1-2 小时的阅读和学习。我会利用碎片时间来阅读别人的博客和一些写得不错的文章,通常这不会占据我太多的时间。我主要的时间都花在阅读经典技术书籍和技术教程上面。

比如前些年我经常逛的 Ray Wenderlich | Tutorials for iPhone / iOS Developers and Gamers

比如最近我在阅读的 Learn OpenGL, extensive tutorial resource for learning Modern OpenGL

这些都是不错的学习资料,非常适合初学者。当阅读完这些教程后,我会挑选一些经典技术书籍来深入研究。

这个过程中,当然是要不断地练习,一方面可以帮助理解知识,另一方面也可以积累编码经验。

坚持阅读英文资料

这个习惯是我在大二的时候就坚持下来的,取得的收益非常大。目前我的阅读基本上只限于英文资料,而且我的听力已经可以达到不看字幕学习 MOOC 的境界。有时候甚至 Youtube 提速 1.5 倍都毫无压力,这个真的是常年积累的结果。

而且英文感觉上来以后,我发现自己在使用 Google 的时候都更加得心应手了,更容易搜索到自己想要的东西。

而且同样是阅读技术资料,我明显感觉自己现在比 4 年前更容易吸收这些知识。而且我觉得中国程序员跟美国程序员的差距最大的就是英语了。学好英语,真的是刻不容缓。

不断改进自己的工具箱

我在以前的文章中写到了我是一个技术 Geek,我喜欢折腾工具,改进工具和工作流,不断提高自己的效率。

从 Vim 到 Emacs,我一刻也没有停止折腾的步伐。在折腾的过程中,我熟练掌握了命令行工具和正则表达式,并且接触了函数式编程,这些都让我变成了一个更好的程序员。

积极融入社区

目前我已退出很多 Q 群和微信群,我几乎不用百度,我也很少逛知乎。

目前我每天必看 Reddit 和 RSS。我也在 Stackoverflow 上面回答问题, 我也时不时上 Google+去提问和交流。

Github 是我每天必须要上的,而且我的所有的代码都托管在上面。

通过写博客来记录自己的所思,所想和所学,扩大自己的圈子,结交更多的朋友。

小结

有了这些好的习惯,接下来要做的就是坚持了,目前我 focus 的重点是 3D Graphics,两年后,我再来分享一下我的学习心得。

November 16, 2016 09:31 AM

关于写博客的一些思考

我最早在 Cocos2D 圈子里面被大家认识是通过写博客,而“子龙山人”这个名号也是自那时起百度便可以搜索到了。

通过写博客,我还认识了与我合作第一本书的作者王寒,认识了泰然网的教主 Iven,认识了带我入 Emacs 教的“一叶道长”,认识了我的 Boss–Cocos2D-X 创始人王哲。而且后来,我也因此进入了现在的 Cocos2D-X 团队做游戏引擎研发。

总之,写博客有很多收获,也认识了很多朋友(这个是最重要的)。通过和这些认识的朋友交流与合作,我感觉自己明显进步不了不少。

本文主要介绍我这四年来写博客的一些思考,当然,我不是想安利大家都来写博客。我更想谈的是,写博客应该写些什么以及如何坚持写博客。

写博客到底写些什么

写博客最怕写成流水帐或者转载大全。写什么东西很关键,没东西写,那肯定坚持不下来。我中间也一度有一段时间没有坚持下来,纠其原因,也是我没有系统地思考和规划过这件事情。最近半年来,我几乎每周都会写一篇博客,而且一篇博客的书写时间大概在 1-2 小时之间。

当然,我目前的文章可能教程和入门型的东西少了很多。以前因为喜欢看我教程的朋友,估计会有点不开心了。不过,我写博客是为了记录与分享我的编程人生,为什么要为了迎合一部分人的口味而去刻意为之呢?我手写我口,我觉得这是每一个写博客的人应该有的态度。

好了,不扯远了,接下来我会谈四点我认为比较有价值的,可以拿来写博客的素材。

可以是翻译看过的好文章

这是最简单而且最容易开始写博客的一种方式。通过翻译一些优秀国外技术文章,一方面可以深化自己对于文章的理解,另一方面,也可以传播知识。我最早就是这么开始写博客的,而且事实上,这种博客还是相当受欢迎的。因为老外的技术文章一般都写得比较用心,只要你的翻译水准还行,肯定是会受到大家喜欢的。

不过这里需要注意几点,不是随便什么文章都适合拿来翻译的。老外也不是谁都可以写出深入浅出的好文章的。在决定翻译一个人的文章之前,自己必须是他的博客的老读者了,而且通过阅读博主写的大量文章已经获益很多。此时,你是迫切希望跟大家一起分享这些文章的。如果你达到了这一步,那么赶紧开工吧。

如果硬要我给出好文章的标准,看看Ray Wenderlich | Tutorials for iPhone / iOS Developers and Gamers 这个网站上面的文章吧。

可以是读书笔记和心得体会

身处在 IT 行业的码农们,大部分都是爱学习的。其原因,一部分是为了现有的工作,还有很大一部分原因可能是为了接下来我们将会迎接的编程挑战(当然这些挑战都是意淫的,比如 GO 语言很不错,我以后想转后端。再比如大数据很牛比,我要学习 R,以备将来使用。)程序员天生有危机意识,而且喜欢过早优化,所以,上面的例子实则每个人在某一段时间可能都出现过。

我们会经常看一些技术书籍和文章,如果这些内容得不到应用,过了一段时间很可能你就会忘记它们。如果这样反复几次,很多原来爱学习的小伙伴就会变得不那么爱学习了(年龄越大越是如此)–反正也用不上,何必学呢。于是乎,我们身边突然之间多了一大群一年可能都不看一本技术书籍的同学。

如果是为了给自己充电,一定要记录一些学习笔记。当然,更好的方式可能是做一个 side project。不过如果有文字能够记录下来是最好不过的了。

这里的心得体会可能是读完一本书之后的,与人交往的,工作上的等等。

人的思绪总是来的快,去的也快。如果有一些想法了,也不要急于马上写成博客。可以先记录下来,然后专门存放在一个地方。过一段时间,等有空的时候,可以翻出来看看。随着时间的推移,你对之前的想法可能会有偏差,回顾的时候是一个自我蜕变的过程。

记录心得体会其实有时候也挺为难的,因为有些话可能不太适合在公开场合说。尤其是一些对人或对事的看法之类的,但是,还是那句话,只要不是恶意的人身攻击,就事论事,我觉得是没啥好顾忌的。

可以是攻克难题的步骤和答案

我们每天都在用 Google 来解决问题,从别人的博客或者 SO 上面的回答来获取方案和灵感,甚至很多程序员可能离开网络都写不出代码来了。其实,善于利用网络也没啥不好,谁的脑子也记不住所有的东西。

但是,我们一味索取的时候,是否想过贡献呢?有时候,我们查找了 N 多资料,把官方手册和论坛都刷了一遍,可能还是找不到答案。而且 SO 上面的回答也没有一个是最佳的。你通过几天时间终于解决问题了,你是否会把你的解决方案和过程也分享出去呢?

可以是自己已经熟练掌握了的东西

大家看到以前我学会 Cocos2D 之后,通过翻译大量优秀教程来“安利”大家学习 Cocos2D。

后来,我学习了一段时间 Vim,而且我感觉我已经 Feel Good 了,我又通过录视频,写教程来“安利”大家学习 Vim。

现在我的 Emacs 水平也很不错了,大家又发现我在“安利”Emacs 了。。。

“子龙。。。能不能停止安利的步伐。。。”

其实写自己熟悉了的东西是最好写的。因为你熟悉,所以你写出来的东西才会条理更清晰,而且更容易引起大家共鸣。

其实还有很多东西可以写,我这里就不一一列举了。坚持写点什么,你总能收获一些东西。

如何坚持写博客

知道写什么以后,接下来关键就是坚持了。

写你感兴趣的并且真正热爱的东西。

这一点真的是非常非常非常重要。如果你写的是一些你压根不喜欢的东西,你是不可能写出好文章来的。当你真正热爱一样东西以后,你就会想尽一切办法去学习和了解它,自然而然,你的素材多了,就更容易写出好文章来了。

控制好写博客的频率

写博客的频率也很重要,一周一篇是目前比较适合我的频率。

找一个称手的写博客工具或平台

比如我的最爱是 Emacs + Hexo + Org + Git,然后我会用 Org GTD 记录一些博客灵感,然后在写博客之前看这些灵感,然后形成文章。关于我是怎么使用 Emacs 来写博客的,大家可以看看这个使用 Org Mode 来写博客

总结

让我们一直这样愉快地写下去吧。

November 16, 2016 09:31 AM

The best way to teach others Emacs

安利 Emacs 的正确方式:

第一步:找到一个对 Emacs 真正感兴趣的人。(这是最难的一步)

第二步:让他把 Emacs 自带的教程看完(C-h t),并且给他们一些 Youtube 视频教程。

第三步:让他放弃学习 Emacs,并且安利他去学习 Vim。

第四步:等上十来年。

第五步:十来年后,此 Vim 党想重新尝试 Emacs 了,而且这一次他懂得自己去找资料学习了。

第六步:终于,他成功学会了 Emacs,而你什么也没干。

2333333

上面的段子来自 reddit:

https://www.reddit.com/r/emacs/comments/3r1z7p/in_your_experience_what_was_the_best_way_to_teach/

那到底我们(Emacser)要不要安利呢? 这是一个问题。

另外,什么样的安利姿势才是正确的呢?

不要在安利的时候说 Emacs 可以做任何事情。

没错,Emacs 就是一个操作系统,它可以完成任何事情!

map(#[%](str % 千万不要对一个没有入 Emacs 党的人过早地透露这个秘密), [但是,但是,但是])。(重要的事情说三遍)。

我曾经试图这样做,但是,不管我说什么,他们总能找到反驳我的理由。

我说 Emacs 也可以打造成 Python IDE。(你这个有 PyCharm 牛比吗?不能调试打断点吧,哈哈。)

我说 Emacs 也可以打造成 C/C++的 IDE。(你这个有 VS 牛比吗?VS 是世界上最好的 IDE!)

我说 Emacs 还可以煮咖啡!!!(你妹!你煮给我看看!!)擦。。

所以,下次安利的时候不要再对人说 Emacs 可以做任何事情了。

不要安利对方快捷键使用和无鼠标操作方式

下次安利 Emacs 的时候,请让他用鼠标!请让他用上下左右键!只有 Vim 党才会宣称 hjkl 比上下左右好使,全键盘流是效率最高的,我们需要的是肌肉记忆。

(没错,上面说的我全部赞同,并且我也是一个资深的 Vim 党。)

但是,如果他是一个 资深 Vim 党,你千万不能给他安利 Emacs,原因你懂的。而其它编辑器党都是喜欢并且离不开鼠标的。

Emacs 支持鼠标操作,而且也鼓励使用鼠标。比如菜单选择,Minor mode 关闭都可以用鼠标来操作。

另外,不要传达出 Emacs 快捷键很虐人的信号,比如 Ctrl 键到死。。。(事实上也确实如此)

多介绍 which-key 和 hydra,鼓吹 helm 和 ido 的操作方式,让他放弃学习 Emacs 的原生快捷键。

要安利 Emacs 是一个编程平台

Emacs 是一个非常好玩的 Lisp 运行时,而且 Emacs Lisp 非常有趣。只有你用了才能体会 Happy Hacking 的乐趣,并且你有可能会乐此不疲。

看完《 The little scheme 》,你会深刻理解程序递归是什么。然后,你会中括号毒,然后你会想玩 Clojure。然后,就一发不可收拾了。

另外,Emacs 是一种生活方式。

你试过用 Org mode 做 GTD 来管理你的时间吗?(Spacemacs Rocks(2): 使用 Org-mode 做 GTD · Issue #2 · zilongshanren/Spacemacs-rocks)

你试过直接在 Emacs 里面运行终端模式器,然后在此模拟器里面输入 vim .emacs 来修改你的 emacs 配置里面的 bug 吗?

你试过在 Emacs 里面使用 IRC 和收发邮件吗?

Emacs 是一个操作系统,只是它缺少一个好用的编辑器。

不过现在 Spacemacs 打开了新的局面,你们(Vim 党和其它党)准备好了吗?

November 16, 2016 09:31 AM

Things I Wish I Know When I Am Learning Emacs

当我在学习 Emacs 的时候,有一些事情,我希望有人能够提前告诉我。

这样的话,我便可以少走一些弯路。

一定要使用 Starter Kit

对于 Emacs 新手而言,一定要使用 Starter Kit!

虽然有很多人喜欢从零开始捣腾,但是,真的不要这样。一来浪费大量的时间不说,而且通常只有学习能力非常强而且毅力也非常好的人才能从零开始配置并坚持下来。

当然,如果你觉得 Starter Kit 满足不了你的需求,你是可以很轻松地进行扩展的。

好的 Starter Kit 通常都是 Emacs 社区里面的高手开发的,它们的模块化,加载时间,按键绑定都是经过精心优化的。向高手学习,在初学阶段是非常重要的。这一点,陈斌在他的《一年成为 Emacs 高手》中也提到了。

如果以前是 IDE 重度用户,推荐从bbatsov/prelude 开始入手。

如果你是 Vim 用户,推荐从 syl20bnr/spacemacs 入手。

这两个 starter kit 都有非常详细的文档,入手之前一定要认真阅读文档。在上手以后,还需要不断地反复阅读文档并动手实践。遇到问题可以去 Github 上面提 issue。

Package 管理和依赖

刚开始学习 Emacs 的时候一定要学会使用包管理器,能够从 melpa 下载包。

每一个 Package 要么定义了一堆功能函数,要么定义了一个 major mode 或者 minor mode。

不要一味地使用 require,这样的话会导致你的 Emacs 加载时间变长。 最好使用 jwiegley/use-package, 它不仅可以智能地引入 package。比如当你的 elpa 目录的一个 package 不存在的时候,直接使用 require 是会报错的。但是使用 use-package 的话就不会。

另外,use-package 还可以优雅地管理你的配置。比如有一些 mode 相关的变量,在 mode 还没有激活的时候,去设置它们没有用的。

这时你需要使用 eval-after-load,然后把相关的设置放进去。 而使用 use-package 的话,就可以非常方便地使用 :defer t 和 :config 了。

最后,有一些 Package 之间是存在依赖的,如果加载顺序不对也不行。这里推荐大家试试 edvorg/req-package

按键绑定

按键绑定,也叫"快捷键绑定",它本质上只是用一个 Hotkey 去调用一个交互式命令。

如下所示,我定义了 C-c C-1 快捷键,它可以在当前的 buffer 位置插入一个“Hello World”的字符串。

(defun print-hello-world ()
  (interactive)
  (insert "Hello World"))

(global-set-key (kbd "C-c C-1") 'print-hello-world)

这里需要注意的是,print-hello-world 函数必须是 interactive 的,否则是不可以绑定一个快捷键的。

一个函数被定义成 interactive 以后,我们就可以通过 M-x 来调用它,如果你发现此函数经常被调用,此时就可以通过定义一个快捷键来解决效率问题了。

上面所提到的快捷键绑定是 99%的 Emacs 党都知道的。

但是,有一些事情可能并不是所有人都清楚。

按键绑定的覆盖问题

有时候,我们定义了一堆快捷键,但是某一天安装完一个 Package 后,突然发现以前的快捷键失效了。通常这是由于某些 minor mode 的快捷键的优先级比你的高,所以被它覆盖了。这里强烈推荐大家看看 Mastering Key Bindings in Emacs - Mastering Emacs ,看完之后相信会对 Emacs 的按键绑定有更多的认识。

这里面有一些小技巧,可以保证你自己定义的快捷键不容易被覆盖。

对于正统 Emacs 用户,定义的快捷键默认最好以 C-c 开头,因为 C-c 是 Emacs 预留给用户使用的,当然某些 minor mode 也会使用 C-c 开头。此时,你可以通过定义一个 minor mode 并且让这个 minor mode 最后加载,然后把你所有的自定义快捷键都放在新定义的 minor mode 里面,这样你的快捷键就不会被覆盖了。

著名的 Emacs Starter Kit Prelude 就是使用的这个方式。

对于 Evil 用户,建议使用 evil/leader 来定义快捷键。更合理的方式是像 Spacemacs 一样,分组来定义。

最后,对于 Spacemacs 用户,建议使用 SPC o 来定义自己的快捷键。

快捷键太多,记不住怎么办?

很多初学者抱怨 Emacs 的按键太复杂,伤小拇指。或者抱怨 Emacs 的按键太多,记不住。

其实,你根本就不需要快捷键。

通过 smex + ido,你可以用交互式的方式来执行 Emacs 的命令。当然,你也可以使用 Helm 来搜索并执行命令。因为你日常经常用的命令不会太多,所以,其实是不推荐定义太多快捷键的。直接使用命令有时候会更快,因为你大脑的负担更轻。

其实你根本就不需要记忆。

你可以通过 abo-abo/hydrajustbur/emacs-which-key 来交互式地显示所有的按键绑定。

最后,Evil 也是一个极佳的方案。Vim 和 Emacs 专注在两个不同的领域,Vim 把编辑器操作发挥到了极致,而 Emacs 在与系统集成方面也做到了极致。

而 Spacemacs 让这一切更和谐。

有问题问 Emacs

在学习 Emacs 的过程中,一定要经常问 Emacs 一些问题。

比如这个函数是干什么用的?(C-h f) 这个按键对应的函数是什么?(C-h k)这个变量是干什么用的?(C-h v)

一定要学一点 elisp,Emacs 自带的《Introduction to Elisp》就很不错。

在使用 Starter Kit 的过程中,有事没事,读一读它们的源码。这样也有利于你在扩展自己的配置的时候,遵循的是此 Starter Kit 的最佳实践。

实在是搞不定了再问 Google,最后才去社区里面提问:

社区提问的优先级是:StackOverflow -> Reddit -> Google+ -> Github issue -> 最后才是 Q 群和微信群

Happy Hacking

November 16, 2016 09:31 AM

关于 Spacemacs 的 Tips

因为 Spacemacs 需要下载大量的 package,而中国大陆的网络环境不是很稳定,容易导致一些 package 在下载的过程中 byte-compile 出错,所以很多人会遇到一些特别奇怪的错误。

我本人也被这个坑了好几次了,所以写文章记录一下。

could not load evil-indent-textobject

这是一系列的错误,根本原因就是下载下来的 package load 出错了,或者根本没有下载成功。

一般的解决方法是先去.emacs/elpa 目录去查找这个 package,检查这个 package 是否已经成功下载。(成功下载指的是对应的.el 文件和.elc 文件都存在)

其次,在启动 Emacs 以后,运行 byte-compile-file 来重新编译这个 package 下面所有的文件。因为 Emacs 默认是加载 elc 文件。

一般情况下,上面的方法可以解决 60%以上的 package 加载错误。

最后,通过 emacs –debug-init 来启动,通过 error trace 来定位问题所在。

如果你还是解决不了问题,可以 fork 我的配置,然后把 Spacemacs 更新到最新就可以用了。

Spacemacs 启动速度特别慢

这个真是冤枉啊!!

其实罪魁祸首是 Helm 这个 package。

你只需要在你的.spacemacs 或者.spacemacs.d/init.el 文件中的 user-init 方法中添加下列代码后,重启 Spacemacs 即可:

;; https://github.com/syl20bnr/spacemacs/issues/2705
(setq tramp-ssh-controlmaster-options "-o ControlMaster=auto -o ControlPath='tramp.%%C' -o ControlPersist=no")

至于是什么原因导致的,我具体也不清楚,大家可以去看看上面的 issue 链接。

Happy Hacking

最后祝大家使用 Spacemacs 开心。

November 16, 2016 09:31 AM

Emacs 当前文件快速查找内容

使用 Emacs 也已经两年多了,积累了不少经验,也一直在安利,不过效果不佳。是时候跟大家分享一些我使用 Emacs 的技巧了,不求安利,只求相互交流。

本文主要介绍 Emacs 里面查找当前文件内容(当前 buffer)的一些方法,它们各有优势,所以我就不一一对比了。

我会在这里介绍它们的用法,并且给出我自己的使用场景。

注:关于插件和 Emacs 的用法,最好的方法其实是录制视频或者制作 Gif,但是录制视频真的花时间,因为还要上传。这里先欠着,后面的zilongshanren/Spacemacs-rocks 再补上。

但是文字有时候非常简洁,可以节约大家的时间。

作为一个资深的 Vim 党,Vim 本身的搜索是一定会经常用到的。一般在 normal state 的时候搜索当前文件里的一些关键字,我会优等考虑使用/来搜索。

而且搜索完成以后,可以按 n/N 来查找“上一个”和“下一个”。

如果要搜索当前光标下面的符号,我会使用bling/evil-visualstar 来搜索,只需要把光标停在你想要搜索的符号上面,然后按一个*就可以了。

通过syohex/emacs-evil-anzu 可以在状态栏显示出当前匹配的 item 个数。

它最大的优点就是简单方便,native to vim 用户。

Swiper

此插件是我最喜爱的 Lisp 黑客 abo-abo (Oleh Krehel) 所写,我的 Emacs 里面配置了很多他写的插件,最好用的是 lispy,hydra,avy 和 ace-window。大家从他的 Github 主页上面也可以看出来这些插件在 Emacs 社区的受欢迎程度。

此插件不具有侵入性,它是 Emacs 内置命令 isearch-forward 的增强,一般绑定的快捷键就是 c-s。所以,学习此插件的用法没有任何成本。

重复按 c-s 可以不停地查找一下个,按 c-r 可以查找上一个。 如果匹配的项目不止一个的时候,可以在下面有一个 mini buffer 可以预览,这个才是 swiper 的 killer feature。

在这个预览窗口里面,我绑定了 c-j/c-k 来上下选择,不习惯 Vim 的同学可以使用原来的 c-n/c-p 来选择。

当你移动选择 item 的时候,你会发现当前 buffer 里面的光标也会跟着移动到对应的匹配项。如果你按 c-g 取消,你的光标还是会回到原来你按 c-s 的地方。(是不是有点晕。。。试试就知道怎么回事了。)

如果在 emacs state 我一般会选择使用 swiper 来查找想要的内容,而不会先回到 evil normal 状态,再按/来搜索。

关于 Swiper 更多的介绍可以参考原作者的项目文档和博客。

swiper 最大的优点就是可以在不切换模式的前提下提供最方便地搜索和预览功能。

Helm-swoop

这又是一个大杀器,如果只是针对当前文件,它的功能和 swiper 一样牛叉(甚至更牛叉)。不过它的 UI 会比 swiper 好看一点点(至少我觉得是)。

但是它有一个 swiper 没有的特性。当你查找一个字符串以后,还可以空格后继续过滤你想要查找的文本。

打个比方,我现在有这样一段代码。

var sprite = new cc.sprite();
mysprite = sprite;

如果你查找 sprite,你会搜索到两行,此时,你空格后再输入 var 就可以再过滤得到

var sprite = new cc.sprite();

赶紧试试吧,用过的都说好。helm-swoop 还有许多其它功能,比如多行查找等,这里就不多说了,看文档吧。

好看的 UI 和持续过滤的功能是它最大的优点。

Helm-semantic-or-imenu

这个功能在 spacemacs 里面对应的快捷键是 SPC sl,我在 spacemacs rocks 的第一个视频里面已经演示了,这里就不多说了。

它在编程的时候用来查找当前文章中的函数列表是最好用的,当然也可以用来查找 markdown 和 org 中的 heading。

另外,通过配置 imenu-generic-expressions,它还可以用来查找任意你想要查找的 patten。

我在 reddit 里面提问就解决了一个 mocha 测试文件里面查找测试例的问题。How to show mocha test names in imenu list. : emacs

此命令通常都是在浏览和阅读代码的时候使用,因为 Vim normal mode 是浏览代码最好的方式,所以,此命令通常在 normal state 下面使用。

优点:不需要过多的输入,就可以查找想要的内容

occur

这个命令我平时用的不多,也是最近才开始使用的。我想说,它真的非常强大,它可以生成一个 buffer 来显示你要查找的内容。

然后,在这个 buffer 里面按 e 进入 occur-edit-mode,然后你就可以直接编辑你查找到的内容了,编辑完后按 c-c c-c 就可以保存并回到 occur-mode 了。

很强大有木有???!

当然对于查找和替换,我有时候更多地是使用 helm-ag,iedit 和 multiple cursor。 但是 occur 绝对也是比较好的轻量级方案。

最后

Emacser,你是如何在当前文件快速查找你想要的内容的呢?欢迎在下方留言。

November 16, 2016 09:31 AM

我的工作流

本文要介绍的工作流是我最近才想出来的, 当然也借鉴了不少其他人的经验,但是,这不是重点,重点是此工作流要解决我目前遇到的问题。

我的问题主要有:

  1. 当我要完成一个代码 feature 或者要修复一个严重的 Bug 的时候,我无法准确预估我需要的时间。
  2. 当我重构一些代码时候,很容易犯一些低级的错误,这让我显得很不专业。
  3. 在解决复杂的问题的时候,我经常要试错然后不断地改进,中间会有推倒重来的可能。
  4. 对于我以前写的有一些代码,要理解它们有一定的难度。

它是什么?

一句话就是: GTD -> DDD -> RDD -> TDD/BDD -> Coding

GTD -- Getting Things Done

GTD 是一个时间管理方法,还有一本书专门介绍它,叫做《Getting Things Done 无压力工作的艺术》。

采用 GTD 来管理个人的时间,可以让自己的时间利用率最大化,把有限的时候花在最有价值的事情上面。

因为我们现在有电脑和各种智能设备,它们很容易吸引我们的注意力,这无形中会消耗我们大量的时间。

这里我就不介绍如何实践 GTD 了,对于我而言,最好的 GTD 工具自然是 Emacs 的 Org-mode,当然也有其它工具可供选择。

当我每天打开电脑,第一件事情就是检查我的 org-agenda 上面的 todo 事项,一般我会列一些 habit 在上面。比如锻炼身体,逛 reddit,阅读 rss 等。

此时,agenda view 上面可能还有一些 project todo 事项,一般而言,这些事项不可能在短时间内完成,我会安排一天中的时间来优先处理它们(比如 schedule)。

当 agenda view 上面没有 todo 事项的时候,我会从我的历史 todo 中找到“重要且紧急”的 todo 列表,然后根据实际情况 schedule 一些到 agenda 中去。

只要 agenda 中没有安排的事情,我都不去做!这一点非常关键。 如果你打开一个网站,看到一篇不错的文章就开始阅读,发现一个不错的 Github 仓库就立马 clone 下来把玩。这样是非常浪费时间的,而且效率也不高。

另外,对于 project,我会把它分解成一系列小的 todo item,然后在实际做这些 item 的时候,我会用 org-pomodoro 来 track 我花掉的时间。这样,等项目做完,我就可以知道我一共在这个项目上面花了多少时间。这也有利于以后做新任务的时候做时间预估。(这里的项目定义参考《无压力工作的艺术》一书)

DDD -- Document Driven Development

我这里使用的是 Org-mode 的文学编程,除了可以写文档,还可以非常方便地在文档里面插入代码和图表,最后可以导出 HTML 和 PDF 供自己和他人阅读。

文档驱动开发主要是针对一些复杂的技术问题和一些重要的技术决策来的。很多时候我们对于一个问题束手无策,可能是我们还没能理解清楚这个问题。换句话说,就是我们连问题本身是什么可能都还没搞清楚,何谈解决它。

要理解一个问题,不仅仅是识别它本身,还包括它的约束和上下文。

所谓的“分析和设计”不止是画 UML 图和写各种设计文档,这些活动本质上是为了让我们更好地理解我们面临的问题。只有当我们完全理解了问题之后,我们才有可能找到一个简单清晰的解决方案。

如果你把思考过程写下来,把讨论过程记录下来,那么你就有机会针对此问题的理解进行深层次的迭代。另外,对于日后维护代码的人来说,这些文档也是宝贵的参考资料。

RDD -- REPL Driven Development

这个听起来挺新鲜的,我也是最近才听说的,不过这个真的非常管用。

我现在写程序如果没有 REPL 感觉很不爽,比如写 C++,编译等半天,非常低效,而且一点也不 Fun。我还是喜欢 Lisp 和 Javascript。

如果在做分析和设计的时候可以写一些代码,它会帮助你更好地理解你的问题和解决方案。它相比于制作原型开销要小得多,而且这个过程非常有趣。

另外,当你要修改一段代码的时候,如果有 REPL,你就可以在程序运行起来后,动态地 hack,进而更好地理解此程序的逻辑,做 web 开发的同学肯定深有体会。

REPL 在验证 API 和 Idea 方面会比 Org-mode 的 babel 更方便。

TDD/BDD -- Test Driven Development or Behavior Driven Development

这两个术语已经被炒得很热了,我这里就不多说了。

在具体实践的时候,注意一定要先写测试,然后让测试失败,再写代码,让测试通过,然后重构,得到简单和优雅的代码。

简言之,即 Failed -> Passed -> Refactor。

因为在实践 TDD 的时候,你可能会运行测试例上百次,所以方便的工具可以让这个过程变得不那么枯燥。

Emacs 当仁不让。我这里拿 node.js 来说,我现在用的测试框架是 mocha,通过分屏在 Emacs 里面开一个 terminal,可以非常方便地运行测试例。我甚至可以写一段 lisp 代码,让我在保存测试代码的时候,自动运行测试。

好的工具可以让我很容易进入一种“流”的状态,然后效率自然大大地提升了。

Coding -- 开始编码

关于编码的问题不是本文的重点,惟一需要强调的是,我们的目标是编写:

简单,优雅和可维护的代码。

另外,不要一味地往代码里面加入过多的 feature,因为简单的 feature 堆砌并不能解决任何人的问题。

我们关心的是解决具体的问题,而不是添加更多的功能。而且你能保证新加的功能本身不会制造更多的问题吗?

总结

工作流千万种,找到最适合自己的才是最重要的。

也欢迎大家和我交流自己的工作流。

November 16, 2016 09:31 AM

三个 Git 问题(下)

在上篇文章中,我们介绍了 Git 内部存储对象的方式,以及为什么不要用 Git 去管理大的二进制文件。

本文将继续探讨上篇文章中遗留的两个问题。

  1. 如果两个 Branch 修改同一个文件的同一行代码,各自 Commit 一次,在 Merge 的时候为什么不会有冲突?
  2. 同样是两个 Branch 修改同一个文件的同一行代码,各自 Commit 一次,在 Rebase 的时候为何有一个 Commit 会被优化掉?

Merge 的测试代码

首先,我们新建一个分支,修改 a.txt,添加一行内容“Learn Git”:

git checkout -b testBranch
echo "Learn Git" >> a.txt
git commit -a -m 'commit'

此时,我们用 find 命令查看一下新增的 object 对象:

find .git/objects -type f

2015-09-21-three-git-question-2_git-cat-file-1.png

这里我们可以看到多了 3 个对象:407b6e (commit), 49bf80 (modified a.txt) 和 81f485(tree object)。我们可以用 git cat-file -p hash-code 来查看这 3 个对象的值。(注意这里我们使用的是 6 位数字的简码)

现在,让我们切回主分支,并且对主分支的 a.txt 做同样的修改:

git checkout master
echo "Learn Git" >> a.txt
git commit -a -m 'commit'

此时我们再查看一样新增的 object 对象:

find .git/objects -type f

2015-09-21-three-git-question-2_git-cat-file-2.png

此时我们只新增了一个新的对象 9e2107(commit),我们用 git cat-file 查看其值为:

git cat-file -p 9e2107(如果这里换成 407b6e,结果是一样的

2015-09-21-three-git-question-2_tree-object.png

此时,如果我们 merge testBranch 分支是不会有冲突的。

git merge testBranch
find .git/objects -type f

2015-09-21-three-git-question-2_cat-file-3.png

此时,我们发现多了一个 1bff79 对象,它的值如下:

git cat-file -p 1bff79

2015-09-21-three-git-question-2_tree-object-2.png

我们知道,整个过程中,我们在两个分支上分别对 a.txt 的同一行代码做了同样的修改,但是最终 git 只会保存一份 a.txt 的内容。另外,整个目录树在不同的分支上面是一样的,所以 tree 对象也只有一份。

虽然我们在不同的分支上面做了两次 commit,但是这两次 commit 只是记录了各自的 tree,parent 以及作者等信息。

而在 merge 的时候,由于我们是 two-way merge,所以最终生成的 merge commit 是有两个 parent 的。

由上面的测试代码我们可以清楚地知道 merge 到底干了些什么事。

Rebase 的测试代码

我们先 reset 掉刚刚 merge 的代码,并且切换到 testBranch 分支:

git reset --hard HEAD~1
git checkout testBranch

此时我们运行 find 命令来查找 objects 目录:

find .git/objects -type f

虽然我们的 1bff79 commit 已经被我们用 reset –hard 命令撤销了,但是我们发现它还是在版本里面,我们可以用 git reflog 来找回这个 commit。 (这里扯远了,不过这也说明了,在 Git 里面只要 commit,你做的修改就不会丢。所以,程序猿们,commit early, commit often 吧。不过记得 publish 之前记得整理 commmits,哈哈,这又是另一个话题了。。。)

现在,让我们 rebase 吧!

git rebase master

接下来,让我们看看 objects 的情况:

find .git/objects -type f

2015-09-21-three-git-question-2_cat-file-4.png

What???

这次操作一个新的 objects 都没有生成。

要理解这个,我们需要清楚地知道什么是 rebase。

当你 rebase 的时候,你会把当前分支上的所有的 commit 全部丢弃,然后把这些 commit 的修改依次应用到目标分支上,并且此时会生成新的 tree 对象和 commit 对象。但是,如果进行 rebase 分支的 commit 和目标分支的 commit 是一样的时候(指的是 tree 和 parent 一样),会用当前 commit 替换掉目标 commit。

这段话听起来有点拗口,但是其实很容易理解。

我们拿上面的示例代码来看。当我们 rebase 的时候,9e2107 commit 和 407b6e commit 是一样的内容,此时会用 9e2107 commit 来替换掉 407b6e commit,即我们前面所指的 commit 优化。

小结

当我们对于 Git 的基本原理有了一定的认识之后,像 rebase 和 squash 这样的高级工具就容易掌握了。而且 rebase 和 squash 是打造整洁的 commit 历史必不可少的工具。

November 16, 2016 09:31 AM

三个 Git 问题(上)

本系列文章主要回答三个 Git 问题:

  1. 为什么不推荐用 Git 保存二进制大文件?
  2. 如果两个 Branch 修改同一个文件的同一行代码,各自 Commit 一次,在 Merge 的时候为什么不会有冲突?
  3. 同样是两个 Branch 修改同一个文件的同一行代码,各自 Commit 一次,在 Rebase 的时候为何有一个 Commit 会被优化掉?

要回答好这三个问题,我们需要理解 Git 的内部工作原理。

在保存文件历史的时候,Git 跟 SVN 最大的不一样在于,它保存的不是文件的 Diff 而是整个文件的快照。

什么是文件快照

mkdir testGit
cd testGit
echo "hello" >> a.txt
echo "world" >> b.txt
git init
git add .

此时我们在 testGit 目录下面新建了一个 Git 仓库,并且新建了两个 txt 文件。此时我们可以在.git/objects 目录下面找到这两个文件。当调用完 git add .之后, Git 会对当前目录下面的所有文件进行“快照”并保存。

大概就是 a.txt 和 b.txt 经过 zlib 压缩后,使用 SHA-1 算法把文件内容和文件头生成一个 40 位的 hash 值,并且使用此 hash 值的前两个字符作为目录,后面 38 位作为文件名来保存压缩后的文件对象。

我们可以通过 find 命令来查找.git/objects 目录下面所有存储的对象:

find .git/objects -type f

下图是输出: 2015-09-12-three-git-question_find-objects-1.png

a.txt 和 b.txt 的文件内容存储在 628ccd10742baea8241c5924df992b5c019f71 和 013625030ba8dba906f756967f9e9ca394464a 文件中。

我们可以通过 git cat-file -p [hash value]来查看保存的对象值(注意需要加上文件目录得到完整的 hash 值)

git cat-file -p cc628ccd10742baea8241c5924df992b5c019f71
git cat-file -p ce013625030ba8dba906f756967f9e9ca394464a

下图是输出: 2015-09-12-three-git-question_cat-file-1.png

这里的 hash 值所代表的文件就是文件快照。此时,如果我们 commit 的话,会生成新的 hash 值对象。

git commmit -m 'first commit'
find .git/objects -type f

下图是输出: 2015-09-12-three-git-question_cat-file-2.png

这里的 18eb80fbbbf9160491c007668d5298f1e86cd40a 和 f6042cce01150551255ec1e892d04b1c129a5fbd 是新生成的对象。

我们用 git cat-file -p 来看看它具体是什么?

git cat-file -p 18eb80fbbbf9160491c007668d5298f1e86cd40a

下图是输出: 2015-09-12-three-git-question_cat-file-3.png

git cat-file -p f6042cce01150551255ec1e892d04b1c129a5fbd

下图是输出: 2015-09-12-three-git-question_cat-tree-1.png

这里的 18eb80fbbbf9160491c007668d5298f1e86cd40a 表示的是当前目录树的 hash 值,里面包含了每个文件的权限,类型,hash code 和名字信息。

而 f6042cce01150551255ec1e892d04b1c129a5fbd 则是我们的 commit 号,也就是平常我们用 git log 得到的内容。

如果此时我们修改 a.txt 或者 b.txt 并 commit,新修改后的文件会再用 zlib 压缩后生成一个 hash code 来当作名字存储。

为什么 Git 切换分支特别快?

因此要把版本的历史 checkout 到工作区,Git 只需要把 commit 对应的目录树的所有的文件解压缩即可。

如果是 svn,则需要找到一个参考版本,然后不断地应用 Diff 才来得到相应的分支,这个速度是非常之慢的。

为什么不推荐使用 Git 保存二进制大文件

因为 Git 使用的是文件快照来保存版本历史,而二进制文件在压缩上几乎没有效果,所以,二进制文件只要有一点点修改,保存的就是整个文件内容。

所以大的二进制文件是禁止放到 Git 里面去管理的。那么多大才算大呢?一般的标准是单个二进制文件的大小不要超过 100kb。

参考文献

November 16, 2016 09:31 AM

最近读过的好书

本文主要记录我近两个月来阅读非技术书籍的心得。

其实我已经有很长一段时间没有阅读非技术书籍了, 一方面可能是自己想在技术方面更精进一些,这两年我专注的点都在具体的技术上。 另一方面,可能也是时间关系。

不过,这两个月我居然能够看完 7 本非技术书籍,而且看书的大部分时间是周末和晚上睡觉前 2 小时。这对我而言可以说是一个巨大的进步。

这次我挑选了 7 本跟互联网相关的书籍,因为毕竟是互联网从业人员。

这七本书分别是《周鸿祎自述--我的互联网方法论》,《浪潮之巅》上下两册,《乔布斯传》,《程序员的呐喊》,《异类》和《从 0 到 1 》。

周鸿祎自述--我的互联网方法论

看红衣教主的书,通常内心都是比较澎湃的。虽然我自己对 360 的很多产品并无爱,但是看教主的书收获还是蛮多的。正如书名所表达的,教主在讲述他的互联网方法论。全书里面充满了“免费”,“体验为王”,“用户至上”,“颠覆式创新”,“微创新”等词汇。这也是近几年来,在各种媒体上能够看到的耳熟能详的词汇。

对于书里面的大部分观点我是比较认同的,但是惟独一点“用户至上”,我觉得有点名不副实。360 号称是一家专注于安全的公司,但是表现出来的很多却是绑架用户,杀毒软件变成了用户电脑上最大的流氓软件。我曾经有一段时间宁愿电脑裸奔也不愿意安装 360 的任何一款软件。因为只要你安装一个,立马赠送一个全家桶。现在“百度全家桶”可能更牛逼了。不过幸好我已 5 年不用 Windows,真是万幸。

回想当时 3Q 大战的时候,我还在读研究生,不过当时我居然可以同时运行他们两家的产品,也真是神奇。当时 360 的股价貌似很高(我不炒股,所以不知道具体是多少,不过出了那么多风头,应该是很高啦。), 不过现在好像股价已经腰斩了。在移动互联网的风口上,360 除了手机助手还行,我目前还没看到啥东西。

360 当时是威风八面,到处树敌,腾讯,金山,百度都要搞它。现代商业竞争已经不是零和游戏了,合作共赢才是主旋律。况且后面还曝出 360 的杀毒引擎造假,可见 360 在技术上并没有优势,也无法靠此形成垄断。后来金山也学习它搞免费并且和腾讯等安全厂商联合起来对抗 360。而 360 靠所谓的安全浏览器,想在搜索上面和百度分一杯羹。但是表现出来的意思,好像是要干掉百度一样。这样百度知道“狼来了”,结果可想而知。

360 像一条鲶鱼,给行业带来了活力。打破了一些陈旧的行业规则,最终给用户带来了利益。不过从目前 360 的处境来看,不光是大公司防着它,而在用户这边也有不少人在反感。什么才是真正的“用户至上”,这值得每一个创业者去思考。

浪潮之巅

这本书的盛名已久,而它的作者吴军博士是有 Google 背景的,这更添加了我对此书的好感。阅读完两本书,我了解了互联网历史上的一些大公司的起源和兴衰,真是长了不少见识。

科技行业没有百年老店

像 AT&T 公司,摩托罗拉,太阳公司,网景公司和诺基亚等公司都曾经无比繁荣,但是在科技浪潮和一些大公司的竞争中失利,最后或被收购,或被拆分,或宣告破产。但是蓝色巨人 IBM 却在一次一次的浪潮中不断变革和创新,最终成就了今天的 IBM。而微软虽然在微机和 PC 端是绝对的老大,但是它在移动互联网和 Web2.0 上面明显已经表现不如 Google,雅虎和 Facebook。而苹果公司已经超过了微软的市值,成为全球最有价值的科技公司。而反观国内,上市不久的阿里现在已经跌破了发行价,而且知乎上面各种黑。另外百度也在移动互联网上面没捞到啥,反而是最低调的腾讯,靠着微信,手 Q 一步步巩固自己的地位。新兴的小米市值也早已突破百亿,成为中国出货量最大的智能手机厂商。科技行业发展太快,以致于很多公司还没来得及看清局势便已日渐势微。

差异化竞争

微软在微机市场能取得垄断,而不是跟 IBM 去竞争大型机市场。Google 研究搜索引擎而不去做操作系统,不过 Android 是个例外。(这表明微软和 Google 在相互入侵对手的领域,但是带来的结果是,苹果赢了。)苹果现在已经成为一个时尚品牌,它在中国消费者这里可没少赚钱。下一个 Google 一定不会是一个做搜索引擎的公司,而是其它领域的公司。

先从一个小的利基市场取得优势并形成垄断之后,再考虑横向扩张会更好。一开始要在大公司看不见的市场上面去积累自己的客户,多合作,少树敌,打枪的不要,悄悄地发财。

一些定律

摩尔定律:IT 产品的性能每 18 个月性能会翻一番,而产品价格会下降一半。如果制造硬件的公司不能够满足摩尔定律,那么它的利润就会减少。

安迪-比尔定律:比尔(盖茨)要拿走安迪所给的。即在 WinTel(Windows+Intel) 架构下面,处理器的性能提升会被新的 Windows 操作系统榨干。所以,消费者就必须一直更新电脑啦。

反摩尔定律:一个 IT 公司如果今天和 18 个月前卖同样多一样的产品,那么它的营业额要减少一半。

70-20-10 律:即一个行业内,老大占 70%的市场份额,而老二和老三分别占 20%和 10%。

诺威格定律:一家公司的市场占有率超过 50%以后,就无法再使市场占有率翻番了。必须通过创新机制,寻找新的市场增长点。

基因决定定律:创始人基因决定着公司的基因,而公司的基因决定了它以后的发展命运。

乔布斯传

很多人说自己是果粉,偶像是乔布斯。但是如果你没有看过乔布斯的传记,那就是盲目崇拜。

乔布斯和苹果的成功,除了乔帮主的个人品味,特立独行的处事风格,还与时代机遇密不可分。苹果公司是他和沃兹创立的,但是主要功臣是沃兹,因为这个极客使得个人电脑成为可能。随后 Apple 2 电脑大获成功,乔帮主和沃兹也大赚了一笔。但是他们各自的处事风格还是完全不同的,乔布斯太刻薄,而沃兹是个大好人。苹果上市的时候,乔布斯有一个一起创业的好友,居然没有一点股份,最后沃兹把自己的股份拿出来给了这些曾经的功臣。后面乔布斯带了一个莉萨团队,但是失败了。看到托斯金的 Mac 团队正在做一些很牛比的事,他又抢了人家的地盘。

在乔布斯的眼里,一个人要么是天才,要么就是垃圾。而同一个人在一天中,可能同时被说成是天才和垃圾。另外,他喜欢跟别人讨论,去进行灵感碰撞,但是最后,他会把共同碰撞产生的灵感归于他一个人的。乔布斯善于谈判和扭曲现实,他事事追求完美,即使篱笆墙的背面也要认真粉刷。

他是个严格素食主义者,即使癌症晚期,依然不会因为怕死而改变自己的饮食习惯。

他从小被抛弃,而他也抛弃过自己的女儿。他重回苹果公司的时候只拿 1 美元的年薪,但是苹果股价上涨以后,他却逼迫董事会给他大量股票和期权。

他在被人赶出自己的公司以后创办了 NEXT 公司,后来投资皮克斯并获得了几十倍的回报。当他重回苹果的时候,NEXT 公司为苹果带来了新机。我们熟悉的 Object-C 语言就是从 NEXT 带出来的。

另外乔布斯在美国远没有在中国这样被神化。

程序员的呐喊

这本书的作者在 Google 任职,里面很多观点争议比较大,但是比较对我的口味。(我是 Google 脑残粉)

作者说编程语言就是宗教,太 TM 对了。何止编程语言是宗教,代码编辑器,程序框架,操作系统等都是宗教。作者推崇 Lisp 和 Emacs,于我心有戚戚焉。

作者讨厌 Java 和 C#,推崇 Ruby,我也深以为然。虽然我还没有认真学习过 Ruby,但是我每天用的 Homebrew 工具和 Github,以及 Rails 的盛名,让我没有理由不爱。

本书还提到程序员应该学习一些数学和编译原理,并列举了这些知识可能的应用场景。最后,作者对 Google 也进行了吐槽。总之,这本书还不错。

异类

异类这本书我是看了唐巧大大的读书笔记后才买来看的。

该书通过研究那些被称为“异类”的人们的成功经验,来寻找他们成功的真正秘决。最后,得出结论,个体的成功除了智商和后天努力以外,环境和机遇的影响也起到了非常大的作用,甚至可以说是决定性作用。那些出生在 1955 年左右的人们,比尔盖茨和乔布斯,那些冰球运动员大多数出生月份相近。以及那些靠机遇顺利获得 10000 小时经验的人们。

在真正的机会到来的时候,只有他们能够抓住机会,成为时代的弄潮儿。

看完这本书,我更加应该在自己认定的领域坚持下去,早点达到 10000 小时,然后等待一个适当的时机即可。

从 0 到 1

我以为我看了 《从 0 到 1 》 会有想创业的冲动。因为很多人说不推荐看从 《0 到 1 》, 而推荐看《创业维艰》。但是,为毛,我看完还是没有创业的欲望。2333333

这本书我一下午就看完了,书的副标题是“开启商业和未来的秘密”。我看完后,还是没啥感觉。 可能还需要再多读几遍才行。

我的理解就是,想要成功创业,需要从 0 到 1 的创新,而不是从 1 到 n 的创新。现在中国所谓的微创新大多数都是从 1 到 n 的复制和创新。所以中国出不了 Google 和苹果这样的公司。所有企业的成功都是不同的,但是失败的企业大多有着相同的原因--创始人的基因,管理层过分逐利,忽视用户,盲目扩张等,都会导致企业的失败。

另外,我发现 Google 的 CEO 创业之后,是请了专门的职业经理人来打理公司,他们本人只负责产品和技术。因为他们很年轻,而且又是技术出身,这相当于请人来给自己当老师。而且,不管公司融资多少, 企业的话语权还是牢牢掌握在创始人的手里。而在跟微软这种大公司在竞争的时候,需要联合所有的厂商一起来竞争,Android 就是这样打败了 Windows Phone。

总结

少刷微博和微信,多看经典好书,原来世界是这么美好。

完。

November 16, 2016 09:31 AM

像极客一样使用 Mac

Mac 是属于程序员的电脑,因为它不仅有好看的图形用户界面,还有非常好用的 Unix 命令行接口。我们编程中有很多任务都需要借助命令行来完成,比如 Git,文件操作,Shell 脚本等。

本文主要介绍我常用的一些 Mac 上面的极客工具。

命令行工具

iTerm2

iTerm2 是 Mac 上面 Terminal 的一个替代品,它本身拥有很多实用的特性。比如友好的全屏支持,无鼠标复制文件,搜索,自动补全等。完整的特性列表可以参考Features - iTerm2 - Mac OS Terminal Replacement

我当初选择 iTerm2 的原因主要是两个:一,配色好看。 二,支持本窗口全屏。(因为这样可以屏蔽 QQ 消息,编程的时候更专注)

Tmux

Tmux 是一个终端复用工具,因为我不想一次打开 N 个 iTerm2 窗口,有了 Tmux 只需要一个终端即可。另外在服务器端,如果远程服务器开启 Tmux,当你断开连接后,再次登陆服务器,Tmux 可以保存上次打开的会话。另外,Tmux 还可以方便地定义多窗口,支持结对编程。

Percol

以前在使用命令行的时候,当我需要输入一个历史命令的时候,我会按方向键不停地寻找。后来,我学会了 C-r 来搜索历史,效率大大提升。现在,我用mooz/percol , 它简直就是一个神器,让你可以通过 fuzzy match 的方式来搜索历史命令。

Zsh

Zsh 绝对是每一个终端爱好者提升效率的神器,结合robbyrussell/oh-my-zsh ,它可以让你的命令行效率提升 N 倍。Oh-my-zsh 提供了大量的主题和插件。现在,我推荐使用 zsh-users/antigen 来管理你的 zsh 插件。antigen 是和 Vim 的 Vundle 类似的包管理器,强烈推荐。

编辑器之神 Vim

关于 Vim 的赞美实在是太多了,我自己以前也是 Vim 的脑残粉。虽然我已转 Emacs,但是在服务器端,还有 Emacs 配置挂掉的时候,Vim 都是我的首选编辑器。而且我在 Emacs 里面使用的是 Evil 插件,把 Vim 的 modal editing 继承发扬光大。

Vim 之所以永不褪色,对我而言原因有二:

  1. Vim 很多 Unix 系统都是自带的,速度快。
  2. Modal Editing + 完美终端集成

编辑器 - Emacs

因为我现在基本上算是 Emacs 死忠粉,所以我会推荐所有程序员学一学。不仅因为 Emacs 是操作系统,Emacs 是神的编辑器。

Emacs 本质上是喜欢自由和 Hack 的极客们自己的工具,每一个 Lisp 黑客都有属于自己的配置。

另外,Lisp 也是一种魔法,谁学谁知道。

改键工具

Mac 本身

Mac 本身系统全局内置 Emacs 的快捷键:C-a/C-e/C-n/C-p/C-f/C-b, 如果 Ctrl 键不好按,那么你的小拇指可能会疼。通过 Mac 系统自己的改键,可以很方便地把 CapsLock 和 Ctrl 键互换。

2015-08-30-use-mac-like-a-geek_swap-ctrl-and-caps-lock.png

不管你是使用 Vim 或者 Emacs,甚至高效地使用 Mac 下面的命令行, 有一个舒服的 Ctrl 键都是必需的。

Karabiner - Software for OS X

这是 Mac 上面一款强大的改键工具,我用它把我电脑上面的右 Command 键改成了 F19. 这样相当于给电脑添加一个快捷键名字空间. 配合下面的 Keyboardmaestro 工具,可以实现非常酷的改键效果.

统计工具

快捷键的修改也是有学问的,通过统计方法,找出自己平时用得最多的软件和操作,然后设置相应的快捷键,这样效率是最高的。强烈推荐一个工具,可以统计你每天使用各种软件的时间以及按键的分布。

WhatPulse

GUI 工具

Beyond Compare

这个软件是收费的,不过有时候确实很管用。比较文件和文件夹差异,应该是我已知的最强大的工具了。

Source Tree

这个没啥好说的,跨平台的 Git 图形工具。 我一般只用它来解决冲突,偶尔也用它来做 Commit 之前的 Code Review。因为 Emacs 的 Magit 是最好的 Git 工具。

Sublime Text

这个主要是开箱即用,我基本没安装任何插件。主要是做一些简单的文件操作。

Mou

Markdown 编写工具。不过速度有点慢,我一般还是用 Emacs 编辑。

总结

这些是我本人目前都在使用的工具,不一定所有人都会喜欢它。正如本文标题所言,它们是给爱折腾的极客们准备的。同时也欢迎大家跟我分享 Mac 上面的好工具。

November 16, 2016 09:31 AM

Mac 无光驱安装 Windows 10

我的旧 Mac 电脑的型号是 13 inch 2011 late,它是有光驱的。后来因为我的硬盘坏了,我换了一个 1T 的机械硬盘。不过,当时的 SSD 已经比较便宜了,于是我给它加装了一块三星的 120G SSD。因为电脑配置是 i5+8G 内存,瓶颈在硬盘。(更深层次的原因其实是--穷。。。不然这么折腾干嘛,直接 15 寸顶配 retina 走起。) 不过没了光驱,你会发现安装系统变得很麻烦了,只能通过 U 盘来安装了。

本文就是记录我是怎么使用 U 盘来安装 Windows 10 的。

Hack Boot Camp Assistant

因为 2011 年的 Macbook Pro 本来是有光驱的,所以默认 Boot Camp Assistant 没有提供 U 盘安装方式。当你打开 Boot Camp Assistant 的时候,你是看不到下面的选项的:

Create a Windows 7 or later version install disk

取而代之的是下面的警告:

There is no optical drive(因为我们把光驱拆了)

此时,我们需要来 Hack 一下 Boot Camp Assistant。

  1. 打开 About this Mac -> System Report,找到 Boot ROM Version 后面的字符串,例如:MBP101.00EE.B09(这个是 15 寸电脑上面的,13 寸的会有所不同)
  2. 同时找到 Model Identifier: MacBookPro10,1(我的 2011 年的笔记本是 MacBookPro8,1)
  3. 打开 Application 下面的 Utilities 文件夹,找到 Boot Camp Assistant.app
  4. 右键“Show Packages contents”,然后找到 Info.plist 文件
  5. 在 DARequiredROMVersions 里面添加你的 Boot ROM Version
  6. 在 PreUSBBootSupportedModels 里面添加你的 Model Identifier,并且把 PreUSBBootSupportedModels 改成 USBBootSupportedModels
  7. 保存 Info.plist 并重新启动 Boot Camp Assistant

现在你就可以使用 Boot Camp Assistant 来制作安装盘了。(当然如果你还有其它 Mac 电脑,也可以不用这么折腾,直接在另外的电脑上面制作安装盘即可)

Burn the ISO to USB

这一步是非常简单的,你需要准备一个 Windows 10 的安装 ISO,同时准备一个大于 4G 的 U 盘,最好是 8G 以上。

然后把 U 盘里面的东西备份好。打开 Boot Camp Assistant,插上 U 盘,选择 Create a Windows 7 or later version install disk。

接下来按照提示走就好了。(注意在开始界面不要勾选下面两个选项就行了。)

2015-08-23-install-windows-on-mac-without-optical-driver_bootcamp.png

Configure the GPT disk and EFI loader

因为在 Mac 上面使用 EFI 需要 GUID 分区表(简称 GPT),而不是传统的 BIOS/MBR 分区表。而到目前为止, Windows 只能在 MBR 的磁盘上面安装,通常都是使用 Boot Camp Assistant 来创建一个带 MBR 的分区来安装双系统。

因为从 Windows 8 以后开始支持 EFI 的方式来安装了,所以这让我们这些无光驱党看到了希望。

(不过有个巨坑,EFI 方式安装的 Windows 10 是没有声音的。所以,这篇文章介绍的东西,其实是然并卵。不过 2013 年以后的笔记本是没问题的,所以如果是 2013 年之前的笔记本还是老老实实用光驱安装 Windows7 吧)

安装过程如下:

  1. 准备一个新的磁盘分区(格式化成 NTFS)
  2. 生成 GPT with protective MBR 的分区表(默认是 GPT with hybrid MBR)
  3. 进入到 Windows 10 安装程序后,通过 Diskpart 命令手动挂载 EFI 分区

具体细节见本文最后的参考文章。

安装失败的可能原因

  1. USB 必须是 MBR 分区表(使用 Boot Camp Assistant 制作的启动盘默认是符合要求的)
  2. 目标磁盘必须是 GPT
  3. 目标磁盘不能包含一个 hybrid MBR,需要使用工具处理成 GPT with protective MBR(具体方法见文章后面的参考文章)

安装 Ubuntu

大家也可以用这种方式安装 Ubuntu,不过烧制 Ubuntu 的安装盘和 Windows 的略有不同,不需要 Hack Boot Camp Assistant。具体方法见:How to create a bootable USB stick on OS X | Ubuntu

制作好安装盘后,同样的是分区,分完区以后,插上启动盘并重启。待听到“咚”的一声后,按住 Option 键不放,等出现启动磁盘管理工具的时候,选择 U 盘开始安装即可。安装的时候可以把之前分好的区重新格式化成 Ubuntu 需要的文件格式。

总结

因为这个安装过程着实让我吃不了少苦头,这里记录一下,希望对有需要的朋友能有所帮助。

November 16, 2016 09:31 AM

November 13, 2016

包昊军的博客

November 03, 2016

包昊军的博客

November 02, 2016

GeekPlux

如何管理好自己的密码

最近 Xcode Ghost 席卷三大平台,有预谋有组织有纪律。很多人看到这个消息纷纷修改常用密码,但我觉得密码管理其实功在平时,一个好的密码可以大幅提升你账户的安全系数。

而且,现在基本上每个网站都需要注册才能享用全部服务,我自己注册了上百个网站,意味者我有上百个账号密码需要记,用脑子记根本记不住(没办法,记忆力差)。后来我慢慢摸索出自己的一套策略用来管理密码,感觉实用且不算复杂。

设计方法

  1. 首先要对账号和密码进行分级分类。根据重要性、产品使用频率、产品应用场景来分。我自己分为五类:银行卡类、涉及支付类、社交账号、涉及数据存储类(各种云存储、云服务等)、其他。
  2. 银行卡类:单独设6位数字,每张银行卡都有各自的密码(规则自己定:比如前三位是自己想的,后三位是卡号的某几位)。
  3. 涉及支付类:单独设密码,方法参考5、6两点。
  4. 社交账号类、涉及数据存储类:着重对待,方法同样参考5、6两点。
  5. 密码设计方法:单独想出一句话,用这句话每个字的首字母构成密码最基本的部分。大小写和符号自己有规律地加。
  6. 排列组合:密码太多记不住很正常。可以用两组(或多组)短语排列组合成整套密码。
  7. 银行、涉及支付类、重度使用产品均定期更换密码。一般密码位数不低于15位(不要以为15位很长)。

密码存储

那么密码设计出来,要存到哪里去呢?

  • 全部用脑子记住。这种最安全也是最高效的方法,但是需要记忆力。
  • 用 1Password, Lastpass 等密码管理软件记。我觉得这个适用于上文提到的「其他类」。
  • 用文本笔记、表格记录。这个也没什么不可以,但是你一定要保存好这份文件,否则后果很严重。这里有个小技巧,你可以把密码全记录到一个 excel 文件中,然后把文件后缀改为 .avi,以此类推。

以上就是我全部的方法了。之所以分成五类主要是因为涉及钱的账号密码,一旦泄密损失的是自己,而社交账号和数据泄密损失的是他人。

一般人可能会觉得自己的数据没什么价值,而且朋友们也都很机智。但是在特定情况下,你的数据会有可怕的功用。想象一下,一个人如果拥有你的绝大部分数据,同时又拥有你所有的人际关系。那么他就可以轻易的运用数据去模仿出你的样子,从而获取他人的信任。一旦获得对方的完全信任,注意,是完全信任,那对方就会沦为待宰的羔羊,任人摆布。

总之,密码和我们的生活息息相关,管理好自己的密码说不定还会有意外的收获:
一个密码改变了我的人生


本作品采用知识共享 署名-非商业性使用-禁止演绎 4.0 国际 许可协议进行许可。

November 02, 2016 10:20 AM

为什么文章写得好的人都很厉害

首先定义什么是好文章。我觉得 能让读者领悟自己想表达的 就叫好文章。换种说法就是 能达到自己写作目的 的文章就叫好文章。

每个人写作的目的不同,有的人抒情,有的人叙事,有的人论证,但这最终的目的都是为了 让读者理解 。即使你的目的是故弄玄虚(或者让别人猜不透自己),本质上也是让别人把你的文章解读成你想让他解读的样子。

所以,能写出好文章的人,具备两个重要的能力。

  • 能恰如其分表达自己想法的能力
  • 能让对方理解自己想法的能力

这两点看起来易如反掌,一般人也以为自己都做到了,但实际的情况是: 我说的和我脑子里想的不太一样,你理解的和我说的不太一样

要做到第一点,首先要对自己脑子中的想法有很好的把握,其次要对文字运用的很熟练。这样写出来的文章才结构清晰,逻辑通顺,言简意赅(说白了就是简单简单再简单)。文字的运用取决于多年来对语言的积累,对想法的把握则需要多了解自己的思维方式,锻炼思维能力……我相信你也知道,我们9年义务教育都在锻炼一种叫思维能力的东西。

要做到第二点,则要照顾到对方的理解能力。比如很多博士写出来的论文,读者根本看不懂,因为读者无法站在和他一样的高度去理解他的文章。继而导致他的文章不流行。而有一个人,他既能理解原作者的文章,又有很好的文字功底,把原文改写的通俗易懂再发出来。大家一看就懂,于是觉得这个人很厉害。其实很多所谓的畅销书不都是这样么。

我觉得拥有以上两点的人基本已经无敌了。不信你可以去拜读一下我朝太祖的文章,感受一下高深哲学被解释成蕃茄鸡蛋的感觉。

那么如何拥有这样的能力呢?我认为写作就是最好的锻炼方式。所以这是个正循环:

写得好->变厉害->写得更好

从把文章写简单做起吧。


本作品采用知识共享 署名-非商业性使用-禁止演绎 4.0 国际 许可协议进行许可。

November 02, 2016 10:20 AM

为什么我喜欢写代码

我知道很大一部分的程序员,最初都是游戏爱好者。因为喜欢游戏,进而喜欢电脑,研究它,想知道怎么制作个游戏,怎么破解一个游戏等等。这部分因为喜欢一件事进而喜欢上鼓捣代码的人,算作一类。另一类就是我这种,一开始并不知道自己真正喜欢的是什么,甚至不知道什么是代码,但接触了编程之后,就停不下来的人。

虽然我在高考填志愿的时候,专业一栏都写的是「计算机科学与技术」,但我对这个专业的了解比对女性生理结构的了解还少。大一的课程终于接触到了真正的代码,我却没有因此产生浓厚的兴趣。而后来,当运行成功一个自己编写的程序时,那种头皮发麻,一股电流从背脊击穿头部的感觉,让我瞬间明白了这才是我该做一辈子的事。

编程的反馈很快

编程之所以有趣,最主要的原因是它能即时反馈。你写下一行代码,立即就能知道它的对错;你写完一个程序,一运行就能知道成功与否;如果做的是界面,那就更明显了,代码一变,界面立马就变。

所以编程学起来也特别快,它能给你立马呈现出你这两个小时到底学到了什么。成就感的积攒,又会推动你进行下一步的学习,不停迭代。

人总是喜欢能更快得到回报的东西。

痛并快乐着

众所周知,程序员最讨厌八阿哥(Bug)。除非是当天任督二脉被打通,一般情况下程序员写出的代码都会有Bug。有时候找Bug找的真的想撞墙,人都快崩溃了。我至今忘不了当初我上司,每次点击运行之后,大喊一声「决战吧,代码!」的样子。

但只要Bug一被解决,整个人都会「羽化而登仙」。。。被摧残过后的成果最令人兴奋。(不过我在这里友情提醒各位,如果你看到一个程序员眉头紧锁,千万不要去打扰他,不要问我为什么)

编程很实用,且容易出作品

好的程序员「笔落惊风雨,诗成泣鬼神」,分分钟能做出一个优秀的网站、APP 或实用的小工具。虽然说要做出颠覆世界或者能给自己带来巨额财富的软件很难,但做一个普通的小程序还是没问题的。比如我有个学长,特别喜欢写那种抢秒杀的代码,双十一前后抢到了一部 iPhone 6S 和各种免单。。。

代码改变思维

Code change the world 这句我一直知道,但代码改变思维我是真切体会到的。尤其是你学的编程语言或者框架多了之后,你的思维会被这些代码所影响。不知不觉,我们的思维方式可能从发散思维,变成了线性思维,或是面对不同的问题采用不同的思维方式。

一切的学科学到最后都是哲学,代码也是有哲学的。它渗透在我们的血液里,给我们渲染出了新的视野。很多程序猿在从业多年之后还保持着强大的好奇心,卓越的学习能力,开阔的眼界,这一定程度上能归功于代码。

虽然现在很多人鼓吹互联网泡沫就要破灭,但我觉得代码仍会是在未来生存必备的技能。美帝宣传「人人都应学编程」不是一句空话。


本作品采用知识共享 署名-非商业性使用-禁止演绎 4.0 国际 许可协议进行许可。

November 02, 2016 10:19 AM

如何阅读一篇学术论文

这是一个知识过剩的时代。大量标榜为「干货」的文章每时每刻都在不断地产出,搜索引擎的便捷似乎也让我们离这些「知识」只有一步之遥。然而,随着多年在互联网上的浸淫,我发现越是容易得到的知识,价值越不大,留存的时间也越不长。所以,相对于那些东拼西凑组成的所谓干货,一本好书或一篇好的学术论文,才是知识的结晶(单从前后两者成文的时间上就可以很好的证明这点)。

关于「如何阅读一本书」的讨论,已经盈千累万,而关于「如何阅读一篇学术论文」的似乎还凤毛麟角。所以这篇文章我打算写一写我在阅读论文过程中总结出的一些方法,希望能对你有帮助,当然如果你有其他更好的方法,欢迎补充。文章共分为四个部分,第一部分介绍读论文的核心要素,第二部分列出阅读单篇论文的具体步骤,第三部分是一些补充。方法论讲完之后,最后一部分将阐述为什么要阅读文献期刊。

一、阅读论文的核心要素

根据每个人读论文的目的不同,阅读的侧重点也会有所不同,但都不外乎要获取知识、解决问题,所以无论你的目的是什么,有三个核心要素是你在阅读一篇论文时必须掌握的:

  1. 这篇论文主要表达的是什么?作者的写这篇论文的目的是什么?或者说是这篇文章做出了什么成果?
  2. 这篇论文的观点/成果是如何实现的?作者用了哪些论据来支持他的观点/成果?
  3. 相关的工作有哪些?在这篇文章之前有什么已成立/取得的观点/成果?作者的观点/成果主要是建立在哪个的基础上?作者的观点/成果比别人突出在哪里?

阅读论文不能盲目的去读,要带着问题去读才有收获,只要了解了这三个核心要素,就可以对论文有一个整体的印象。

二、阅读论文的步骤

针对单篇论文:

  1. 认真地阅读标题、Abstract 和 Introduction。
  2. 把每一个章节的标题、子标题都过一遍,具体的内容先不急着看。
  3. 阅读 Conclusion,回应刚才的 Abstract 和 Introduction。
  4. 瞥一眼参考文献,看引用了哪些文章,里面有没有自己已经看过的。

这四个步骤完毕之后,基本上可以解答上文提到的三个核心问题。而且你还能获得额外的一些信息,比如这篇文章和我研究的相关性、值不值得我精读、写的是否通俗易懂、我的知识储备是否能读等等。

如果得到的判断是这篇文章需要精读,那么我们可以开始下面的步骤:

  1. 阅读文章的主体,重点注意抛出的观点,和提出的论据。
  2. 对于有公式、图表或其他具体论述的地方要多关注,时间足够就认真阅读。
  3. 遇到有引用参考文献的地方,先不要打断当前的阅读,但要在脑子里有个印象,以便将来进行相关的阅读,从而更好的理解本文的背景。

这三个步骤花的时间可能是上面四个步骤时间的几倍,完成之后基本上对整篇文章的细节都掌握了。如果没有读懂,可能是自己的知识不够,也有可能是状态不好,你可以选择把这篇文章放在一边以后在读,或者再把这三个步骤重复几遍。当然,如果你读懂了,并且这篇文章的内容正是你苦寻已久的及时雨,那么可以进行下面这步:

  1. 把文章的论证重现一遍。
  2. 改动重现过程中的相关步骤,与本文进行对比。

完成这步,你对文章的理解程度肯定会比一般人深,如果有新的发现或结论,你还可以与原作者进行交流,或自己在其基础上发表新的论文等。

三、一些补充

上一节中的阅读步骤都是针对单篇论文的,其实在阅读论文过程中,我们很少是阅读完一篇再读下一篇,而通常是同时读几篇,每篇只读一部分,得到答案就够了。所以针对上述的步骤,还需要一些补充:

  1. 要获取某个知识/解决方案时,应该是大规模、分批次阅读,而不是逐篇阅读(通常这篇读不懂的,会在另一篇中有答案)。
  2. 拿到一个方向相关的大量论文后,尽可能从标题判断出阅读顺序(也要考虑发表时间)。
  3. 阅读单篇论文,不要逐行阅读,提炼出适合自己的阅读论文顺序。
  4. 阅读论文时要敢于想象(猜),大胆假设,小心验证,很可能一次性就猜对,节约很多时间。
  5. 每篇论文中详细的推导过程,很可能你根本不需要懂。
  6. 不要有公式恐惧症。关键的公式可能就三五个,其他不懂也没关系。公式之间的恒等式推导过程可以完全略。假如到了万不得已非要看懂的情况,重点看公式推导过程中引入的假设条件,和公式中每个字母所指代的东西。
  7. 读不懂的时候,不要气馁,可能是自己的知识储备还不够,也可能是今天的状态不好,先放一边,读下一篇。

大量阅读提升广度,精准阅读提升深度。读的越多越熟练。

四、为什么要阅读文献期刊

只要深入掌握到阅读与分析期刊论文的技巧, 就可以掌握到可能在大学不曾研习过的三种能力:

  1. 从无组织的知识中检索、筛选、组织知识的能力
  2. 对一切既有进行精确批判的独立自主判断能力
  3. 创造新知识的能力

阅读论文是我在研一后半年才意识到的一件收益很大的事(惭愧),也是一个漫长的旅程。如果你也有幸开始这个旅程,希望你能轻松上路,如果你在途中遇到了困难,希望你能「不求甚解」,从万千论文中挑出自己最容易读懂的读,挑软柿子捏。等走的路多了,自然会知道哪有坑洼,哪有岔路。这些话也送给我自己。

参考文献

  • Keshav, S. (2007). How to read a paper. ACM SIGCOMM Computer Communication Review, 1–3. http://doi.org/10.1145/1273445.1273458
  • Laramee, R. S. (2011). How to read a visualization research paper: Extracting the essentials. IEEE Computer Graphics and Applications, 31(3), 78–82. http://doi.org/10.1109/MCG.2011.44
  • 彭明辉《研究所新生完全求生手册》

本作品采用知识共享 署名-非商业性使用-禁止演绎 4.0 国际 许可协议进行许可。

November 02, 2016 10:18 AM

如何搭建一个私人网盘

文章主要讲了为什么要搭建私有网盘,以及如何用 docker + ownCloud 搭建。

前两天,360 云盘宣布将停止个人服务。一石激起千层浪,关于如何选择网盘,如何应对网盘关闭的讨论一下子又变得此起彼伏。没办法,目前的现状是,网盘很难有大的盈利空间,还面对严苛的内容审查和隐私保护,虽然于用户来说提供了便利,但于公司来说实在是一件出力不讨好的事情。

之前的网盘方案

国外的网盘我一直是三家一起用,分别是 Dropbox 存储代码和一些重要或私密文件;Google Drive 存储一些大文件和私密文件;OneDrive 存储一些电子书(同步太慢了)。国内的网盘我之前只用两家,一是坚果云,放一些个人常用的小文件,包括一些文档和软件配置文件;另一个是百毒云,放一些各处转存来的大文件、自己的照片和学习资料,一方面因其空间大,另一方面因其同步流畅。然而,百毒云前段时间把我的网盘全面封掉了,丢失了很多大学时的照片(其它文件要不不重要,要不有备份),申诉无果,实属无奈。

搭建一个只属于自己的网盘

所以我决定搭建一个只属于自己的网盘。考察了几种方案(包括买 RAID 或 NAS 等),发现已有人在这方面做了努力,提供了像 SeafileownCloud 这样的产品。接下来对比了两个软件,我决定选择用 ownCloud,主要出于以下几点考虑:

  • 可以设置是否加密,保证数据安全。
  • ownCloud 可以用于同步日程、联系人、浏览器书签等,最重要的是密码管理,这对于目前有无数密码需要记的我们非常实用。ownCloud 还有个应用商店,大家可以自行发现有用的应用。
  • ownCloud 提供网页和各种设备、系统的客户端(Windows、Mac、Linux、iOS、Android皆有)进行访问你的网盘。
  • ownCloud 能将外部存储(如 FTP、WebDAV、Amazon S3,甚至 Dropbox 和 Google Drive)的文件挂载到 ownCloud 上,实现无缝存储和分享。
  • 文件支持版本管理,还有回收站,所以不必担心误删。

搭建方法

首先你得先有一个自己的 VPS。。没错,要不然你的数据往哪放,ownCloud 在哪运行。

有了 VPS 之后,就可以按照官网教程一步一步安装搭建了。然而,步骤相当繁琐,你得先安装 PHP、MySQL、 Apache 等等,所以我们要祭出神器 —— docker(这里就不介绍 docker 的用法了,以下内容默认大家对 docker 的基本使用有所了解)。这样一来,之前冗长的步骤,就化成了三步:

  1. 安装 docker、docker-compose,下载 ownCloud 的 image
  2. 配置 docker-compose.yml
  3. 配置完毕,启动,打开 ownCloud 主界面配置数据库、管理员等

下面是对上面三步的详细讲解,嫌太长的话可以不看。只需要把下面用到的两个 docker images (owncloud、postgres)下载好,安装 docker-compose 并拷贝 docker-compose.yml 文件到你想要存储 ownCloud 数据的文件夹,然后运行 docker-compose up 就好,一气呵成。

使用 docker

安装好 docker 之后,直接下载 owncloud image 运行

1
docker run --name owncloud -p 80:80 owncloud

其实就可以看到 ownCloud 已经运行起来了,访问你的 VPS 地址,就可以看到 ownCloud 的界面。

但这时的 ownCloud 还没有数据库,所以我们还需要用 docker –link 来添加一个数据库存储 ownCloud 的数据,这里用到了 postgres 这个 image(数据库你可以自己定,不一定要用 postgreSQL)。

1
2
docker run --name owncloud-postgres -e POSTGRES_PASSWORD=mysecretpassword -d postgres
docker run --rm --link owncloud-postgres:owncloud-db --name owncloud -p 80:80 owncloud

第一条命令会启动一个 postgreSQL 数据库,默认的用户是 postgres,密码设为了 mysecretpassword,host 是 owncloud-db。

但这时我们运行的 docker container 一旦删掉,我们的数据就没有了,所以我们需要用 docker 中的 volumes (或 docker data volumes)来把 ownCloud 的数据持久化。

配置 docker compose

这样一来,我们得启动两个 container 作为 data-only container,然后再启动 owncloud 和 postgres 关联这两个 data-only container,非常繁杂,幸亏我们有 docker-compose 帮忙。先安装它:

1
pip install docker-compose

然后配置 docker-compose.yml,下面配置中的 volumes 就是在配置数据持久化的目录结构。由于我把 docker-compose.yml 存在了VPS 的~/owncloud文件夹下,所以底下 volumes 配置中,冒号前面的宿主目录是那样写的,而冒号后面的是 container 中的目录,具体:

  • /etc/postgresql 存储数据库的配置
  • /var/lib/postgresql 存储数据库中的数据
  • /var/www/html/app 存储 ownCloud APP 的数据
  • /var/www/html/data 存储 ownCloud 的数据
  • /var/www/html/config 存储 ownCloud 的配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# Composition of the containers
postgres-data:
image: postgres
command: /bin/true
volumes:
- ~/owncloud/etc/postgresql:/etc/postgresql
- ~/owncloud/var/lib/postgresql:/var/lib/postgresql
owncloud-data:
image: owncloud
# This is a data container, so we want to exit as soon as the container is created
# BUT we will have to fix permissions issues first (33 is the ID of the www-data user)
command: /bin/bash -c "/bin/chown -R 33 /var/www/html/data && /bin/chown -R 33 /var/www/html/config"
volumes:
- ~/owncloud/var/www/html/apps:/var/www/html/apps
- ~/owncloud/var/www/html/data:/var/www/html/data
- ~/owncloud/var/www/html/config:/var/www/html/config
owncloud:
image: owncloud
ports:
- 8080:80
volumes_from:
- owncloud-data
links:
- postgres:postgres
hostname: cloud
domainname: cloud.example.org # Change to the hostname you will use
postgres:
image: postgres
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=mypostgrespassword
volumes_from:
- postgres-data

把 docker-compose.yml 配置好之后,只需运行

1
docker-compose up

就可以把 ownCloud 运行起来了,上一步中的很多操作,这里一步就搞定了。不过切记!owncloud-datapostgres-data两个 container 和 volume 千万不要删。删之前请备份

ownCloud 配置

访问你 VPS 的 8080 端口(刚才配置文件里写了)打开 ownCloud 主页,需要做两件事

  1. 输入管理员的账号和密码
  2. 选择数据库用哪个,且输入数据库配置,这里对照我们刚才 docker-compose 里的写的输入就好

点击完成,一切 OK,进入文件页面尽情探索吧!

参考&延伸阅读


本作品采用知识共享 署名-非商业性使用-禁止演绎 4.0 国际 许可协议进行许可。

November 02, 2016 10:17 AM

October 30, 2016

包昊军的博客

October 22, 2016

X lambda

再见,YY

不知不觉地,有如忧伤
夏日竟然消逝了
如此地难以觉察,简直
不像是有意潜逃

当我无意中在电台节目听到狄金森这首诗的时候,2016年的夏天已经过去了,如此地迅捷轻快,就像诗中所说,即使没有翅膀,夏日也能如此轻逸地逃去。这个夏天,我从工作了5年多的公司YY离职了。

在一个城市呆上几年之后,这个城市的脉搏和节奏就会慢慢渗入你的生活,不知不觉地就会变成你的一部分,就像我已经渐渐地习惯了广州白天人流的拥挤,还有夜市街道的喧闹。在一个公司呆上几年之后,它的风格与精神也会慢慢变成你的一部分,潜移默化地,如此地难以觉察,以致于我已经无法回想起现在的自己与当初进入YY的时候有哪些不同之处,仿佛一直从未变过,而其实变化实在不少。

几年前入职YY的时候,我还是一个毕业两年多的年轻小伙,而现在已经变成一个中年大叔。这几年间,YY从一个500人的小企业发展成一个3000人的大公司,办公室越换越大。一般人都可能会有这种错觉,当一个大公司的员工谈他所在的公司有如何如何成就的时候,就好像收到了一种暗示:说话人肯定参与其中并为此做出了很大的贡献。前面对YY的叙述好像暗示对此我有很大的功劳,然而实际上人微位低,我只是数不清的众多一线员工中的其中一个,努力工作却只对大事件有非常微小的作用。但无论是非常直接地还是间接地,至少每个人都是参与其中的,能感知YY到办公室的氛围与变化,陪伴经历了一些事情:公司上市了,然后准备私有化又取消;架构调整,部门成立了又分拆;员工流动,新员工来了变成老员工,然后又离开。因为有上市的机会,很多员工都拿到不错的股票回报,有人因为拿得多退休离开,有人因为拿得少不满离开,有人因为公司给的钱多而入职,也有人因为公司薪酬太低而离职,有毕业一年跟到好产品就飞黄腾达的明星员工,也有加入多年还是在一线耕耘的无闻小兵。在这里,财富和机会是如此的既近在眼前,又远在天边。在这里,事情和旋律是如此的井然有序,又混乱不堪。在这里,决择和人生是如此的灿烂光辉,又黯淡无声。这样的描述像是在描写淘金热潮时代的旧金山,或者改革开放初期的深圳,却也是对这几年的YY的最好概括。

活在这样一个变化频繁落差巨大的时间地点上,我想每个人都会多多少少发展出一种淡定的态度,你不知道什么时候会有一些好事或坏事落在你的头上,正如你不知道哪一天哪个部门会做什么的调动,又或者某个同事在某一天会兑现大量股票买个房车,或者哪个同事又在某一天突然离职,变化来得快而不能掌控,所以慢慢的你就有了一种对变化的淡泊,或者叫冷漠。很多事情我们单个人都无法掌控,所以活在当下做好眼前就已经足够。得到了不必过于欢喜,失去了也未必是灾祸。这种淡定,我想应该是这几年在YY的经历得到的独特体验。

写到这里,我知道很多读者都想问同一个问题:别扯这么多那些都没有人关心,你就说说YY上市你拿到了多少股票多少钱,发财了没有。而且问这个问题的同时,脑海中自然地会出现一个暴富退休的年轻人,在飞机游艇上写回忆录传授成功经验的画面。遗憾的是,这个画面并没有发生在我的身上。所以我还是继续在YY做着一线小兵的工作,过着属于我的平淡生活。

几年前在加入YY的时候,机缘巧合之下由老傅带入了后台存储团队,之后便一直呆在这个团队做存储相关的工作,中间经历了多次团队调整和人员调动,虽然后来也曾想过离开去做其它方面的事情,然而终于没有成行。从最早的10人小分队到后来的数十人大团队,从Oracle到Mysql,又从早期的只有关系数据到后来的文件系统,KV,Cache,YY存储团队这几年的经历可以说是一个互联网公司后台存储团队发展壮大所走的典型道路,产品线分类越来越丰富,系统也变得越来越大。很幸运地我能跟随着存储团队的发展在这几年间接触到了各种不同的技术和应用,扩展了眼界。当在技术上已经把存储产品线丰富起来之后,更多的工作就落在接入业务上面,以业务的体量来做为KPI,然而一个公司的快速膨胀期总是有限的,毕竟快速增长状态不是常态不能长期保持,当业务体量上无法取得大突破的时候,这种KPI制度就会让技术人员觉得非常难受,毕竟业务的发展有独特的周期和特点,并非技术人能完全把握。做平台的人总是希望接入所有种类的业务,就像做业务的人总是希望能服务尽量多的用户,然而实际情况并不总是那么理想。在YY存储团队混迹了几年之后,我已经能明显的感知到这种不能突破的天花板。路难行,行路难,停杯投箸,拔剑四顾,最终我还是选择了离开。

一份工作就如一份关系,从一个工作离职跟恋人一次分手也有类似的地方:曾经彼此有过美好的时光,当不能继续携手向前的时候,你所能做的只有放手并祝福对方。即使后来在某个遥远的异乡,夜里你孤独地醒来,回想起过去属于恋人的甜蜜的点点滴滴,也只能一声叹息。

祝YY越来越好。感谢在YY每一位同事,愿你们都得到幸福。

October 22, 2016 01:37 PM

October 15, 2016

Gih’s Blog

我的计算机史

  • 1999: DOS, 软盘, 金山, Win95/98, 五笔打字, 冷启动, 热启动…
  • 2004: 网吧, Internet, HTML, Windows Me/2000, 个人主页, BBS, QQ, 易趣, Tom, 雅虎…
  • 2005: QuickBasic, VisualBasic 6.0, Windows XP…
  • 2006–2008: 组装机, 微机原理, IBM80x86汇编, C语言, 操作系统, 数据库基础, 网络基础, 51单片机, ASP.NET, IIS, 3DsMax, 计算机图形学, PhotoShop…
  • 2009–2011: Ruby on Rails, MVC, Perl, CGI, Linux, FreeBSD, Solaris, AIX, IBM小型机, Web 2.0, LAMP, ThinkPad, System adminstrator…
  • 2012–2014: Ruby on Rails, Javascript, DevOps, BigData, Hadoop, AWS EC2, Cloud, Common Lisp, Go语言, 远程开发, Macbook pro…
  • 2015–2016: Ruby on Rails, OA, 光伏, Electric vehicle…
  • 2017: …

October 15, 2016 12:00 AM

我的计算机史(关键词)

  • 1999: DOS, 软盘, 金山, Win95/98, 五笔打字, 冷启动, 热启动…
  • 2004: 网吧, Internet, HTML, Windows Me/2000, 个人主页, BBS, QQ, 易趣, Tom, 雅虎…
  • 2005: QuickBasic, VisualBasic 6.0, Windows XP…
  • 2006–2008: 组装机, 微机原理, IBM80x86汇编, C语言, 操作系统, 数据库基础, 网络基础, 51单片机, ASP.NET, IIS, 3DsMax, 计算机图形学, PhotoShop…
  • 2009–2011: Ruby on Rails, MVC, Perl, CGI, Linux, FreeBSD, Solaris, AIX, IBM小型机, Web 2.0, LAMP, ThinkPad, System adminstrator…
  • 2012–2014: Ruby on Rails, Javascript, DevOps, BigData, Hadoop, AWS EC2, Cloud, Common Lisp, Go语言, 远程开发, Macbook pro…
  • 2015–2016: Ruby on Rails, OA, 光伏, Electric vehicle…
  • 2017: …

October 15, 2016 12:00 AM

October 09, 2016

Redguardtoo

purify quora.com with vanilla javascript

"Smart" http://quora.com always recommends the stories I hate to see.

So here is my way to toggle the stories display on Chrome and Firefox.

Step 1, create a new bookmark with below link,

javascript:a=Array.from(document.getElementsByClassName("AnswerStoryToggleModal"));a.forEach(function(e){e.style.display=a[a.length-1].style.display==='none'?'block':'none';});

Step 2, DONE! You only need click the bookmark to hide or show the stories when visiting http://quora.com.

Here is the original vanilla javascript,

var a = Array.from(document.getElementsByClassName("AnswerStoryToggleModal"));
a.forEach(function (e) {
    // check 'display' of the last item in story feed before toggling
    e.style.display = a[a.length - 1].style.display === 'none' ? 'block' : 'none';
});

Screenshot:

purify-quora-nq8.png

by Chen Bin at October 09, 2016 12:15 PM

September 20, 2016

Redguardtoo

山东韭菜猪肉虾仁水餃

1 原料

  • 韭菜一斤(8把)
  • 虾仁适量
  • 八角一个
  • 猪肉适量
  • 饺子皮90片

2 流程

  • 韭菜切碎
  • 虾仁切成小块
  • 猪肉冷冻后切丁
  • 葱姜切末
  • 八角磨成粉(或十三香或五香粉),可以用捣蒜的工具捣尽量碎
  • 以上材料混合,加鲜味酱油,加菜油(馅不干且有香味),加盐适量,可再加适量麻油
  • 包饺子时面皮涂点水,对折用两个大拇指用力压扁饺子边
  • 饺子底部粘生粉防止粘一起
  • 熟后可用蒜末和醋调味

馅:

dumpling-inside.jpg

开吃:

dumpling.jpg

by Chen Bin at September 20, 2016 05:46 AM

August 31, 2016

Redguardtoo

湖南菜攸县香干

用料:

  • 豆豉8粒
  • 蒜瓣4片
  • 生姜一片
  • 辣椒4根
  • 豆腐干6块

步骤:

  • 姜蒜切好,姜切沫,蒜切片
  • 蒜苗切斜段,青红椒各切圈
  • 香干切片,斜着下刀更好看
  • 锅中烧开水,水开后将香干片倒进去煮一分钟捞起控干水分备用
  • 锅里放油,立刻放适量豆豉(dou chi)姜蒜辣椒炒香,蒜蓉微红色即可进入下一步
  • 倒入香干,加生抽适量,老抽几滴上色,白糖少许提鲜,盐适量,耗油适量,最后放入蒜苗就起锅了

小结:

  • 3分钟可以炒完

you-xian-xiang-gan.jpg

by Chen Bin at August 31, 2016 10:02 AM

August 25, 2016

Redguardtoo

Emacs as C++ IDE, easy way

CREATED: <2016-08-25>

UPDATED: <2017-02-28 Tue>

This is a newbie friendly solution which works at Linux/OSX/Cygwin (should work at Windows too, but I don't develop at Windows).

Setup is minimum. Only GNU Global and two Emacs plugins are required:

Here is the step to step guide.

Step 1, create sample projects for experiment

I have two projects ~/proj1 and ~/proj2.

Both projects use files from read only directories /usr/include and /usr/src/linux/include.

We create a new directory ~/obj to store index files created by GNU Global because directories of third party libraries are read only.

Let's create directories,

mkdir -p ~/{proj1,proj2,obj}

The content of ~/proj2/lib.cpp,

void proj2_hello(int a2, char* b2) {
}

The content of ~/proj1/main.cpp,

void proj1_hello(int a1, char* b1) {
}

int main(int argc, char *argv[]) {
    return 0;
}

Step 2, scan C++ code and setup Emacs

Run below command in shell to scan code,

# dump index files to ~/obj if the project/library directories are read only
cd /usr/include && MAKEOBJDIRPREFIX=~/obj gtags 
cd /usr/linux/include && MAKEOBJDIRPREFIX=~/obj gtags 
... 
# dump index files inside the projects
cd ~/proj1 && gtags 
cd ~/proj2 && gtags
...

After installing Emacs plugins (minimum setup from their website is enough), insert below code into ~/.emacs,

;; Please note `file-truename' must be used!
(setenv "GTAGSLIBPATH" (concat "/usr/include"
                               ":"
                               "/usr/src/linux/include"
                               ":"
                               (file-truename "~/proj2")
                               ":"
                               (file-truename "~/proj1")))
(setenv "MAKEOBJDIRPREFIX" (file-truename "~/obj/"))
(setq company-backends '((company-dabbrev-code company-gtags)))

Usage

Use the Emacs plugins as usual.

But you need install latest company built on 25th August because I fixed a company issue yesterday.

Screenshot,

cpp-gtags-demo-nq8.png

Technical Details (Optional)

Check GNU Global manual to understand environment variables GTAGSLIBPATH and MAKEOBJDIRPREFIX.

by Chen Bin at August 25, 2016 02:20 PM

August 14, 2016

Redguardtoo

No worries when elpa is down

I use one liner shell command to clone Emacs Lisp Package Archive (ELPA):

mkdir -p ~/elpaclone && cd ~/elpaclone && curl -L https://elpa.gnu.org/packages/archive-contents | perl -pe 's/(^\(1|\n)//g' | perl -pe 's/\]\)/])\n/g' | perl -pe 's/^ *\(([a-z0-9A-Z-]*).*\[\(([0-9 ]*).*(single|tar).*/\1-\2.\3/g' | perl -pe 's/ /./g' | perl -pe 's/single/el/g' | perl -pe 's/\)//g' | xargs -I {} curl -L  -O https://elpa.gnu.org/packages/{} && curl -L -O https://elpa.gnu.org/packages/archive-contents

The https://elpa.gnu.org/packages/archive-contents contains all the information of packages. I re-organize it to make sure each line corresponds to one package. Then I use cURL to download everything.

Usage is simple.

Insert below line at the beginning of ~/.emacs when elpa.gnu.org is down:

(setq package-archives '(("elpaclone" . "~/elpaclone")))

This solution also works for MELPA.

by Chen Bin at August 14, 2016 12:52 PM