一周笔记

(How to Write a (Lisp) Interpreter (in Python))

老文章。来自Peter Norvig 的用 Python 写个 lisp 解释器。代码和解释都很完美。记得有公司出面试题出类似的,如果用这文章里的思路解决就秒杀啊。

DNS The good parts

比较详细的介绍 DNS 的文章

2016年科技阅读列

一系列架构和大数据文章的集合,质量良莠不齐,偶尔也有好玩的。

高并发性能调试经验分享

最喜欢看这种调试的文章了,特别是这篇『在多线程和高并发环境下,如果有一个平均运行一百万次才出现一次的bug,你如何调试这个bug』,连思路带工具一路娓娓道来,非常值得一读。

Penetration Testing Tools Cheat Sheet

渗透工具 cheatsheet,初入安全的朋友可能会用得到。

“The Best Programming Advice I Ever Got” with Rob Pike

Rob Pike 谈论 Ken Thompson 给他的编程建议:

Ken taught me that thinking before debugging is extremely important. If you dive into the bug, you tend to fix the local issue in the code, but if you think about the bug first, how the bug came to be, you often find and correct a higher-level problem in the code that will improve the design and prevent further bugs.

The twelve-factor app

从 Two Scoop of Django 上看来,有点类似写 app 的 best practices.

The Product Managers’ Guide to Continuous Delivery and DevOps

简单介绍什么是 Continuous Delivery 和 DevOps。

ResysChina

着重推荐算法文章的高质量知乎专栏

Real–world HTTP/2: 400gb of images per day

也是 debug 类的好网站。一个图片分享网站讲他们迁移到 HTTP/2 的性能提升和坑。


一周笔记

The Software Development Poverty Trap

软件开发中的马太效应,越是差劲的团队越缺乏长期规划,用老旧的技术,写 ad-hoc 的代码,优秀的工程师越容易离开(斜眼看我司)。

“把程序写好”这回事

来此余晟的微信公共号(对,就是翻译正则表达式的那哥们)。很多程序员在学校和工作中编程,但不知道如何写好程序,貌似也没有学校教如何『写好程序』。写好程序绝不是编译通过,跑过测试,符合 coding style 那么简单。文中提到的『荣誉感』还是挺重要的,我写代码的时候就会想着对自己的每一行代码负责,通过 code review 看别人的代码质量也会潜移默化影响我对这个人的评价,和对待他的态度。

What every programmer should know about memory

挺长的,从偏硬件的角度讲解内存的原理。前面几章在各个 OS 教科书上都能找到,后面的内容就比较贴近现实,看着很有意思。

The Little Manual of API Design

记得 GoogleTechTalk 里著名的一集 How To Design A Good API and Why it Matters 么,这个 manual 可以看作那个 talk 的扩展读物。API Design 在程序设计中的重要性相比于架构不遑多让,看看这些 best practices 有益身心~~

What Powers Instagram: Hundreds of Instances, Dozens of Technologies

一篇老文章,介绍 Instagram 早期时候(2012)的架构。我最喜欢这种早期架构的文章,看别人在高速增长期如何用快糙猛的方法解决问题。里面给的小部分技术在今天看来有些过时了,但大部分还很有借鉴意义。比如提到的 gunicorn 和 Fabric,是 Python web 开发中的标配(还有 supervisor);vmtouch (查了下发现是一个超级light weight的内存数据管理工具,代码也写的很棒);Munin,Pingdom 监控,Sentry 报告错误。


一周笔记

第二周

这周 RSS 里没啥非常好的文章,就把以前的笔记里翻出来几篇凑数。

Elevate yourself with side projects

爱因斯坦的在专利局工作时候的 side project 是相对论论文,学校门卫波洛克(对,现代艺术甩泥点子那位)的 side project 是画画,Slack 一开始也是游戏公司的 side project 聊天工具。Side project 能真正有效促进一个人的专业进步(深有同感)。不是有句话说么,这年头在湾区没有一两个 side project 都不好意跟人打招呼。

What every computer science major should know

压箱底的老文章。 知名博主 Matt Might的一篇长文,很详细的列出了一个合格的 CS 学生应该学习和掌握的知识,任何一个学 CS 的学生都应该看看这篇文章。

Peer Code Reviews At Loose Cannon

