Fermat618's Blog

Happy coding

重新弄好打印机的经过

Linux 系统下一直能用的打印机,大概又是因为升级,从前一段时间开始,就不能用了。显示打印成功,但是打印机绝不动一下。删除后重新安装打印机,一切都显示正常。都显示正常,没半点报错信息。

终于忍不了了,折腾了老半天,算是搞定了。

首先,安装 hplip 这个包,为了不出问题,我顺带连 hplip-gui 也一起装了。

再以 root 权限运行 hp-setup 命令。

以上过程,每一个都不能错半点,否则你也得不到半点错误提示,甚至会得到正确提示,但打印机就是不能用。

比如说,在系统的打印机管理里面添加打印机,不管你怎么弄,打印机都成功不了。或者运行 hp-setup 命令时没有用 root 用户,那么会在后面的一个步骤上卡死,那个界面你再不无法点得动了,打印机自然也是安装不上的 。

又写了个油猴脚本

来个总结吧。

首先,是如何看到脚本出错信息。脚本出错的信息,只有在全浏览器的控制台下面,才可以看到。按 Ctrl + Shirt + J, 可以进入浏览器的控制台。在过滤窗口,输入脚本的名字的一部分,可以过滤出错信息。网页上的 javascript, 普通写得烂,出错信息一堆堆的,不过滤 ,有用的信息就会被淹没。

临时调试脚本,可以使用 Firefox 自带的代码片段速记器(shift + F4)。按 Ctrl + / 可以方便地注释掉一行。想知道某些东西有哪些方法可以调用,可以选中那个对象(或返回一个对象的表达式),按查看,就可以看到。

Firefox 自带的开发者工具的好用程度,已经超出了 firebug, 所以不推荐用firebug 了。

如何存储信息。使用 GM_getValue, GM_setValue, GM_deleteValue, GM_listValues 等函数,可以针对每个脚本存储本地信息。由于单个的 GM_setValue 不可以存储列表,我把整个存储空间当成一个列表来使用了,反正我与个脚本也只需要存储一个列表。要再加功能,另开一个脚本就好了。

写稍复杂点的功能时,使用 typescript 比直接使用 javascript 爽多了。特别是想建立类的时候,代码干净了很多。

最后,说一下我写的这个脚本的功能。上论坛时,总会遇到某些令人不愉快的人,而很多地方又没有提供黑名单功能。为了屏蔽掉这些人的帖子,我以前写了一个脚本,把这些人的名字保存起来,然后过滤掉这些人发的帖子。但是那些个名字都是以字面形式保存在代码里面的,每次需要把人加入时,就要手动打开程序,编辑文本,很是麻烦。所以,这回我把代码改了一下,直接点击鼠标就可以把某些人加入到黑名单里面。

双显示器的连接

长久以来,多次感觉到,文档与编程窗口不能同时看见,经常需要切换,影响效率。看到有人说到,双显示器可以大大提高程序猿的工作效率。正好旁边又有一个显示器,于是便想拿来用了。

首先遇到的一个问题,便是没有两个 VGA 接口。主机是 Intel 集成显卡,机箱倒过来一看,有一个 VGA 接口,一个 DVI 接口,另一个 HDMI 接口。便想到了接口转换,问了下其它人,只要是把显示器插上去了,就可以。上网找相关的转换线,有 HDMI 转 VGA 的,也有 HDMI 转 DVI 的, 但 DVI 转 VGA 的,只有 24+5 转 VGA 这一种类型,没有 24 +1 转 VGA 的。双上网查询 DVI 接口相关资料,恍然大悟,DVI 分 模拟 (DVI-A)、数字(DVI-D) 和集成(DVI-I) 三类,而 X+5 与 X+1 相差的那四根线,正是用来传递模拟信号的。于是死了心,DVI-D 转 VGA 的转接口,看来是没有了。

转换线拿到后,把显示器插上,果然,双显示器立即就可用了。在 Gnome3 的“显示”设置上,还可以调整两个显示器的相对位置。默认的是把主显示器在左边,后来插上去的,也就是第二显示器,在右边,刚好符合。

工作了一段时间后,遇到另一个问题,便是 pycharm 的弹出菜单总是出现在第一块屏幕上面。上网查询相关的信息,发现这是早就存在的一个 Bug. 查询多个帖子,有说把 JDK 从 1.7 降到 1.6 的,把 X 的模式换成 Xinerama 的,还有 Ubuntu 下特定的,把启动快捷菜单的 autohide 设置成 on. 最后一个 Ubuntu 特定,是不能用了。X 模式也不太明白,也没看到有人说用这种方式成功的。倒是看到了不少把 JDK 降级而成功解决了这个问题的,但没说具体方法。是于,自己用编辑器打开 pychar 的启动文件,查看相关代码,终于找到了 PYCHARM_JDK 这么一个环境变量,而且后面在检查 $PYCHARM/bin/java, 于是,这个变量应该设置成什么样子,也就知道了。试着命令行中用

