生如编程

周末跟5+聊了一回。

讨论到交通灯不科学的问题,我说:其实设计交通灯跟编程一样,都是很复杂的事情。
5+:编程有很复杂吗?
我: 看你编什么咯。

停下来想想,其实难点并不在于编程,而在于需要解决的问题。交通灯其实就是一个问题,可以用编程来解决罢了。至于说解决的是什么问题呢,其实无非都是一个生存的问题。

或者说,其实编程就像是活着,活着就像是在编程。

社会工程学LBS

最近被派到北京参加baidu的hackathon。晚上同学说一起出去转转吧,我说好,出去转转。

结果就迷路了。

结果就掏出手机开始导航,奇迹啊,几个宅男依靠一件电子产品就能走遍帝都了。好吧故事都不是这样讲的。真实的故事是,LBS确实可以告诉你大致的位置,从哪里来往哪里去该怎么坐公交车,但是他无法告诉你在一个巨大的广场里,怎么找到最近的一家快餐店,填饱空空如也的肚子。

于是我就叫住正好对面走来的一对母女,问了一下路。然后10分钟后几个宅男就在快餐店里大快朵颐了。

所谓“社会工程学LBS”么…回来的路上我们趁兴讨论了一番,“也许可以这样,搞一个问路按钮,然后服务器会给离你最近的几个用户发出请求,他们会在地图上点击一下,说明合适的位置,再发还给你,有点像平民窟的百万富翁里面的观众求助环节”。

“是的”,我回答,“给你求助的那个用户,其实就离你10米远,而你却用卫星信号绕地球7周半来接受这个答案”。

程序员怎么打电话

程序员打电话分三步:

第一步) 语音识别输入gtalk
第二步) 发送gtalk消息
第三步) 语音合成读出消息,声音指纹使用对应发送者的

完.

背景:这样打电话的兄弟俩通常距离不足十米,对方就在隔壁实验室。

程序员精进 – 自勉

今天读了这篇文章How to Interview an Engineer for your Startup,看了看人家大牛如何面试Engineer,学习了不少。

其实关于link shortener,本来也有思考过一些。不过确实没有像此文思考如此之细。尤其关于数据库优化方面是我软肋,更是无法了解如此之多。

也不仅单是这个case吧,也不仅单是服务器、数据库或者网站吧。总归一个好的Engineer应该随时把握着开发-性能-质量-产品周期,把事情做到最好。要不断历练自己。

复制原文红字自勉吧:
These days, people that can cross between frontend, backend and design are the true rockstars and exactly what we’re looking for, so I like to get a feel for whether they “just like to code requirements” or they can truly get proactive and help us build product.

做个工程师还有不少路要走。

一句话搜索

俺记得原先用google搜索东西的话,他的网络参数是这样子的:


http://www.google.com/?q=search+key+word

有阵子没用这玩意了,最近正好被人问起,发现这招不灵了。试了几下发现原来是这样:


http://www.google.com/#q=search+key+word

至于说不是地址栏输入直接就能search吗为何如此麻烦,嘿嘿你不知道我想干嘛。
注意,直接用wget去访问这样的link是没用的,需要修改UA让他觉得你是个浏览器才行。

[编辑]
感谢zhiqiang的提醒,是我记错了,正确的search命令应该是这样:


http://www.google.com/search?q=search+key+word

[/编辑]

新的革命:自由网络 vs. 安全网络

我起了一个巨大的标题。我本不该的。我希望用这样的标题党引起更多人注意这个问题,共同来解决这个世界性的世纪难题。

这篇bo的内容起始于我关于网络实名制的一些想法。但是不成熟,所以没有抛出来。很快就遇到的CSDN以及其他诸多著名网站密码流出事件,更是证明了网络安全问题其实是长久以来悬而未决的难题。后来美国又发起了SOAP的论战,当时俺兴趣不在这个上,也没有仔细去读新闻。过年回来后知后觉地追踪了下时下火热的方韩大战的新闻旧闻,说来这故事跟网络自由和安全其实有些相关性,那就是拷问网络言论究竟应不应该受到责任约束的问题。今天上午在公司无事,反过头来详细阅读了译言翻译的上许多对SOAP和PIPA的详细分析和报道。再反思以前一直思考着的,互联网未来发展的趋势;该不该实名制;用什么方式帮助大家公平地排队买火车票;怎样有效地分发和贩售知识,这种新世纪的财富;怎样有效地让人们在互联网上能够公平自由地发表言论,同时又能切实地为自己的言论负责;怎样保证信息流动性的同时还保证他的可靠性。因为一直思考着这些,发现这些问题其实源于同宗,于是就有了今天这个标题。