技术博客大多谈的是如何学习新技术,如何设计架构,如何找工作,却很少谈如何正确的 Code Review 的。Code Review 在工作中非常重要,是有效的学习/分享知识,增进办公室人际关系的途径。这一系列文章就很详细的谈了如何 Code Review。附赠这篇 Code Review Best Practices

香草冰淇淋,世界末日与烧鹿骨

这是一篇我很喜欢的非技术文章。从顾客离奇的投诉『新车会对香草冰淇淋过敏』说起,分析了一些显式因果关系和其背后看似荒诞的理性依据。原文链接已经失效,给的链接是别的网站转的。


一周笔记

什么是一周笔记

这是经女朋友提醒,准备每周一篇,阅读的技术博客,文章等的简短评论,干货收集,读书感想及各种好玩的事儿。

模仿 O’Reilly 风格插图

一直觉得O’Reilly书系的示意图简洁美观,希望写博客或者presentation的时候也能用,今天花时间查了下,字体用的是 Myriad,编程字体用的是 Ubuntu Mono,用 Keynote 画了几个简单的图形,供日后使用。

Questions to ask your interviewer

每次面试时候最后面试官都会问 “Do you have any questions?”,然后大多数人都只是问一些隔靴搔痒的问题,这篇文章比较深入的探讨这个问题,怎样让问面试官问题成为你的加分点,哪些问题能反映出这个公司的开发流程和工程师文化(关系到是否值得加入),面试必备的好问 (Victoria 同学翻译成了中文)。

Cron best practices

Cron通常用来每隔一段时间跑一个脚本,但大多数人都只是用来检查重启服务器或者备份。本文介绍了一些 Cron 的高级玩法,输出错误码,发送邮件,设置 timeout,等等。BTW,这个作者还有一些 Linux Crypto 的文章,也很值得一看。

Choosing a Linux Tracer

介绍了一堆Performance monitoring & tunning 的工具,来自 Netflix senior performance architect。我查 systemtap 的时候翻到这哥们的博客,发现此人文章很棒,然后和我们司的performance engineer 聊文章中的问题,发现这博主是他当年在 Sun 的同事 … …

Latency Numbers Every Programmer Should Know

这个介绍不同存储器性能的表格历史悠久,这个 gist 及其评论里很多人贡献了他们知道的相关的文档和视频,很多都很有意思。一些常用的还是很有必要背下来的(比如 CPU, 内存,硬盘, SSD 读取速度的倍数关系),对于初学编程的人能更好的了解 locality 的重要性。

保证分布式系统数据一致性的6种方案

介绍了一些国内的案例,文末的链接也挺好的。

浅谈 WHR 全历史排名

云风的文章和他的代码一样,一向以信噪比高著称。这是一篇谈博弈中的打分算法问题的,文中谈到elo就是电影《社交网络》中 Eduardo 在窗户上写的公式。


2016 年计划

2016 新年计划

一如既往的,新年是要计划的,实不实现再两说。

2015 的计划回顾

  • 第一条计划算实现了四成,八九月份找到工作了。
  • 第二条读书计划,毫无疑问轻松达成。
  • 第三条写博客计划,额,可耻地没做到。
  • 第四条掌握 Haskell 计划,算是搞定了。
  • 第五条计划,今年继续~~

今年的计划

  • 写一个有分量的开源项目,或者写个库放上 pypi 之类的。
  • 学一门手艺,摄影,跑酷,弹贝斯,篆刻或者其他什么的。
  • 健身。因为练的无氧,这不好量化。以后再加具体的目标。
  • 读书就不列了,一直没撂下。写博客,一周至少一篇。
  • 继续找女朋友~

找出编辑距离不大于 K 的单词

关于 edit distance 的一道题

最近看到一个很有意思的面试题:给一个单词和一个字典,找出字典中所有和给定单词编辑距离不大于 k 的词。

一个常见的思路是遍历一遍,求单词和字典中每一项的编辑距离。我们知道编辑距离是二维 DP,时间复杂度为 $O(L^2)$,其中 L 为每个单词平均长度,则总时间复杂度为$O(NL^2)$, N 为字典中词的个数。

这个方法的问题在于,一旦查询单词变多,性能会很糟糕。基于知乎 Lee Shellay的回答,可以通过构造 Trie, 结合 DFS,来解决这个问题。