PYCHARM_JDK=/usr/lib/jvm/java-6-openjdk-amd64 charm

启动,果然,Bug 不见了。


最后,把 pycharm 安装目录下 bin/pycharm.sh 中,加上了这一句,以免每次都需要手动打。

Vim 的超级牛力--一次 debug 经历

Python 在数值计算方面,有一个非常好的功能,可以写一个 Fortran 程序,然后通过简单的 f2py 命令,便可变成一个 python 模块,以供在 python 里面调用。

 

在我用 f2py 把 Fortran 文件编译成 python 模块时,遇到一个奇怪的问题。当用其它的编辑器打开并修改  .f90 文件后,再使用 f2py 转到 python 模块,会发现模块里面的变量值没有更新。但当使用 vim 打开文件并保存后,就能得到正确的值。

 

刚开始,我以为是不同编辑器对修改时间可能有不同处理,影响到构建程序依赖关系得不到正确处理。所以,我改用了直接调用相关命令,而不依赖于 scons 的自动构建。但是,问题依然存在。

然后,我 又试了不同的编辑器,eclipse 的内置编辑器,nano, gedit, emacs 等,其它编辑器表现一至,除了 vim 比较特殊。

 

这个没办法了,我又拿出其查看访问时间与修改时间的东西,当编辑器保存完时,查看这两个时间,没发现 vim 和 其它编辑器有什么不同。

 

后来,我新建了个目录,把相关的文件放进去,终于发现,当有 .mod 文件存在时, f2py 会用原来的 .mod 文件,而不是生成新的 .mod 文件。

 

为什么 .mod文件能影响到 python 中 module 变量 的值呢,是因为我用了带 parameter 的变量,这样的参数会被写入到 .mod 文件,而不是 .o 文件。

 

后来的后来,我发现用 vim 修改了文件之后,python 模块中的变量值,变成了一个乱码。于是我又保存了老的 .mod 文件,再跟用 .vim 保存完 .f90 后的 .mod 文件相对比,果然,内容变了。

再仔细一看变了一部分,real 默认成了 real 8. 我喜欢用 -fdefault-real-8 来写 Fortran 程序,这样子,不用每次都写 real(8) 了,省事得多。这个参数需要手动指定,我在哪里指定了这个参数呢,SConstruct 中,但是那个文件不在当前目录,起不了作用。

终于的终于,我想起来了,在 vim 的插件 syntastic 的配置文件中,我指定了这个参数。由此,一切直相大白。synntastic 会在保存的时候,编译一遍当前文件,相获得语法错误,于是,就产生了新的 .mod 文件。

 

f2py 在这个地方没有问题么,也有。问题在于,它隐式的采用了一个中间文件,而没有任何的警告。这是不妥的,如果要采用那个文件,要么使用显式的命令行参数来指定,要么另外生成。就像在运行 gcc main.c foo.c 时,无论有没有 main.o 或 foo.o 存在,编译器都要新生成一个 .o, 因为命令行参数中指定的,不是 .o, 而是 .c.

Gtk Tree View 子节点乱跑的问题

pida 有个 ctags 插件,可以从 ctags 分析出来的文件中,新建 tags. 但有时,会发现 tags 乱跑的问题,即某一个父节点的子节点,跑到其它地方去了。

ctags 输出的文件,在程序内部,经分析后,成一个一个的元素。每一个元素,有一个指向父节点的指针。在把这些分立的节点插入进 tree view 的时候,如果子节点先插入,插入时其父节点还未被插入,那么,那些父节点在插入后,不会自动把以前的子节点收进来。

解决这个问题的一个办法,是在插入 tree view 之前,先调整顺序,把一个节点的所有父节点,都放在自身的前面。因为节点可以有分级,父有子,子有孙,递归和 Python 的生成器恰好派上用场,程序看起来就很简洁明了了。

        items = list(....)

        def pop_parent_first(item):
            p = item.parent
            if p is not None and p in items:
                for i in pop_parent_first(p):
                    yield i
            else:
                items.remove(item)
                yield item
        while len(items) != 0:
            item = items[0]
            for x in pop_parent_first(item):
                yield x

第一行,是把原来的元素,做成一个列表。pop_parent_first(item) 函数,在弹出一个对象前,检查有没有父对象,如果有,延后自身弹出,先把它的父对象弹出来。

在 anjuta 中,我又发现了这个问题,觉得很可能是上面这个原因。可惜的是 anjuta 用 C 写的(而不是vala),难找到有用的信息,看了一会,也没找到出错点。

Fortran 中根据语义的补全

Fortran 中的 if 结构,函数声明等,都有不少的信息冗余。如

