Categories

Calendar

November 2017
M T W T F S S
« Jun    
 12345
6789101112
13141516171819
20212223242526
27282930  

同义反复

忘记了之前在哪里看到说数学其实就是同义反复而已。从某种程度上来说,这样的言论也不能说完全是乱说,比如一系列的等价的推导,其实可以说就是在说同一件事情。但是“同义反复”多少有些贬义的意思,具体来说应该是指“不必要的反复”吧,但是数学应该不是这样吧。实际上,来考虑一下什么样的“反复”是“不必要的反复”就可以了,我觉得,那些只要是“不太明显” (non-obvious) 的关系,把这样的关系建立起来,应该也都是有其意义的,而尤其重要的是其中那些“深刻”的关联。

不过“深刻”这样的词是不是太抽象了呢?实际上,最近一年一来,接触了些数学专业的人,在同他们讨论问题的时候——好吧,其实大部分时候是我在听他们讲问题的时候——“深刻”这个词便时常在我脑子里出现。有时候听到他们说一些东西,会觉得很震惊,惊讶“原来如此”,惊讶自己从前的理解是如此的“肤浅”和不得要领。然而我一直想要来描述“深刻”这个词,却一直没有想法。也许应该举一个例子,不过有许多例子一时也想不起来了,也许有很多比较合适的经典的例子,比如 5 次以上方程不可用根式解之于 Galois Theory 的联系,我却又没法讲出来。

实际上大致就是那种感觉,不仅仅存在于数学,也存在于任何学科任何领域。从某一方面来说,世间的万事万物,作为一个个的独立的存在的话,并不是什么重要的存在,反而是它们之间的相互关联更加重要一些。所以,如果看到一个现象,得到的只是一些很明显的联系的话(也就是所谓的 obvious 的东西),也就是所谓肤浅了,这样用处大概并不大;但是如果是能抓住更深层次的东西(也就是 non-obvious 的东西),往往就能把问题看得更透彻——这样的带来的优势是可以比较形象地比喻的。比如说,各大洲上的生物有一些具有非常高的相似性,如果能顺着这个线索最终追查出原来曾经几块大陆是连在一起的,那么不仅为什么相差十万八千里的生物具有很高的相似性的问题变得豁然开朗,而且可以由此得到更多的结论来。

深刻,也就是抓住本质的东西,就好比照妖镜照出妖怪的本来面目。唐僧看不见妖怪的原形,所以妖怪用一些“烟雾弹”轻易就让唐僧上当受骗了;但是孙悟空有火眼金睛,却能看透妖怪的本质。这似乎让看透本质这样的能力越来越虚幻起来。但是神话毕竟只是神话,现实中没有人能有火眼金睛,但是看问题看得深刻——或者说,看到本质的东西,这样的能力却并不是遥不可及的。

久别重逢的 std::bad_alloc

久别重逢是说,自从在教科书上见过它一面之后,这才是第二次碰面。也就是说,在这些年的编程经历中,从来没有遇到过吧——至少在我印象中是这样的。以至于我都开始怀疑在“平常的”程序中,它是否真正存在了。内存分配,C 里的 malloc (或者配套的函数) ,如果分配失败了会返回地址 0 ,所以,“作为良好的编程习惯,每次申请内存之后,应该检查一下返回值是不是 NULL ”,这样的“良好习惯”也许刚开始写几个程序的时候还能坚持,到后来就完全不管了——因为从来没有遇到过 malloc 返回 0 的情况,申请内存怎么会失败呢?如果连内存都申请失败了,那接下去估计也没有什么好做的了,估计系统已经处于崩溃边缘了,与其每次都费力去检查,还不如让它自生自灭好了——反正之后如果尝试去访问这个 0 地址,肯定会碰到段错误 (segment fault) 而挂掉的,当然,一个不好的地方可能就是这个挂掉的位置和最初申请内存失败的位置已经相差了十万八千里,可能追踪起来会比较麻烦。

至于 C++ 里,就更简单了,new 的时候如果申请不到那么多内存的话,会抛出 std::bad_alloc 异常,如果没把这个异常接住,让它一直跳到最顶层的话,程序会立即挂掉。比 C 更加“人性化”——当场挂掉,而不是在某个未知的其他地方 segment fault 。如此一来,就更加熟视无睹了。