所以算法思路并不难:

  1. 根据字典中的单词构造前缀树,标记每个单词结束时的结束符为 ’$’。
  2. 设计函数 API 为check_fuzzy(trie, word, path, tol)trie是在树中当前走到的节点,word 表示走到当前节点剩余需要处理的查询单词,path表示走到当前节点已经记录的字典单词前缀,tol 表示剩余可容忍的编辑距离。然后定义一个set,不断找到可能的单词并入这个set,直到结束。
    所以,函数只在tol 为0时候终止(为什么不是word为空时候终止?因为有可用的编辑距离都用在增加后缀的情况)。
  • 匹配当前字符,有两种情况:匹配,那么直接递归下一层;不匹配,可能是字母不一致或者是 word 已经结束(这个情况很容易被忽略),需要 tol 减一后递归下一层。
  • 增加任意字母(字典单词比查询单词多字母)。这里和知乎回答里的不一样,那里是枚举了26个字母,其实只要枚举当前 tree 的所有节点字母就行了(Jayxon 大牛想到的)。
  • 删除字符。word 向后移一个字母,tol 减一。

最后代码如下:

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
# Based on Lee Shellay's code http://www.zhihu.com/question/29592463

END = '$'
def make_trie(words):
trie = {}
for word in words:
t = trie
for c in word:
if c not in t:
t[c] = {}
t = t[c]
t[END] = {}
return trie

def check_fuzzy_v4(trie, word, path = '', tol = 1):
if tol < 0:
return set()

ps = set()
if word == '':
if END in trie:
ps = {path}

for k in trie:
# match current or mark as substition
ps |= check_fuzzy_v4(trie[k], word[1:], path+k, tol - (not word or k != word[0]))
# add random char
ps |= check_fuzzy_v4(trie[k], word, path+k, tol-1)

# delete one (if word is empty, word[2:] will not report error)
ps |= check_fuzzy_v4(trie, word[1:], path, tol-1)
return ps

if __name__ == '__main__':
words = ['hello', 'hela', 'hel', 'dokm', 'i', 'ke', 'ik']
t = make_trie(words)
print check_fuzzy_v4(t, 'helo','', tol=2)

然后试试大一点的数据。我们知道在/usr/share/dict/words存着拼写检查的单词表,一共 2.4M 共 235886个单词(至少在我的 Mac 上是这么多)。可以用它来构造字典 cat /usr/share/dict/words > ./words.txt。然后把一句话改的乱七八糟,用代码来跑跑试试:

1
2
3
4
5
6
7
8
def test():
origin = "For you know only a heap of broken images"
modified = "Far your knn onlie a deep of borken iimaes"

words_list = [line.strip() for line in open('words.txt', 'r')]
tree = make_trie(words_list)
for w in modified.split():
print check_fuzzy_v4(tree, w, tol=2)

结果也挺快的:

  • CPython: 2.53s user 0.25s system 50% cpu 5.470 total
  • Pypy: 1.63s user 0.19s system 43% cpu 4.186 total

就是这样, 喵~

PS: Lee Shellay回答又更新了,提升了性能和准确度,代码比我这的好,欢迎去看。


Producer consumer problem in coroutine

What is coroutine

As the name implies, coroutine refers to co-operative routine. It allows you to suspending and resuming execution at different locations. So, it’s essentially just context switching. Not surprisingly, coroutine is implemented in primitives like setjmp/longjump or ucontext in low level.

In many senarioes, coroutine is a more light-weight alternative of threads. For programming languages with GIL (like Python), coroutine would used to handle concurrency.

Producer and consumer problem

Let’s take a look at classic “producer-consumer” problem. At each time, one coroutine produce products and add them into queue, the other coroutine take products from queue and use it (hmm, sounds like video buffering, right?).

The code below assumes you already have some knowledge of generator.

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
import time
import random

def coroutine(func):
# A wrapper to convert function into generator
# From David Beazley
def start(*args,**kwargs):
cr = func(*args,**kwargs)
cr.next()
return cr
return start

def producer(target):
while True:
time.sleep(1)
data = random.randint(1, 10)
print ("# producer: sending data {}".format(data))
target.send(data)

@coroutine
def consumer():
while True:
data = yield
print ("# consumer: receving data {}".format(data))

if __name__ == '__main__':
g_consumer = consumer()
producer(g_consumer)

Simple enough, send() is a built-in function of generator. The producer send data to consumer, consumer receives data from yield.

Coroutine usage

Yes, the famous concurrency library gevent is based on coroutine.

PEP 342: Coroutines via Enhanced Generators

一个“蝇量级” C 语言协程库

