写统计软件的两个常见错误

Two common mistakes when writing statistics software

最近在写一个软件,发现两个常见的错误:

1. 缺失 数据的处理

对于缺失数据,表示上可以是NA, “.”, -9,应该先保证软件读入的数字是正确的。

之后,在模型中,是应该丢掉数据,还是impute数据?是impute到均值还是用随机数值?

在写软件的时候必须要清楚。

 

2. 数值运算的维数

在统计中,常常有大量的数值运算,比如矩阵相乘。

在文章中这类运算往往会忽略维数,比如矩阵的行数和列数。

这时候软件中应加入更多的边界检查,这样就能避免程序崩溃。

 

 

一些思考方法

一些思考方法
Some way of thinking

一直以来,导师和一些优秀的同学都有不同寻常的有趣的思考方式、交流方式。通过和他们的接触,我总结了一些思考方法。

1. STAR

与别人交流学术往往在于交流方法,交流思考的方式。而人由于所处环境,学习过程的影响,往往有不同的知识结构。比如学习空间统计的人,往往把问题往空间统计模型上靠。这时,有效的交流不是两个人在各自的领域发表见解,而应该是了解对方的思想。
在这种场合,可以使用STAR方法。

STAR方法通常在找工作时回答Behavior Question时使用。它指的是(wiki):

The STAR (Situation, Task, Action, Result) format is a job interview technique used by interviewers to gather all the relevant information about a specific capability that the job requires. This interview format is said[by whom?] to have a higher degree of predictability of future on-the-job performance than the traditional interview.

  • Situation: The interviewer wants you to present a recent challenge and situation in which you found yourself.
  • Task: What did you have to achieve? The interviewer will be looking to see what you were trying to achieve from the situation.
  • Action: What did you do? The interviewer will be looking for information on what you did, why you did it and what were the alternatives.
  • Results: What was the outcome of your actions? What did you achieve through your actions and did you meet your objectives. What did you learn from this experience and have you used this learning since?

The STAR technique is similar to the SOARA technique.

当我们讨论一个学术问题时,在提出解决方案之前,应该在问题的Situation上首先取得共识。在Task方面,我们应该确定行动的目的,或者解决问题的程度,并了解自己的局限(比如时间精力)。之后在Action上了解自己应该怎么做,具体分步骤怎么做。最后在Result部分,可以可以思考完成整个流程自己学了什么,有哪些经验可以为其他工作所用,向导师汇报,与同学交流等等。

STAR方法在交流学术问题,阐述自己的能力上很有用。它本质上希望能站到对方的角度看问题,从对方的处境,思维方式,行为方式来思考。因此它是提高交流能力的方法。

STAR方法还可以稍作扩展用在写作上,这形成一种写作的结构 (Organize information in terms of problem-solution Hoey 1983):

  1. Description of a situation
  2. Identification of a problem
  3. Description of a solution
  4. Evaluation of the solution

这种方法在科技论文写作中很有用。

2. 五问法

五问法,或者称“五个为什么”,“5 Whys” 是丰田创造的应用于制造业的管理方法。先举两个例子:

(1)(http://www.iceo.com.cn/guanli/110/2012/0807/254654.shtml)

一、工厂的地板上有一摊油,为什么?

答:因为机器漏油。

二、为什么机器会漏油?

答:因为油箱破了。

三、为什么油箱会破?

答:因为我们所采购的油箱材质较差。

四、为什么我们所采购的油箱材质较差?

答:因为价格低。

五、为什么我们要采购价格低但质量差的油箱?

答:对采购员的奖励是视短期节省的开支而定,而不是看长期的绩效表现。

(2)(http://zh.wikipedia.org/wiki/%E4%BA%94%E4%B8%AA%E4%B8%BA%E4%BB%80%E4%B9%88)

  • 我的汽车无法启动。(问题)
  1. 为什么?电池电量耗尽。(第一个为什么)
  2. 为什么?交流发电机不能正常工作。(第二个为什么)
  3. 为什么?:交流发电机皮带断裂。(第三个为什么)
  4. 为什么?:交流发电机皮带远远超出了其使用寿命,从未更换过。(第四个为什么)
  5. 为什么?:我一直没有按照厂家推荐的保养计划对汽车进行过保养和维护。(第五个为什么,根本原因

在实际应用当中,有可能将这种提问进一步扩展到六问、七问,甚至是更多的“为什么”。

从这两个例子可以看出,五问法是一种破茧抽丝,追根究底,找出问题根源的思考方法。在软件开发中,在面对一个bug时,我们如何修正错误呢?就地修改自然是“最省时间”,但是这解决问题了么?如果软件本身架构蹩脚,图方便的就地修改可能会带来长期的隐患。

从研究上讲,最重要的就是弄清问题。先用五问法对一个研究问题彻底搞清楚,才有利于下一步的印证或者仿真。值得注意的是,每一次新的发文,都要在核实、查证上一个为什么的答案是不是禁得起推敲。不然五次发问都是表面功夫。这就丧失了五问法的意义。

 

3. 六顶思考帽

六顶思考帽是一个拓展思维广度,协助决策分析的思维工具。它把思维的类型用六种颜色的帽子来表示。

http://www.mindtools.com/pages/article/newTED_07.htm

  • White Hat:
    With this thinking hat you focus on the data available. Look at the information you have, and see what you can learn from it. Look for gaps in your knowledge, and either try to fill them or take account of them. This is where you analyze past trends, and try to extrapolate from historical data.
  • Red Hat:
    ‘Wearing’ the red hat, you look at problems using intuition, gut reaction, and emotion. Also try to think how other people will react emotionally. Try to understand the responses of people who do not fully know your reasoning.
  • Black Hat:
    Using black hat thinking, look at all the bad points of the decision. Look at it cautiously and defensively. Try to see why it might not work. This is important because it highlights the weak points in a plan. It allows you to eliminate them, alter them, or prepare contingency plans to counter them. Black Hat thinking helps to make your plans ‘tougher’ and more resilient. It can also help you to spot fatal flaws and risks before you embark on a course of action. Black Hat thinking is one of the real benefits of this technique, as many successful people get so used to thinking positively that often they cannot see problems in advance. This leaves them under-prepared for difficulties.
  • Yellow Hat:
    The yellow hat helps you to think positively. It is the optimistic viewpoint that helps you to see all the benefits of the decision and the value in it. Yellow Hat thinking helps you to keep going when everything looks gloomy and difficult.
  • Green Hat:
    The Green Hat stands for creativity. This is where you can develop creative solutions to a problem. It is a freewheeling way of thinking, in which there is little criticism of ideas. A whole range of creativity tools can help you here.
  • Blue Hat:
    The Blue Hat stands for process control. This is the hat worn by people chairing meetings. When running into difficulties because ideas are running dry, they may direct activity into Green Hat thinking. When contingency plans are needed, they will ask for Black Hat thinking, etc.

简单来说,白色注重事实;红色注重直觉和感觉;黑色是悲观角度;黄色是乐观角度;绿色要求创新;蓝色要求对思考进行思考。

通过交替使用不同的思维,来扩展对问题多种角度的考量。

个人认为这个思考方法在于转换自己的思维方式,增加思维的广度。

 

 

制作Scientific Poster

制作Scientific Poster
How to make scientific poster

做一个好的poster需要用Illustrator,简单的来讲,Poster中任何非文字的部分都应该采取矢量图(公式、图示、表格),而文字部分应该在Illustrator里完成。

如果想我一样从没用过Illustrator,应该看这个youtube教学视频,这个视频专门针对Scientific Poster分八讲讲解Illustrator中各个细节。

下面一些链接都很有用:
UMichigan library link

Illustrator Creating Text

Scientific Poster Design

A good pattern of color

推荐吳俊瑩的文章

作为一个工科学生,学术水平应该和管理水平同步提高。
吳俊瑩 老师,据我所知,在一个IT公司工作过,同时对公司、项目管理有仔细的思考。
今年以来,读他的文章给我很多启发。

比如 “研發人員應該「拜」王陽明”中首先提到了获取知识的两个方法,看书和问人。之后提出实践,最后讲“在技術的各種領域中,根本的道理有很多還是相通的,新東西永遠學不完,重點應該是自己有沒有把每一件研發工作都 徹底完成,一步一腳印地累積。” 在学术领域,我凭着兴趣接触了各种项目,在很多方面都有自己小小的贡献,但没有适当的总结,写出论文。如果能早早研读吳俊瑩老师的文章,一定会有更好的结果。

吳俊瑩经常在台湾的ITHOME杂志发文。很多文章非常精彩。可惜没有一个索引页面把他所有的文章组织起来。
我就列出几个最近的文章链接吧:

彼得杜拉克如是說
「不聞不問」不算「信任」

在Solaris下安装R

2015-06-16 更新:
这里讲的方法可能已经不管用了,至少在我使用Solaris 10 的VirtualBox Image的时候,下面的方法行不通。

Install Solaris 10 and install R under Solaris
最近的写的一个R package vcf2geno不能在Solaris 10 下编译。
为了解决这个问题,我决定重复CRAN上Solaris 10的测试环境
下面写个流水账

1.安装Virtual Box
下载,安装。

2.安装Solaris 10 和 Guest OS
一定要注意的是手动分区,把/tmp的容量设置到5G以上。不然默认的/tmp只有512M,这样就不方便安装Solaris Studio (第3步)。
安装virtualBox的Guest OS 软件后,Solaris的屏幕分辨率增加。

3.安装Solaris Studio 12.3
下载,解压,安装 (就是特别慢)

4.安装OpenSolaris
Solaris里面没有ubuntu的apt和fedora的yum,但是有个类似的软件叫做OpenCSW Link.
参考OpenCSW的手册Getting Start.
然后用pkgutil安装tetex, gcc4g++, iconv, readline

5.下载R
去R的主页下载源代码,然后解压

6.配置环境参数,安装

export CC=suncc
export CFLAGS="-xO5 -xc99 -xlibmil -nofstore"
export CPICFLAGS=-Kpic
export F77=sunf95
export FFLAGS="-O5 -libmil -nofstore"
export FPICFLAGS=-Kpic
export CXX="sunCC -library=stlport4"
export CXXFLAGS="-xO5 -xlibmil -nofstore -features=tmplrefstatic"
export CXXPICFLAGS=-Kpic
export FC=sunf95
export FCFLAGS=$FFLAGS
export FCPICFLAGS=-Kpic
export LDFLAGS=-L/opt/sunstudio12.1/rtlibs/amd64
export SHLIB_LDFLAGS=-shared
export SHLIB_CXXLDFLAGS=-G
export SHLIB_FCLDFLAGS=-G
export SAFE_FFLAGS="-O5 -libmil"

export CPPFLAGS=’-I/opt/csw/include -I/opt/csw/include/readline’
export LDFLAGS=’-L/opt/sunstudio12.1/rtlibs/amd64 -L/opt/csw/lib’

export PATH=/usr/xpg4/bin:$PATH
export PATH=/usr/sfw/bin/:$PATH

之后我们依次执行:
./configure
gmake #(Solaris下面的GNU make)
gmake install

经验:

    Solaris下自带的软件很少,常用软件要到OpenCSW下载
    Solaris的路径设置和Linux很不同,不在/usr/bin, /usr/local/bin等等。这时候可以用glocate来快速查找, 见【2】

主要参考:
【1】http://cran.r-project.org/doc/manuals/R-admin.html#Solaris
【2】Solaris下的locate工具. Link
【3】http://www.opencsw.org/get-it/packages/

GCC 相关设置(跨平台、不同版本)

查看gcc build-in specs

gcc -dumpspecs
-dumpspecs
Print the compiler's built-in specs---and don't do anything else. (This is used when GCC itself is being built.)

看gcc到哪些目录查找头文件:

gcc -print-search-dirs
-print-search-dirs
Print the name of the configured installation directory and a list of program and library directories gcc will search---and don't do anything else.

This is useful when gcc prints the error message installation problem, cannot exec cpp0: No such file or directory. To resolve this you either need to put cpp0 and the other compiler components where gcc expects to find them, or you can set the environment variable
GCC_EXEC_PREFIX to the directory where you installed them. Don't forget the trailing /.

看gcc编译到那个平台

gcc -Q -v
-Q Makes the compiler print out each function name as it is compiled, and print some statistics about each pass when it finishes.
zhanxw@host10-41.sph.umich.edu: ~/temp> gcc -Q -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/opt/local/libexec/gcc/x86_64-apple-darwin10/4.7.1/lto-wrapper
Target: x86_64-apple-darwin10
Configured with: ../gcc-4.7.1/configure --prefix=/opt/local --build=x86_64-apple-darwin10 --enable-languages=c,c++,objc,obj-c++,lto,fortran,java --libdir=/opt/local/lib/gcc47 --includedir=/opt/local/include/gcc47 --infodir=/opt/local/share/info --mandir=/opt/local/share/man --datarootdir=/opt/local/share/gcc-4.7 --with-libiconv-prefix=/opt/local --with-local-prefix=/opt/local --with-system-zlib --disable-nls --program-suffix=-mp-4.7 --with-gxx-include-dir=/opt/local/include/gcc47/c++/ --with-gmp=/opt/local --with-mpfr=/opt/local --with-mpc=/opt/local --with-ppl=/opt/local --with-cloog=/opt/local --enable-cloog-backend=isl --enable-stage1-checking --disable-multilib --enable-lto --with-as=/opt/local/bin/as --with-ld=/opt/local/bin/ld --with-ar=/opt/local/bin/ar --with-bugurl=https://trac.macports.org/newticket --disable-ppl-version-check --with-pkgversion='MacPorts gcc47 4.7.1_2'
Thread model: posix
gcc version 4.7.1 (MacPorts gcc47 4.7.1_2)

看gcc默认开启了哪些FLAG(定义了哪些macro)

$cpp -dM <(echo "") #define __DBL_MIN_EXP__ (-1021) #define __FLT_MIN__ 1.17549435e-38F #define __DEC64_DEN__ 0.000000000000001E-383DD #define __CHAR_BIT__ 8 #define __WCHAR_MAX__ 2147483647 #define __DBL_DENORM_MIN__ 4.9406564584124654e-324 #define __FLT_EVAL_METHOD__ 0 #define __DBL_MIN_10_EXP__ (-307) #define __FINITE_MATH_ONLY__ 0 #define __DEC64_MAX_EXP__ 384 ... ...

和MacPorts 相关的——如何选择gcc

sudo port select --list gcc
sudo port select --set gcc gcc42 # default gcc in Mac Snow Leopard
sudo port select --set gcc mp-gcc47 # MacPorts GNU GCC 4.7

和平台相关的Macro定义:

其他参考
【1】StackOverflow上的讨论:哪个macro可以区分Mac和Linux Link
【2】SourceForge上Pre-defined C/C++ Compiler Macros,包括以下方面:
Standards
Compilers
Libraries
Operating Systems
Architectures
Devices
Link

Mac Mail 邮件签名的字体

Mac Mail设置邮件签名的时候,签名字体和邮件正文总是无法一致。
比如下图:(之前)

参考:http://apple.stackexchange.com/questions/10530/mail-signature-changes-font-size-when-sent-to-gmail
发现Mac下解决这个问题不难,但也很烦。
把步骤写下来,以备不时之需吧。
1. 退出Mail
2. 进入: ~/Library/Mail/Signatures
3. 用Web Archive Folderizer打开: DBA028E6-8465-492D-B0AF-4B19D3C06450.webarchive (总之是一个奇怪的文件名字)
之后会出现新目录“DBA028E6-8465-492D-B0AF-4B19D3C06450”,里面有一个叫做“localhost”的文件。
4. 用Text Edit打开这个文件,把所有font-size = medium,都改成font-size = 12px 。 因为默认的邮件字体是Helvetica 12。
5. 把改好的文件存为localhost.html
6. 用Safari打开localhost.html,然后存成Webarchive格式,覆盖:DBA028E6-8465-492D-B0AF-4B19D3C06450.webarchive
7. 打开Mail

好了,现在的签名字体和邮件正文的字体一致了。

(之后)

Linux环境下测量程序的内存占用

Measure memory consumption of programs in Linux

在衡量程序性能时不仅需要测量运行时间,还常常需要测量其内存使用。比如如果一个程序比更一个程序快,有可能是以空间换取时间,因此单纯比较运行速度就失去了意义。我们这里演示如何在Linux环境下测量程序的内存占用。

我们先要弄清楚内存的含义:对于运行中的程序而言,它有四个部分:数据,文本,堆,栈(data, text, stack, heap)。对于操作系统而言,所有分配给程序的内存都用虚拟内存表示(Virtual Memory)。对硬件而言,内存分为物理内存(Physical)和交换内存(Swap)。这三种环境的关系有两种:(1)程序内存会被映射到虚拟内存,即虚拟内存中分段对应数据、文本、堆、栈; (2)虚拟内存中某些部分放在物理内存中,另一些放到交换内存中。

为了程序运行有空间上的效率,Linux系统下会共享内存。例如共享程序库(比如libc)或者是内存映射(memory map)。据我的理解,共享部分在程序空间中可以是文本部分,在虚拟内存中放在相同的地址,在硬件中可放在物理内存或交换内存。

对应上面不同的概念,Linux常用的内存相关术语有:
常驻内存(Resident memory), 表示硬件中物理内存的占用。
虚拟内存,Virtual memory,操作系统使用内存的一种抽象。
页(page),操作系统以页为单位管理(分配,回收)内存

测量程序的内存占用可以用不同的统计量。从程序的生命周期来看,可以分为最大(peak)内存占用,即时内存占用,和平均(average)内存占用。从内存的含义来讲,可以在程序本身的语境(context),或是操作系统,或是硬件的语境,比如:栈内存使用(heap memory),虚拟内存占用,物理内存占用。

测量一个程序的内存占用时,最理想的是我们知道(每时每刻)程序的四个部分(在虚拟内存中)分别有多大,对应的,在物理内存或者交换内存中占用了多少空间。但是这是一个很难达到的目标,因为:(1)操作系统以页方式分配内存,很难得到每一个页中具体多少空间被占用;(2)虚拟内存往往比物理内存大很多,当操作系统给一个运行的程序分配内存时总是提供多余程序精确需要的内存;(3)注程序在物理内存的占用加上程序在交换内存的占用构成了程序总的使用情况,但是现有的工具并不直接给出这一数值,需要手动把Resident Memory和Shared Memory相加 。

在实际情况下,通过一些现有的工具,我们往往更关心这几个易于获得统计量:
(1)max resident memory size
可以使用time -v命令(要指定全部路径,否则bash的time命令不识别-v)
注意这个测量值往往远远高于程序运行时消耗的内存。
(这是time 1.7版本的bug,time 报告的内存用量是实际的4倍,因为wait3/wait4返回的实际内存用量,单位是kbyte,但time错把单位设定为page)
本质上time使用了wait3或者wait4命令来获取程序的resources (见 ‘man wait3’)。
使用Python实现时,可以用resources module来获取。
另外,使用这个思路还可以得到程序的运行时间(用户时间,内核时间,实际时间)

(2)top 或者ps
可以查看程序的Resident Size(RES)列,Share Memory(SHR)以及Virtual Memory Size(VIRT),这一统计量默认每秒更新一次。这一方式的缺点是测量值为即时测量,每时每刻这个值都有可能变化。优点是使用起来很方便。
使用Python实现时,可以先使用multiprocesing.Process启动一个程序,然后使用psutil module,构造psutl.Process(),然后使用get_memory_info方式来获取rss,vms

(3)与(2)类似,但为了更多的信息,还可以读取/proc
这是通过读取/proc//status和/proc//smaps来获得程序的内存使用量的。理论上这是最精确的测量。这两个文件的具体格式可以用‘man proc’来获得。
在Python中,需要另外写程序来解释这两个文件,例如下面这样:

zhanxw@amd: ~> cat /proc/15648/status
Name:	takeMem
State:	R (running)
Tgid:	15648
Pid:	15648
PPid:	15163
TracerPid:	0
Uid:	248396	248396	248396	248396
Gid:	248396	248396	248396	248396
FDSize:	256
Groups:	500 1007 1013 1017 1028 1033 248396 
VmPeak:	   12752 kB
VmSize:	   12752 kB
VmLck:	       0 kB
VmHWM:	    1756 kB
VmRSS:	    1756 kB
VmData:	    1112 kB
VmStk:	     136 kB
VmExe:	       4 kB
VmLib:	    3232 kB
VmPTE:	      40 kB
VmSwap:	       0 kB
Threads:	1

在实践中,可以使用已有的程序,例如tmem【4】,以及lh3的runit【6】(下载udp,在runit/下,用make编译)。输出如下。

tmem

./tmem ./takeMem 1000000000
	    3908		    3908	      84	      84
Allocating 1000000000 memory
	  988336		  988336	  148012	  148012
	  988336		  988336	  308524	  308524
	  988336		  988336	  442636	  442636
	  988336		  988336	  567244	  567244
	  988336		  988336	  686308	  686308
	  988336		  988336	  796396	  796396
	  988336		  988336	  889588	  889588
	  988336		  988336	  949516	  949516
runit

./runit ~/mycode/smake/takeMem 1000
Allocating 1000 memory
-- CMD: ./runit /net/fantasia/home/zhanxw/mycode/smake/takeMem 1000

-- totalmem     198345252.000 kB
-- available    190966956.000 kB
-- free           1569216.000 kB

-- retval                   0
-- real                15.749 sec
-- user                15.350 sec
-- sys                  0.170 sec
-- maxrss             704.000 kB
-- avgrss             704.000 kB
-- maxvsize         11904.000 kB
-- avgvsize         11904.000 kB

参考资料:
【1】Python resource 模块
【2】Python os 模块,介绍wait3,wait4
【3】Python multiprocessing模块,介绍了Process这个易于使用的类,与subprocess相似,但有更灵活的用法
【4】Measuring Memory Usage,介绍了用C来读取/proc//status的程序,并给出tmem的下载链接
【5】Understanding memory,非常详细的介绍Linux内存分配,优化
【6】Heng Li’s personal website 可以下载比较hash library的程序,里面有一个runint/文件夹,编译之后可以用runit来测量内存使用

Embedding Python in C/C++

将Python嵌入C/C++

首先嵌入和扩展是相关联但不同的两个概念:嵌入是指在C语言中使用Python,而扩展是在Python中使用C语言(以library的形式)。

其次嵌入Python的本质是嵌入Python 解释器(Interpretor)。因此我们需要调用相应的Initialize, Finalize函数。另外,为了让C/C++识别Python相关的函数,我们还需要#include 以及相应的linker options:-Xlinker -export-dynamic -ldl -lrt -lutil

另外,Python语句有两种:statement 和 expression。注意statement是没有返回值的。因此Python语言里有exec和eval分别对应这两种情形。最本质的区别是statement有副作用(side effect),比如会把值绑定到一个名称上,比如: a = 3 。当我们用PyRun_SimpleString(“a=3”)时,这种副作用是在当前的environment下(内部实现是dict)多出一个变量(dict的key),名称是a。

此外要注意Python的执行代码是和environment相关的,比如global和local,想用的函数名称(例如:dir,str,print),变量都是保存在各自的environment里。平时我们写的 if name == "__main__" 就是说默认的环境是在模块__main__里面。我们要取出默认的函数或者变量(Python内部不严格区分这两个概念,知识函数可以callable),可以用下面的代码(以取出dir函数为例):

    PyObject* main_module =
        PyImport_AddModule("__main__");

// Get the main module's dictionary                                                                                                                                                  
// and make a copy of it.                                                                                                                                                            
    PyObject* main_dict =
        PyModule_GetDict(main_module);
    //  pFunc is also a borrowed reference 
    PyObject* pFunc = PyDict_GetItemString(pDict, "dir");

嵌入Python还应注意内存的使用。因为Python主要使用(另一种是Python的malloc, free)Reference count方式来管理新的变量,我们应记住在获得一个PyObject*类型的指针之后,用Py_DECREF或者Py_XDECREF来减少reference count (注意,特殊情况下取出的结果是不能减少reference count的,比如取出list中某个元素)。

最后给一个例子来说明怎么在Python里计算任何表达式expression (参考了FAQ[1])

#include <Python.h>
                                                                                                                                                                                     
double checkExpression(const char* formular, double gq, double dp) {
    Py_Initialize();

    // Get a reference to the main module.                                                                                                                                               
    PyObject* main_module =
        PyImport_AddModule("__main__");

    // Get the main module's dictionary                                                                                                                                                  
    // and make a copy of it.                                                                                                                                                            
    PyObject* main_dict =
        PyModule_GetDict(main_module);

    char s[1024];
    sprintf(s, "GQ=%lf", gq); //, dp, formular);                                                                                                                                     
    if ( 0 != PyRun_SimpleString(s) ) { // something wrong happen!                                                                                                                   
        fprintf(stderr, "\nSomething wrong in assigning GQ\n");
        return -1.;
    }
    sprintf(s, "DP=%lf", dp);
    if ( 0 != PyRun_SimpleString(s) ) { // something wrong happen!                                                                                                                   
        fprintf(stderr, "\nSomething wrong in assigning DP\n");
        return -1.;
    }

    PyObject* ret = PyRun_String(formular, Py_eval_input, main_dict, main_dict);
    if (ret == NULL) {
        Py_XDECREF(ret);
        PyErr_Clear();
        return -1.;
    };
    double res;
    if (PyInt_Check(ret)) {
        res = PyLong_AsLong(ret);
    } else if (PyFloat_Check(ret)) {
        res = PyFloat_AS_DOUBLE(ret);
    } else if (PyBool_Check(ret)) {
        res = ret == Py_True;
    }
    Py_XDECREF(ret);
    Py_Finalize();
    return res;
};

重要参考资料

【1】 扩展/嵌入Python的FAQ Extending/Embedding FAQ

【2】API 手册 Python/C API Reference Manual

【3】嵌入Python的流程性说明 Embedding Python in Another Application

【4】扩展Python的流程性说明,这里介绍了Python底层的知识,这些知识不会在”嵌入Python的流程性说明”中重复出现 Extending Python with C or C++

iTerm2和Emacs的Meta Key

How to set up Meta appropriately for iTerm2

学校的电脑是Mac,Mac下最好的terminal是iTerm2;我用的编辑器是Emacs,但Terminal下的Emacs不能正常工作,比如M+/是我设置的hippie-expand,我不能用Alt/Command + /按出来,一定要用Esc+/ 。
被困扰了好久之后,我终于下定决心彻底解决这个问题。
NOTE:Alt/Command 指的是一个按键,在windows键盘上是Alt,Mac键盘上叫做Command,上面有一个四瓣的小花(Apple Clove)

首先要弄清楚流程:
1. 我按的键能不能传入iTerm2?
2. iTerm2如何把我的击键传到远处的Linux Terminal?
3. 远端的Terminal里的Emacs能不能正确解析击键?

1. 我按的键能不能传入iTerm2?

在Mac系统,一些组合键还真不一定能传到iTerm2,举个例子:Alt/Command + / ,这个是iTerm2里Find Cursor的Shortcuts,就是我一按这个组合键,就执行Find Cursor的功能,iTerm里的shell是收不到我的按键的。
解决方法:详细见这个邮件列表 http://groups.google.com/group/iterm2-discuss/browse_thread/thread/e960f5098ff3c4a0
就是增加一个系统级别的设置,把Find Cursor的组合键Overwrite掉。

顺便说,我顺便发现一个工具ShiftIt,就是用组合键安排窗口,比如:把窗口最大化,放在左边1/2,右边1/2…… 非常顺手。

2. iTerm2如何把我的击键传到远处的Linux Terminal?

下面是假设我按下键了,iTerm2会把我的击键传到Linux Terminal上。注意这个过程并不简单,因为我的按键会被remap,也会被改动。
举例来说,如果我按下Alt/Command,如果有remap(比如在iTerm2里设置了,或者用Double Command等设置了Remap),实际打出的可能是Option(就是Windows键盘上的Win键)。
还有一种可能是我的击键是Alt/Command,但Linux Terminal不认识Alt/Command,只有强制iTerm2把Alt/Command输出成+Esc 才成。

简单来说,在iTerm2 针对每个Session的设定里,在设定keys时,选择Preset xterm;然后把左右Option Key都选成+Esc。
另外在Linux Terminal中.inputrc文件中加入:
set input-meta on
set output-meta on
set convert-meta off

更多资料,参见在iTerm2里设置Emacs 和 paredit以及 iTerm2官方讲Keybinding的wiki

其中官方wiki指出了一个技巧,Ctrl+V,然后击键,可以看出到底快捷键具体是什么,比如Ctrl+v,然后按Esc,就会出现:”^[“, 说明Esc对应 ^[

3. 远端的Terminal里的bash/Emacs能不能正确解析击键?

Bash 常用的一个功能是backward-kill-word,就是说用快捷键向前删除单词,一般可以用Ctrl+W,还有一种常用的快捷键是Alt+Backspace。比如在iTerm2里可以Remap Alt+Backspace,但是怎么才能输入Alt+Backspace呢?直接输入时,Backspace会删除上一个字符,这时候通过查ASCII表,可以看到Hex 0x17对应backspace,那么我在iTerm2里设置0x17就可以了。

现在在Terminal 方式的Emacs里面(emacs -nw方式),应该可以用Option来代替Meta键,比如Meta+/可以用Option+/来输入。但如果我还是习惯用Alt/Command+/怎么办?
为了不在Emacs里设置,可以在iTerm2中设置 Alt/Command + / 为发送Escaped sequenced: ^[ /

假设我们按了快捷键,只有要Emacs事先知道快捷键对应什么功能才行。(例如可以用Ctrl+h k来看我们输入的快捷键对应的功能)。

经过以上三步,现在如果我需要在Emacs里输入Alt/Command + /, 可以按Esc+/ , Alt/Command + /, 或Option + / .
当然好处不止如此,比如在Emacs里各个窗口移动,可以很方便的用Shift+ArrowKey; 在一个文档快速移动,可以用Ctrl+ArrowKey。当然最大的好处是可以用Option作为Meta,以后按键就方便多了。

2016-12-09补充
如果在GNU screen里用Emacs,需要在.screenrc里加入这两行:
term xterm
c1 off

这是为了确保GNU screen不会改变键盘上输入的Escape sequence。
换句话说,可以保证“Shift+向右的箭头”可以从键盘送到Emacs程序。