总而言之,渐渐地有了这样一种印象:像内存申请失败之类的情况,大概只有在嵌入式设备等非常极端的资源匮乏的平台上编程的时候才会碰到吧。结果这次却在一个内存很大很大(256G 物理内存)的环境里遇到了。果然是和车祸之类的类似,越是在看上去很太平的路段,越会让驾驶员掉以轻心呀。

CPU 风扇的问题

在上次讲调试的小课堂里我曾经提到过我前一阵子遇到过一个 Matlab 的问题,具体症状是,开启 Matlab 来做计算,过了大约一天以后,再去看,就会发现系统处于完全无响应的状态,完全挂掉了,键盘有一个灯在闪烁。

碰到了好几次之后我相当崩溃,这个 bug 几乎处于不可调试的状态,首先要重现 bug 就很麻烦,虽然几乎都能重新,但是我不可能一直坐在那里盯着屏幕等一天(具体需要几个小时来重现我也不太清除),而且 bug 出现之后系统会挂掉,唯一的办法是强制重启,这个时候所有现场都被销毁了。另一方面,阅读代码的必杀技也没法用,因为我的程序是一段不长的 Matlab 代码,在这样的脚本语言里,内存都是自动管理的,我能想到的危害最大的操作大概就是申请超大内存了,一般会有两个结局:如果 swap 空间还能承受得了的话,会使用 swap 空间,并导致系统奇慢无比,不过并没有挂掉;如果 swap 空间都不够用了,那么 Linux 系统会把这个进程 kill 掉,而不是同归于尽。总的来说,不太可能是这段 Matlab 脚本导致了这么严重的问题,如果真有问题的话,估计是出在 Matlab 了,并且还是非常强悍的 bug ,会让系统彻底挂掉。

Symlink with Directories

Linux 下的软链接 (英文通常称作 symbolic link, soft link 或者是 symlink) 是非常实用的东西,我想任何经常在 Linux 做事情的人都会时不时地会用到这个东西。相比起硬链接来说,它要更加灵活一些,限制也更少,而相对于 Windows 下的那种“快捷方式”来说,它又更加底层一些,处在文件系统层面,很多操作(例如打开文件 open )都会自动 follow 链接到指定的文件,因此它对大部分应用程序可以说是完全透明的。虽然如此,也有一些特殊的地方对于把 symlink 当作“自己本身”来处理还是当作“它所指向的源文件”来处理却并没有一个定论的。

for vs. each

虽然这个问题我是在 Python 里遇到的,但是用 Ruby 解释起来比较容易一些。在 Ruby 里,遍历一个数组可以有很多种方法,最常用的两种无非是 for 和 each:

arr = [’a’, ‘b’, ‘c’]
 
arr.each { |e|
puts e
}
 
for e in arr
puts e
end

通常我比较喜欢后者,似乎因为写起来比较好看,不过从效率上来说前者应该会稍微快一点,因为后者实际上是在遍历的过程中对每个元素都调用一个 lambda 函数来做的,虽然一般情况下并不明显,不过设置上下文并调用函数确实是有开销的,特别是在动态语言里面(不考虑 JIT 内联优化的话)。不过这次的问题并不是性能。然而确实跟“ each 对每个元素都会新建一个 scope 而 for 则不是”有关。

(\d+|\d*\.\d+)

我在之前的 blog 上专门有一个分类叫做 Bug Archive ,用于记录自己平时碰到的应该记录下来的 bug ,现在我觉得这个分类大概还得继续下去吧。因为记录的目的是希望自己以后不要再犯同样的错误,所以通常实际记录下来的其实也都是非常低级的 bug 了。毕竟复杂的 bug 一方面本身也比较难重现,再者遇到了也不是很羞愧的事情。

但是低级的 bug 就不一样了,低级的 bug 并不是指它本身很低级,而是对于你来说不应该会犯的错误——比如,在你最熟悉的地方出现问题。就像今天要说的这个 bug 一样。我想,如果你熟悉正则表达式,大概在打开本文之前就看出来标题里的正则表达式有问题了;如果一开始没有看出来,现在我告诉你它有问题,你再去看一眼,应该也知道问题的所在了。当然,如果你对正则表达式不熟悉,那也只能说这个问题对你来说不是低级的 bug 。

GoDaddy: No Number in Name!

I spent the whole morning struggling with GoDaddy’s payment page. I bought my domain name from GoDaddy. All of my friends suggest it. Maybe because it’s DNS service is stable? Or just all others sucks more? But one thing I have to say is that the website of GoDaddy is really very very bad. It’s […]