经过半个世纪的发展,互联网正走向新世纪,新革命。互联网极大便利人们的同时,也极大地改变,甚至可以说是,毁灭着人类。因为他的便利性地下藏着的是巨大的漏洞,无尽的陷阱。互联网最初的设计来自于学校,而目的是用于军事。也就是说,最早的互联网使用者,是科研机构和政府部门,是用于组织内部,是互相信任的不同机构的通讯网。互联网,从其设计之初的基础来看,就是一个信任网络。互联网是一个明码通信网络。当然,VPN,SSL等技术从不同层次改进了网络的安全性,但VPN并不是互联网的一部分,只是一个局域安全网,而SSL则会遇到密钥分发这个密码学永远要面对的难题,中间人攻击仍然是他的软肋。目前网络常用的用户名-密码验证机制,有着他的巨大漏洞。这绝不是说网站增强自己数据库的安全性就能解决的问题。举例而言,假如我是黑客,我完全可以建立一个用明码存储用户名密码的网站,然后用吸引眼球的东西引诱大家来注册。例如说糟糕图床,例如说二次元漫画,例如说盗版游戏下载,例如说灵异事件讨论和照片分享,等等。你会说我不沾游戏不看盗版电影不上糟糕站,那你会不会订打折机票,会不会团购旅游?恭喜你,你又中招了。黑客世界除了能逆向工程软件和系统,还有逆向社会工程学。你会说我每个网站都用不同的用户名不同的密码。但是这个世界有几个你这样的聪明人,十几亿网民中有千分之一的密码泄漏,就够养活一个师的黑客大军了。

强调绝对的网络自由的后果就是,网络不存在任何安全性可言。那就会陷入无政府主义的痛苦深渊。每个上网的人都必须是黑客,否则就是黑客押镖保护下的惊慌旅客,再不然就是待宰羔羊。网站安全要靠站长自身素质、纪律和技术支持;上网安全要靠网民自身武艺高超。那将会是一个骗子横行的世界。例如我可以开发一个720软件,密码存我这,包你上网无忧。结果实际我是地下黑客组织的大后台,我一边大肆兜售安全软件,一边就把所有人的私人信息插标出售。那你还敢信银行专用防火墙吗?那你还敢信全自动保安系统吗?那你还敢信网上银行吗?自动操盘bot?天哪没有网络安全的世界是不可想象的。

那么,反其道而行之呢?建立强有效的安全网络?问题是如何去做,问题是技术水平是否足够,问题是国际政治环境是否允许,问题是新的政策新的法律新的技术将如何影响现有的利益集团。

实名制:毫无疑问,实名制会给网络减少不少乐趣。用一个名字拉风的马甲胡吹海泡的时代一去不复返了。但是好处呢?每个人都要为自己说的话负责;或者仍然允许匿名,但是如果你用的是匿名,则你说的话根本没人搭理。谩骂、水军、造谣、煽动、这些毫无营养的网络垃圾终于可以省省了。更多的空间让给建设性的建议和评审,让给知识的有效流动。同时,实名制的信息签名制度使得一个人说的话在被复制和传播的时候他的签名被同时传播,于是制止了匿名转载,保证了CC实现。另外如果有一个强有力的独立部门承担网络ID的认证工作,则任何其他网站都不需要保存用户的密码了,于是再也没有密码泄漏问题。当然前提是那个独立认证部门必须切切实实的强有力,值得信赖。
技术难度:签名的不可复制性需要密码学的严谨论证;公民的网络ID将成为他的新一代身份证,同时也是他全部虚拟财产的钥匙,需要强有力的安全部门保证这个帐号的安全。

默认加密:现代互联网仍然是默认明文的。使用一些简易设施,任何人都能截取局域网或者wifi中传递的私人信息。加密应该在网络的底层实现,而不是依赖应用程序提供者的道德约束。问题是想要从底层修改网络协议,影响面太广,不是一日可完成的任务。并且这要求世界各国的网站齐心合力一同做到。新的IPV6协议中包含更多的安全部件,但仍然是可选项。强制安全的网络协议是未来人类的必然选择。
技术难度:向后兼容

异构云系统:这是我的独立发明。未来互联网就是云系统。但是现在的云系统都是同构的,同一个系统内部的无数并行运算组件其实是运行在同一个机房几乎完全相同硬件上的完全相同的软件。这也就是说,无论你的安全机制多么严密,只要黑客攻克了你那么一点点,那么所有的机器,所有的组件,全都被攻克了。我认为真正安全的系统必须是异构复合的。同样是处理邮件,同一个服务,在云端由不同的并行组件,就可能由成千上万种完全不同的方式处理掉(当然结果是相同的,异曲同工嘛)。这在自然界,就是称作“物种多样性”的杰作。这样黑客就算创造出一种病毒,也仅仅能摧毁某种特定类型的细胞,而其他的细胞完全不受影响。那种特定类型的细胞灭绝之后,这种病毒也就跟着一起灭绝了。要如何实现异构复合的云计算系统?毫无疑问要依赖遗传编程。啊伟大的人工智能,创造奇迹吧,改变软件工程的历史性大手笔!
技术难度:遗传编程的工程应用。于此相比,更大的难度在于安全性对于服务提供商而言是附加题,他们不会在没有利益驱动的前提下自动花大笔的精力去做。因此此事的推动需要等待互联网的进一步进步。

我写了好多,但还是太少了。但是很明显的,再多写也没有意义。推动网络革命需要的技术进步,远远比不上他所需要的社会进步那么急切。如果互联网不能进一步前进,进一步暴露他的问题,给各利益方以驱动,则即使技术摆在那里,也没有人想要去用。结果又变成了,技术拯救人类和技术毁灭人类的死亡竞速。