if (3 > 2) then
    !...
endif

real function add(a, b)  
...
end function

后面的 endif, end function 等,长度都不少。这样虽然提高了可读性,但手动敲下未免还是太麻烦。把时间花在敲入一个很长还必定会有的东西上面,很不划算。如果用 snippet 之类的方案,需要记住一个 key, 然后每次按 tab 展开,除了语言本身,又多了一个记忆 key 的负担。

于是,我想到了一个更为自动化的补全方案,就是让程序根据已经输入的内容,在敲入回车键换行时,自己判定这是不是一个未完成的 if 结构,然后自动在后面补全 endif.

因为这个功能绑定到了一个很重要的按键上,所以功能要尽量的不添乱 。所以,我想到把当前行按照程序语义给解析出来。如果能成功的解析,就补全后面的内容;如果不行,就执行普通的Enter.

解析程序的语义,还可以带来另外的好处。一个是自动加空白,如

do i=1,n

给自动化成为

do i = 1, n

有了空格之后更加的好读。另一个功能是把一些冗余的东西省掉,如

if (a > 3) then

有用的信息只有

if a > 3

因此,只需要敲下上面的文字,再回车,就可以补全成符合 Fortran 语法规范的形式。

代码在此 https://github.com/fermat618/fortran-construct-complete

 

整数,小数;整型,浮点型

这四个概念,前面两个,表示数学中的整数,小数;后面两个,表示计算机中相应的表示。

在C语言中,整数一般用整型表示,而小数一般用浮点型来表示。先学C语言的人往往容易对此有固定的印象。同时会觉得浮点型表示的数都是不精确的。然而,这个印象是不准确的

浮点型数也可以表示整数,而且在整数不是非常大的情况下,这种表示是精确的。对浮点型表示的整数,进行加、减、乘操作,得到的仍是精确的结果。仍可以用来表示长度,表示数组下标等。

在很久以前,浮点数的计算很慢。然后,现在的发展下浮点数的计算已经快多了。而对于脚本语言来说,速度往往不是那么的重要。Cpython 的运行要比相应的C语言慢100倍左右。这时,使用整型来表示整数的速度优势完全没有了,而空间优势也不大。所以,在脚本语言中区分整型和浮点型已经是件很尴尬的事。而 Javascript 的数值类型已经默认只有一个,就是浮点型。Matlab 的默认类型也已经是浮点型。Python 3 中 int 已经默认可以为任意大的整数,算是给整型的存在又找回了点面子。

 

Python 中的 sum 函数

 
 

 


做 projecteuler.net 上面的题时,遇到一个问题,需要对某个范围内的数进行筛选,并且把所有符合的结果都给出来。直接些的做法是做一个数组,在筛选的时候,遇到符合条件的,就放进那个数组里面。但维护状态容易出错,我尽量避免。另一种做法就是

def foo(n):
    if p2(n):
        return [a]
    else:
        return []

然后把结果集合在一起。

集合在一起的方法在 Haskell 里面就很多了 concat, join, >>= id 等。Python 里面,我首先想到的就是用 reduce 和 + 弄了个。

from functools import reduce
from operator import add

reduce(add, map(foo, range(10000)), [])

但看着总是有些不爽。因为对于普通数的相加,一个 sum() 函数就搞定了,哪用得着那么麻烦。于是试了一下

sum(foo(x) for x in range(10000))

报错,再了看那错误信息,说是什么 int 不能和 list 相加。然后又去查了一下 sum 函数的文档

sum(iterable[, start]) -> value

Returns the sum of an iterable of numbers (NOT strings) plus the value
of parameter 'start' (which defaults to 0).  When the iterable is
empty, returns start.

虽然上面写了是求数值的和,而不是字符串的,我还是试了下把最后面的那个参数置为 []

sum((foo(x)for x in range(10000)), [])

果然是可以啊。

接着我试了一下字符串的相加,得到一个有意思的错误。

sum(['foo','bar'],'')
TypeError: sum() can't sum strings [use ''.join(seq) instead]

它报了个错,然后给了个更好的写法。

''.join(seq)

用这个作为一大串字符串的相加,不但可读性好,而且效率上可以预期将比 sum() 函数的直接使用加法把字符串联接起来要好。这点在 PEP8 里面也说过,虽然 Cpython 实现的字符串的 += 运算符比较高效,但不能对其它实现作如此预期。

s = ''
for s2 in xxx:
    s += bar(s2)

的代码应该写成

''.join(bar(s2) for s2 in xxx)

 

 

debian 下编译安装同时支持 python2 和 python3 的 vim

 

Debian 下编译安装同时支持 python2 与 python3 的 vim

由于 python 相比 vim 原来的那个脚本实在是强太多,我转到用 python 来写 vim 代码了,并且是坚定的 python3.

