Nested Array in Bash

用了Bash这么久,才知道Bash支持Array。但是却缺乏对嵌套数组,或者是多维数组的支持。自己的实验里面需要用到结构性的数据,这样看起来或者改起来会比较方便,而且因为这部分是用来处理实验结果,需要经常修改,所以不适合放到C++里面去写。 因此就有了需要Bash支持嵌套数组的需求。 最终的解决方法不是很漂亮,但是也足够我自己用了。这个想法的出发点是这样的: Bash在处理数组的时候会用到IFS这个环境变量。比如这样一段字符 Li,Age*1;Weight*2;Height*3;Friends*Sun^Wang Wang,Age*11;Weight*12;Height*13;Friends*Li^Sun 如果IFS是空格,我们就可以得到两个元素 Li,Age*1;Weight*2;Height*3;Friends*Sun^Wang Wang,Age*11;Weight*12;Height*13;Friends*Li^Sun 如果IFS是分号(;),我们可以得到另一个数组 Li,Age*1 Weight*2 Height*3 Friends*Sun^Wang Wang,Age*11 Weight*12 Height*13 Friends*Li^Sun 也就是说我们可以通过指定不同的IFS让一段字符成为不同的数组。 所以,我们可以通过对每一层使用不同的IFS来表达一个嵌套数组。比如上面那段字符,我们用不同的IFS字符,先用空格( ),再用逗号(,),之后用分号(;)…… 如此做下去就可以达到层层盘剥的效果(-_-!) 在实际使用中,我们只要保证每一层用到的IFS不会在数组内容中出现就行了。 为了能够生成那样一段字符,我们自然需要一些函数做辅助。 具体的代码放到Github上了。目前这个方法虽然有效,但是使用起来仍然不够简洁,看起来如果找不到更好的Bash下的解决方法,就得考虑换用一种20世纪的脚本语言了… Github: Nested-Array-Bash

最近

已经好久没有写东西了,三言两语也是一种存在。前两天把文章投出去,这个学期也快要结束了。 真是充实的一个学期,短短不到三个月的时间里:mm过来,生活状态上带来了不小的变化,体重上来好几斤;第一篇文章被接收,总算有了Publication,感觉挺欣慰;之后是自己的PhD资格考试,严肃地讲了一次自己的工作;接着接着就是做新的文章;最后就是还没有结束的TA,第一次做助教,给小本上复习课感觉压力山大。 之后做什么呢?要做新的东西,要去学开车,暑假要去实习。很是期待暑假的实习,可以见见同学,可以看看另外一种生活状态。埋着头,慢慢走吧。忽然很想听崔健。

rand函数不可重入

写C代码的时候,srand(int seed) 和 rand() 是常用的伪随机数生成函数。 这两个函数的使用方法很简单,但是一个可能被忽略的细节是,rand() 依赖一个内部的、全局的状态变量。所以 rand() 是不可重入,也是不是线程安全的 (thread-safe) 。 如果多个线程同时调用 rand() 函数,那么无论你如何使用 srand(int seed) 都无法保证结果是可以重现的。每次运行程序,各个线程中 rand() 函数生成的伪随机数序列都和上次不同。 在调试的时候,不能重现的结果会是比较棘手的障碍。 幸好,我们可以用C++11 提供的伪随机数生成器 Pseudo-random number generation (这么翻译好机械-_-!)用法很容易在网络上找到,这里有一个最简单的例子。 #include //….. { std::default_random_engine gen(0); int a_random_number = gen(); } default_random_engine维护自己的内部状态,各个线程都用同样的参数初始化default_random_engine,就可以得到一致的伪随机数序列了。

Tips 2013-02-17

如果有需要把工程从Linux下面移植一份到Windows下,却又不熟悉Visual Studio的各种配置,那么用CMake来管理工程是一个不错的选择。从Makefile改写CMakeLists.txt并不麻烦,而且CMake可以自动生成VS的工程文件,很好用。 用 floor 和 ceil 这类函数最好先对参数做显示的类型转换,否则VS会报错。 isinf 和 isnan 能不能则不用,VS没有现成好用的对应版本。 关于把一份Git工程同步到SVN的版本库里去的方法,网上讨论的很多,比如这个: http://stackoverflow.com/questions/661018/pushing-an-existing-git-repository-to-svn 但是如果你的Git的工程已经有了悠久历史,那么在rebase那一步你可能会有非常多的conflicts要处理。一个小办法是每次出现了冲突都用这一行代码: $git checkout . –theirs && git add . && git rebase –continue 当然前提是当前目录下面没有不在git管理下的其他文件。这个虽然不解决根本问题,但是会方便很多。 总是有想要重构自己实验工程的想法,看起来工程的框架还是不太好。我总觉得好的框架应该是很容易修改的,现在每次想要往工程里新加一组实验就觉得有些代码碍手碍脚。如果不想有重复代码函数粒度就太小,而且接口复杂不可读,时间一长就看不出它们都是干什么的了。否则就有大段的重复代码,十分难看。如果把实验操作部分放到Bash脚本里面,移植又是一个问题。很少看到讲这种不大的工程怎么去搭框架的问题,苦于自己技术水平不够,想想真是头疼…