General concepts: concurrency, parallelism, threads and processes

A Curious Course on Coroutines and Concurrency


Colorizing Loggers -- A simple practice of decorator

What is this post about

Many people believe that decorator is one of the obscure concepts in Python.
Trust me, it is not. To be short, a decorator is a function that modifies other
functions via closures.

They are plenty detailed articles about what decorator it is so there is no need to write one more. If you are not familiar with it, you may want to check these:

In this article, I am going to use a simple but interesting example to show

  • how to colorize the output in command line
  • how to implement a switch case in Python (Python does not have built-in switch case)
  • a decorator with parameters

OK, let’s rock.

colorize your text

There is a set of escape sequences used to change the color of texts. So if we want to colorize a sentence, we just need to put the sentence between the color escape sequence and reset escape sequence. For example:

1
2
3
4
5
6
7
>> ORANGE = '\033[33m'
>> RED = '\033[31m'
>> GREEN = '\033[32m'
>> BLUE = '\033[34m'
>> RESET = '\033[0m'
>> print ORANGE + "Chinese New Year" + RESET
>> print GREEN + "Chinese" + GREEN + "New" + BLUE + "Year" + RESET

You will see
Chinese New Year

and
Chinese New Year

How to choose the color in code

As I said, Python does not support switch case. So we cannot switch the color name and choose the corresponding escaped sequence. Fortunately, dictionary would do the work.

1
2
3
4
5
6
7
8
9
10
11
def getColor(color):
# @param color: string like "red" or "yellow"
# @return the corresponding escape sequence.
# If not valid, return empty string
return {
RED = '\033[31m',
GREEN = '\033[32m',
BLUE = '\033[34m',
# and more ...
ORANGE = '\033[33m'
}.get(color, "")

The trick is, the dictionary’s built in get method. The first parameter here is key, the second optional parameter is default. As the docstring shows:

D.get(k[,d]) -> D[k] if k in D, else d. d defaults to None.

Use the decorator

Suppose we have a function to implement a task. It may has three kind of return string, if task is completed successfully, it returns “SUCCESS: blah blah …”, if the task is finished but we cannot ensure it’s corretness, it returns “WARNING: blah blah …”, if task failed, returns “ERROR: blah blah …”, how do we colorize these return strings?

Raw code

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
def colorize(*args):
def getColor(color):
return {
'black' : '\033[30m',
'red' : '\033[31m',
'green' : '\033[32m',
'orange' : '\033[33m',
'blue' : '\033[34m',
'purple' : '\033[35m',
'cyan' : '\033[36m',
'light_grey' : '\033[37m'
}.get(color, "")
def _colorize():
def wrapper():
RESET = '\033[0m'
text = func()
#if not isinstance(text, basestring):
# text = str(text)
level = text.split(':', 1)[0]
color = {
'SUCCESS': args[0],
'WARNING': args[1],
'ERROR': args[2]
}.get(level, '')
return "{0}{1}{2}".format(getColor(color), text, RESET)
return wrapper
return _colorize

@colorize("green", "orange", "red")
def do_task():
# working working ....
if (success):
return "SUCCESS: Yeah~~"
elif (warning):
return "WARNING: wait, what?"
else:
return "ERROR: something went wrong here."

As you can tell, to make the decorator with parameters, we need to put it in another decorator.

Decorator are often used as cache, profiler, logger, synchronization(acquire lock, drop lock) and so forth. One of my favourite library Click is also a wonderful example.

Happy Chinese New Year ~


How to delete nodes of linked list with pointers-to-pointers (Chinese)

我在用C++写 Leetcode中 Remove duplicates from linked list II 一题时,看到别人的一份代码,感觉写法很有趣,细细研究了一下。受益不少。

在大部分链表题中,我们习惯于创建一个空节点dummy,使之指向链表的头结点,以方便对
第一个节点进行操作(比如,删除它)。最后答案返回dummy.next。比较有节操的同学会在
删除链表的某些节点时用delete,以免内存泄露,但是难道就没有考虑过dummy节点感受么?

使用一个二维指针,可以优雅的解决了这个问题。

举个简单的🌰:

1
2
3
4
5
6
7
8
9
10
11
/*
* Suppose we have a linked list "1->2->3", we want to delete the
* second node, remains "1->3".
*/

ListNode **p = &head, *succ;
p = &(*p)->next;
succ = (*p)->next;
delete (*p);
(*p) = succ;