而有两个我想用的 vim 插件却是 python2 的,自己改成 python3 版本的话工作量太大,而且升级的时候又得操 心。

以前在 Debian 下编译的 vim 无论怎么都没法做到同时支持 python2 与 python3. 最好的情况是,编译好后 同时有 +python/dyn+python3/dyn 特性,但只要执行了其中一个 python 版本的命令,另一个 python 版本的命令便无法执行。

后来发现是 Debian 自带的那个 python 的问题,具体问题我忘了。

既然系统自带的那个有问题,解决的办法就是自己安装一个没问题的 python 了。但系统的 python 不能 随便换,否则遇到的麻烦接连不断。那就编译一个 vim 专用的 python 吧,不放在 $PATH 当中,也不影响 系统原来的 python 和其它程序。

编译 vim 专用 python, 安装目录当然就放在 vim 的目录之来了。下载 python2 与 python3 的源代码,并 如此

./configure --prefix=/usr/local/share/vim/usr --enable-shared

定义 ./configure 时的选项。这样安装时就自动安装到 vim 所属的目录下面了,并且其共享库也编译好了。

然后就是编译 vim 了。经试验,改如下两个环境变量

PATH=/usr/local/share/vim/usr/bin:$PATH
LD_RUN_PATH=/usr/local/share/vim/usr/lib
export PATH LD_RUN_PATH

之后,再进行 ./configure $PATH 环境变量,决定 vim 找到的 python 的位置。而 $LD_RUN_PATH 环境变量,则是链接 vim 时所需 要的,可以让 vim 找到 python 的 so 文件的位置。有了这个之后便不再需要 $LD_LIBRARY_PATH 变量了。

./configure \
        --with-features=huge \
        --enable-python3interp \
        --enable-pythoninterp

接着 make 完成后测试 ./src/vim 不出问题的话,就可以成功了。

需要注意的是,每次成功编译后,需要执行

make clean && make distclean

否则不能编译成功。

安装好后再看两个 python 所占大小,才区区 202MB ,想一下可以不费劲地用现有的 python2 的插件了,真是很 划得来啊。

Date: 2012-10-22 21:59:33 CST

Author: fermat

Org version 7.7 with Emacs version 24

Validate XHTML 1.0

 

掉 intel fortran 编译器返回派生类型的方式的坑里了

 
 

最 近写 matlab 与 Fortran 的混编程序时,为了使程序写起来更容易,采用了 Fortran 2003 中 iso_c_binding 模块的一些内容。这些内容的采用使得那些接口写起来 更容易 了,本来松了一口气,想换到 intel fortran 编译器下的时候,又来了那个让我 一看到 就头疼的段错误。

然后又得开始调试了。在仔细查看了程序后,没发现有问题。 然后开始写小程序测试 里面用到的语句。吸引以前的教训,现在仔细检查调用 matlab mex API 后的返回值。可 是 type(c_ptr), 这个 iso_c_binding 模块中规定的放 C 指 针值的数据类型, 却没法打印出来看。

通过 locate iso_c_binding 命令。找到了 intel 的 iso_c_binding.f90 这个文件。打开后查看其内容,发 现

type, bind(c) :: c_ptr
integer(xx), private :: ptr
end type 

type(c_ptr) 的内容被声明为了私有的。 把这个文件拿出来,去掉 private 属性,再手动编译,总算是可以看到 type(c_ptr) 的 内容了。 把同一个 mex 程序用 intel fortran 与 gnu fortran 编译器分别编译两份, 再开一个 matlab, 在里面声明一个变量,传进去,查看各个函数的返回,找到了 mxGetPr 函数返回的结果不对。

 

又另写了一个函数,把 mxGetPr 函数的返回值申明为 integer(8), 再看结果,却又是对 的了。

终于定位到了是 integer*8 和 type(c_ptr) 的区别,才想到可能是 intel 对于后一 种可能采取了不是传值而是传值的方式。另写函数一验证,果然如此。

派生类型与内置类型一样,也是标量。同样的标量,在 intel fortran 中的处理,却是 很不相同,这很超出我预期。这种处理方式真是相当的不统一。标量传值,矢量传址,这 才是统一的方式嘛。

又试了给函数加上 bind(c) 属性,这次终于可以传值了。但是加上了这个属性的函数, 其编译出来的文件的符号的处理又跟普通的 fortran 函数不一样了。 matlab 给 Fortran 的那些 api 的符号名本来是按照 fortran 来的,这么一来就得在 bind(c, name=xxx) 里面写明了。使用 nm 得到实际的符号名后,写死在里面,再加上条件编译,总算是使得 gfortran 与 intel fortran 都可以编译那个程序了,代价就是只能用在 64 平台及开启 了 -largeArrayDims 选项的情况下了。