比较变量地址并不可靠

最近云风大牛又在黑我C++,可是在我学会之前,我还是要坚定不移地待在这贼船上。 嘿嘿 :] “用比较地址的方法来判断两个变量(的引用)是不是同一个变量是不可靠的”,这个问题很简单,却也容易忽视。 现实的情况是这样的。在写Computer Vision实验的时候,因为程序要面临的计算量往往很大,对程序的性能的优化是十分重要的。 所以我就写出了这样的代码: void foo_func(Foo& foo) { //< 如果foo和上次传入的是同一个,就省掉一部分重复计算 static Foo *p_foo = NULL; if (p_foo != &foo) { p_foo = &foo; //< 大量计算 } //< 后续计算 } 在实验里,Foo这个类本身往往比较复杂,不可能做内容的逐一比较,同时这个类的实例在初始化之后内容就保持不变。写这个函数的时候没有觉得有什么问题,结果也是正确的。 但是,这么做显然是不可靠的。 这段代码就说明问题了: #include int main(int argc, char **argv) { using namespace std; for (int i = 0; i < 100; i++) { […]

Mac远程桌面到Linux服务器

假期结束,回到学校开始干活 :] 为了远程使用Linux服务器,折腾了一个下午。最终看来还是用vnc最简单了。 实验室有两台强劲的Linux服务器用来做研究。之前我一直都是用ssh登到服务器上去码代码,反应速度很快,感觉很不错。但是因为在做机器视觉,难免需要看远程的图片。命令行虽然快,总不能每次都把图片拷贝到本地再看,有时候需要可视化中间结果,ssh也行不通。 当本地机器是Linux系统的时候比较好办。可以用ssh加X forwarding的方法。在本地开一个X,然后把远程服务器的X指令通过ssh转发到本地的X,码代码没有什么延迟,感觉还是很不错的。 sudo X :11 vt11 2>&1 >/dev/null & 这样可以在本地新开一个X,Ubuntu下用Ctrl+Alt+F11可以切到第11个虚拟终端 回到之前的终端,开ssh和xterm xterm -display :11 -e ssh -X server-host & 然后可以切换到第11个虚拟终端来使用远程Linux服务器上的X了。 这样虽然好,但是要求本地机器上有安装X。在Windows和MacOS下虽然有解决方法,但是比较麻烦。 用VNC的话就没有这个问题,毕竟VNC的客户端是很容易找的。 当然需要先ssh登录到Linux服务器上安装vncserver sudo apt-get install vnc4server 然后启动vncserver vncserver 这样就搞定了。 在本地的Mac下可以用自带的Screen Sharing App或者著名的Chicken of the VNC连接到server-host:5901来查看和控制远程Linux桌面。 在服务器上启动了vncserver之后,可以通过修改 ~/.vnc/xstartup 这个文件,来指定远程的X启动之后要执行什么命令。我喜欢用openbox,所以我的xstartup文件就是这样子 #!/bin/sh # Uncomment the following two lines for normal desktop: # unset […]

dup, pipe 和 fork

(好久没更新了,呼…一大波死线刚刚结束…) 我几乎一直在用Bash,可是却少有接触到Unix系的系统编程,对系统调用还是知之甚少。这两天实验室里讨论了一个比较基础的问题: 在自己写的程序中,怎么样得到另一个可执行文件的输出? 比如我们有/bin/pwd这个可执行文件,我们可以在自己的程序中用 system(“/bin/pwd”); 或者是 execl(“/bin/pwd”,”.”,NULL); 调用它。 如果想要得到它的输出,应该怎么做比较好? 这个其实是作业题的范畴,涉及到了几个Unix的系统调用: #include pid_t fork(void); #include int pipe(int fildes[2]); #include int dup(int fildes); int dup2(int fildes, int fildes2); 手册里有各个函数的详细解释。 在这个例子里,pipe用来生成一对文件描述子 (file descriptor,我觉得描述子这个翻译不是很好…),往第二个描述子里写内容,从第一个里面读内容。fork用来得到一个子进程,这里,我们会让子进程来执行pwd,并且把输出写到pipe的一端,然后父进程可以从另一端读入。 但是pwd默认输出到屏幕,也就是标准输出,我们需要使用dup2,把标准输出指向pipe的一端,这样就可以完成任务了。 所以,最终的代码是这样的 #include #include #include #include int main(int argc, char **argv) { int fd[2]; int pid; pipe(fd); int rpipe = fd[0]; int wpipe = fd[1]; […]