尝试解释一下下。p是一个二级指针,也就是说,在一开始,p是一个指向一个指向head指针的指针(也就是(*p)指向head)。这样的好处就是,当我们需要在某一个时刻删除指向的节点(delete *p操作),p本身不受影响(当然,是p指向的指针所对应的内存空间被释放了)。唯一一点不方便的时,其他每次移动的时候,都要用(*p)(p淡淡的看着他指向的指针往后移。)

和用dummy解法不同的是,dummy解法指针后移是ptr = ptr->next;。那我们这呢?(*p) = (*p)->next;?这样是错的。比如1->2里从1移动到2的过程中,就把节点1修改了。所以,要移动的是p。即为p = &(*p)->next,其中->的优先级是高于&的,把p赋值为(*p)->的地址,所以现在(*p)指向老(*p)的next。

另一个大家可能关心的问题,在delete (*p)后,(*p)的前驱节点的next是怎么不找丢的呀?这其实涉及到delete的本质(Stackoverflow对这个问题有个不错的回答)。当我们调用delete的时候,那块内存里的数据其实并没有消失,只是这块内存地址被标记为可以利用,当之后的程序需要new的时候,才有可能覆盖掉这里的数据。就像爱情,没有一段新的覆盖,老的怎么忘的掉(情人节了还在改博客,唉~~)。所以这个代码严格意义上说是由风险的,如果在delete的一瞬间,正好另一个程序/进程new了一块内存,又刚好是这里,这个方法就废了。fix的方法就是delete前,赋给一个临时变量,把next覆盖当前,再delete临时变量。

修改的过程中,发现陈皓也写过类似的文章,这个trick被Linus举例为什么才是core low-level coding,真正懂指针的做法。 他的文章还有配图,如果我表述的还是没让大家理解,推荐去读一下。

PS:《Pointers In C》的第十二章《Using Structures and Pointers》,也有关于指针链表操作的详细解释。


我为什么要编程

王小波和乔治奥威尔都曾以《我为什么要写作》为题写过他们的走上作家之路原因。对于这个问题,王小波借用了一位登山家的回答,“因为山在那儿。” (如你所知,那位登山家最后也确实在珠峰上挂掉了。)乔治奥威尔的回答则显得更有条理:纯粹的个人主义,美学热枕,历史责任感和政治目的。

你看,程序员界就很少有这么高的境界,去中关村随便抓个程序员,问他为什么编程,他就绝不会回答 “为公司立心,为开源立命,为community开太平”。当然,也有一些身怀抱负的有为青年(比如我),会不时纠结于这个问题。比如最近,我就为自己的志大才疏感到无比痛苦。成功学那些骗小孩儿的早就免疫了,得靠一些真实的数据来打鸡血。

首先,优秀的技术大牛是什么样的?我把我RSS订阅里的中文技术博客博主的信息都翻了一遍,统计出了一些有趣的东西:

  • 2/3的大牛来自top20的学校,他们中不少人在读PhD。
  • 至少一半的人大学前就开始接触编程。
  • 15% 左右的人在海外留学或者工作,基本全部来自第一项的top20学校中。
  • 在国内工作的中有四个都是在阿里。
  • 貌似全部都用Mac。
  • 30% 以上的人有优异的数学(参加过中学数学竞赛等)或者算法(ACM, OI)背景。
  • 大部分人都有相当不错的文字功底,写作能力和阅读量都不赖。
  • 他们大多数至少在国外技术博文翻译、Linux及开源、函数式编程、算法这四项中沾一样(这项可能是我口味问题,订阅有偏向性)。
  • 30岁以上的人中,有不少是非计算机专业的。30岁以下的,基本都是CS科班出身。

所以,我还不是完全没有机会的。我之所以喜欢这一行,就是因为这一行的聪明人太多了,勤奋的人太多了,这让我很有压力,这种压力很爽。

正如莱蒙托夫在的那首小诗《帆》:

在大海的深蓝色的云雾里,
一只孤独的帆儿闪着白光。
它在寻求什么,在那遥远的异地?
它抛下了什么,在那自己的故乡?
波涛在汹涌着,海风在呼啸着,
桅杆弓起腰来发出扎扎的声响。
不,它不是在寻求幸福,
它也不是在逃避幸福!
它下面是碧色的澄清的水流,
它上面是金色的太阳,
而它,不安地,在祈求着风暴,
仿佛是在风暴中才有安详。