Categories

放弃在 Matlab 中 OOP 了

当然,做 Research ,或者说做实验,目的就是要跑出结果来,代码写成什么样子其实无关紧要,而且通常代码的生存时间(比起一般的项目代码来说)很短,大可以不必理会下面这个 tip :

code_tip

不过,太乱的代码自己多少也有些看不顺眼,而且养成一个习惯毕竟也是好事,其实做实验的一些代码也是经常要重用的。特别是我现在这种状态,经常要作为苦力把各种算法拿过来跑一跑,做一个对比。其实除非是做那种完全纯理论的研究,一般都是需要实验来验证的,而做实验就必然要做对比,何老师经常说:“做 Research ,目的不是你的结果要有多好,关键的地方在于你要做得比所有现在已知的方法都要好。”其实也挺有道理的。不过这样一来,会有一些经常需要用来做比较的方法,这些代码在各个实验中其实是差不多的,也总会想着能够尽量地重用,而不是每次都重新写一遍或者 copy & paste (通常是诡异的 bug 滋生的温床)。

虽然我用 Matlab 的时间也不算太长,但是从我第一眼看到它到现在为止,我一直都对它有一个感觉就是:一个 ad hoc 的东西。似乎是最初要解决一个什么问题而创造了一些工具,后来为了解决更多的问题,就把更多的东西堆叠到这个工具箱中,到最后就成了这个“怪物”──功能强大,不伦不类。或者说,不是 well-designed ,不“美”。比如 Matlab 既然可以让你在 prompt 里输入任意一个变量之后很智能地显示出变量的内容来,我却怎么也找不到把一个值转化为一个表示它内容的字符串的函数,按照前面 ad hoc 的说法,就好像作者设计的时候就认为内容转化为字符串之后总是用来打印到屏幕上的,或者根本就没有考虑过,所以就只提供了一个 disp (或者 display)函数。再比如要把函数放到同名的文件里,最后目录下面就是一堆文件混杂了。归类放到子目录下面吧,要设置 Path 还没有简便一点的办法。

不过后来我发现原来新版本的 Matlab 支持 OOP 的,这样可以把一堆函数放到一个类里面,而不是乱七八糟地散落在当前目录里了。我就是这样开始用 Matlab 的 OOP 的。在基类中定义基本算法框架,然后在子类中覆盖特定的方法来实现各种算法,最后有一个统一的接口用于比较──看起来确实是比较完美的做法,但是其实问题还是比较多。

Matlab 的 OOP 虽然在某些地方语法有些怪异,但总体来说和其他的 OOP 大致差不多,和 Python 一样显式地传递 this 参数;继承、多态都有;因为是动态类型的,所以没有重载;毕竟也是脚本语言,元编程也支持一些(其实我只是想要实现把一个“类”放到一个变量里,调用这个变量的某个方法就可以创建一个这个类的对象这么简单的功能而已)。

其实一些小的不便大多能忍受,但是有一个问题从一开始就在烦恼我了:那就是版本的问题。对于普通的函数来说,每当你改动了函数的代码,Matlab 会自动发现并在你下次调用的时候自动使用新版本的函数。然而对于类的代码来说,Matlab 就做得不好了,当它终于意识到有更改了,却发现还存在一些由以前版本的类创建的对象,于是 Matlab 给了一个 warning ,拒绝使用新的代码。其实如果只是更改了成员函数,而没有改变类的成员变量的话,完全可以直接更新的了,因为成员函数反正也只不过是语法糖嘛(更何况在 Matlab 里连 this 都是显式传递的)。这个问题每次都让我觉得非常讨厌,如果之前的对象是无关紧要的,我便把它 clear 掉,有时候 clear 一下还不行,要再创建一个对象,再 clear 一次;如果之前的对象是经过很久的计算才得到的重要结果,那么只好把它保存到文件中,退出 Matlab ,再启动 Matlab ,然后再重新加载刚才保存的对象──实在是非常麻烦。

所以最终我还是放弃 Matlab 的 OOP 了,并不是所有的问题都要用 OOP 来解决,而 OOP 带来的更多的便利似乎在于语法上更简洁一些,而那在 Matlab 脚本这样的语言里似乎完全没有价值。但是我还是希望将函数好好地组织一下(我想说不定不久以后我连这个底线都会放弃了),所以最近开始利用 Matlab 里同的 package 的功能了。

依旧是怪异的方式:让一个目录的名字以加号开头,那么它就代表一个 package 了,比如你有这样一个目录结构:

current directory
 |-- foo.m
 `-- +bar/
     |-- foo.m
     `-- baz.m

注意到那个目录的名字是 +bar,这样你在调用 foo 的时候是使用顶层的 foo.m 中定义的函数,然后你可以把函数分类组织到各个 package 中,现在你可以通过调用 bar.foo (没有前面的加号)和 bar.baz 来调用 package bar 中定义的两个函数。虽然有点怪异,但是还不错!只有一点不太方便:bar.baz 想要调用 bar.foo 的时候也必须写全称,否则就会调用到顶层的 foo ,换句话说,package 内的代码本身对自己身处哪个 package 并没有什么概念。

哦,当然,还有另外一个更不方便的地方,这次问题出在 vim ──我有时候会用 vim 来编辑 Matlab 代码。vim 不认识 Matlab ,它觉得世界上不可能会有哪个疯子会让文件名以加号开头,所以你在终端下输入 gvim +bar/foo.m 的时候会得到这样的结果:

Error detected while processing command line:
E492: Not an editor command: bar/foo.m
Press ENTER or type command to continue

zsh 倒是挺通情达理,你输入 gvim +bar/foo.m 再按 TAB 的时候它会自动修正为 gvim ./+bar/foo.m,看来 zsh 是知道 vim 的怪癖的,因为如果是 emacs +bar/foo.m 就不会有这样的特殊对待(当然,Emacs 本身也确实没有这样的怪癖的)。可是,就算是在 vim 里输入 :e +bar/foo.m 也是会报错,害得我每次都得再前面加一个 ./

不管怎么说,现在先这样吧,我也觉得再这样下去我不是变成愤青就是要彻底脑残了。-.-b

3 comments to 放弃在 Matlab 中 OOP 了

  • 是因為vim吃+開頭的命令行參數。(話說這個還是vi的傳統,倒不是vim的發明。)

    emacs不吃加號,可是減號也吃呀。假如有個文件名叫-test,emacs -test一樣會報錯。

    基本上非腦殘的程序都支持以–的形式規避,所以:

    emacs — -test
    vim — +test

    就可以啦。當然加./也算是一個辦法。

  • matlab是有把一个值转化为一个表示它内容的字符串的函数的,sprintf()就是,如果用fprintf()就是输出到屏幕上

  • @yoco
    sprintf 不是要加 % 表示值的类型?不能像 disp 那样通用。