因此纸尽言犹,先就此收笔,静候网络发展的佳音吧。

又是命名法

命名法是个我不想再纠结的东西,不过最近又遇上了…
碰巧过完年,脑子锈掉了,居然读不懂自己年前写的代码了。我向来奉行“代码即注释”原则,也就是说…几乎不写注释…完全依靠函数和变量命名的含义来表达代码的意图和思路,除非真有复杂到需要注释的地方,例如莫名其妙的需要多线程同步的地方之外,其余一律不写注释。不过反作用就是,年前那阵子贪玩,没认真好好写代码,导致命名有点糟糕,过了年回来再读就有点绕人了…

一边改那些变量的名字,一边就遇到这个命名法的问题。我还在用匈牙利前缀,整数用n,浮点数用f,字符串用sz。现在需要自己增加几个自己的规则。
iterator类型,我原先用it做前缀,打出的代码看上去非常丑陋,例如:


for(std::vector<Phone>::iterator itPhone = phoneList.begin(), 
    itPhoneEnd = phoneList.end(); itPhone != itPhoneEnd; ++itPhone) {
  //...
}

单纯就觉得这两个字母放在前面不搭,不美…现在我用小写t做前缀,好看多了。


for(std::vector<Phone>::iterator tPhone = phoneList.begin(),
    tPhoneEnd = phoneList.end(); tPhone != tPhoneEnd; ++tPhone) {
  //...
}

当然了,这是古董代码了,有了foreach,这样的代码已经可以进垃圾堆了。不过难免用到iterator的时候俺就该记得用这个t前缀。还有就是项目里的代码我不想霰弹枪一起动,先这样放着比乱改安全很多…

受这个启发,typedef出来的变量类型用大写T前缀,例如


typedef std::vector<Phone> TPhoneList; 
for(TPhoneList::iterator tPhone = phoneList.begin(), 
    tPhoneEnd = phoneList.end(); tPhone != tPhoneEnd; ++tPhone) {
  //...
}

我喜欢typedef出几个项目中常用的容器类型。仅仅到容器类型,而iterator类型直接使用Type::iterator引用,而不进一步typedef。这样恰好保持代码的简洁程度适中,容易理解。

std::string类型用str前缀,而raw string类型(0结尾的字节序列)用sz类型,从而区分两者。这一点通常不重要,但是当你使用printf的时候:


std::string strName = "Peter Pan";
printf("hello %s\n", strName.c_str());

str前缀会提醒你记得用上c_str()方法。事实上这个问题惹恼我好几回了,最倒霉的一回是同事从北京打电话跟我联调,他那边输出的log出现乱码,而我这边丝毫没有,他通过IM给我看输出,告诉我printf打出来的。我不相信我的眼睛,好像就是变戏法。直到调了好久他才突然说,噢!忘记.c_str()了!
当然这仍旧不是好方法。真正问题出在printf不是一个类型安全的函数,编译器无法为你做编译时检查。但是类似printf, sprintf这类函数使用方便,即使最终产品不使用,调试阶段输出log还是会经常使用的。这个时候区分std::string和raw string就变得有意义了。

堆上空间使用p前缀,而栈上空间不使用。这里主要说的是数组。主要目的是当你使用p前缀的变量时候,就会提醒着自己记得思考该让谁delete掉他这个问题了。
例如


char szName[] = "Peter Pan";
char* pszName = new char[10];

我忽然在思考,是不是应该用q前缀来命名那些堆空间的拥有者,而用p前缀来命名那些弱引用。但如果是这样,那么不明拥有者,或者拥有者变更的情况怎么办?再引入一个ps前缀(p_shared_)?也许会弄得太复杂了。也许既然想了这么多,还不如一开始就用智能指针。为什么我不想引入智能指针?我不想引入复杂度。因此如果仅仅为了区区命名而导致复杂度增加,还不如一开始就简单化。所以还是所有指针都用p前缀。到底谁负责,自己好好想清楚。我想,引导读程序的人思考,比用带有暗示意味却可能是错误的命名要好得多。
在使用弱引用时,我经常使用的名字,例如pRead, pWrite, pBegin, pEnd, pCurrent, 等等,这些名字具有强烈的暗示性,说明他们是弱引用,正常人是不会去释放这样名称的变量的,更不会给这样的变量开辟空间。而另一些名字则比较危险,比如pNext, pChild, pParent, pSibling。这些名字的变量,可能你需要去释放他,可能你不需要(例如这是一个树状数组)。这个时候注释是必须的,否则很可能今天写的代码,明天就忽然觉得,不对,这里既然pNext被移动了,那么原来那个东西就应该先释放掉。然后Boom!程序崩溃。还好,现代程序员几乎不需要自己编写类似这样的基础数据结构,直接用酒精久经考验的标准库就行了。而且,就算偶尔碰上了,只要仔细调试通过,以后几乎不会再去改他了(毕竟最多的改动还是在上层嘛)。所以不算什么太大的麻烦。

呜,暂时想记录的就这么多了,给以后想要偷懒的自己看。