找回密码
 注册
Simdroid-非首页
查看: 724|回复: 8

[编程进阶] Mathematica 面向对象编程解决方案

[复制链接]
发表于 2011-8-25 14:33:08 | 显示全部楼层 |阅读模式 来自 LAN
本帖最后由 guocong89 于 2011-8-25 19:11 编辑

很多朋友都在问如何让Mathematica能够像C++一样进行面向对象编程,我在暑假阅读《计算机程序的构造与解释》时突发灵感,信手写了这么一个Mathematica面向对象包,特把相关技术细节整理下来。

面向对象原理

什么是面向对象?维基百科说,“面向对象程序设计英语Object-oriented programming,缩写:OOP),指一种程序设计范型,同时也是一种程序开发的方法。它将对象作为程序的基本单元,将程序和数据封装其中,以提高软件的重用性、灵活性和扩展性”。

C语言是面向过程的,我们把要做的事情分成一个个函数来完成,比如我要写一个公交车自动收费系统,那么我需要为公交车行驶,公交车报站,公交车开关门,乘客上车,乘客交费,乘客刷卡等行为编写相应的函数。

后来人们发现,程序里的很多函数有很强的分类性与隔离性,公交车行驶可能和乘客上车完全不相干;而且人类认知世界的方式也是先知道有某样东西,然后知道它有什么用。因此,一种面向对象的程序设计思想逐渐诞生。


面向对象里最核心的概念就是对象。拿生物作类比,类就是一种物种,对象就是一个特定的生物;类是抽象的概念,对象是存在的实体。因此,对于“猫”这个类,它有性别,毛色,年龄等数据属性(称为成员变量),还有捉老鼠,睡觉等行为属性(称为成员函数);而一只特定的猫,就会拥有它自己特定的性别,毛色和年龄,但对于成员函数,相同类的对象是相同的,只是函数执行时会用到对象的成员变量值而计算出不同的结果。


类概念的引入,使得我们可以把相关性很强的函数与数据进行封装,增强程序的模块化程度;同时面向对象更加适宜我们对一个大型项目进行建模。

那么,C++里的面向对象是什么样子的呢?我们来看个例子。

假设我们有两个学生对象 tom和kate,他们都有speak函数来说出自己的名字,
  1. tom.speak();
  2. kate.speak();
复制代码

运行结果可能是
  1. My name is Tom.
  2. My name is Kate.
复制代码




我们看到,两个speak函数具有相同的签名(signature,表示这个函数的参数类型,数目,返回值类型等一系列特征),但执行时带入了不同的参数。实际上,C++的成员函数与普通函数有个本质区别,即成员函数有一个隐含参数this,表示调用该成员函数的对象的指针,这样,成员函数就能够通过this访问这个对象的成员变量与函数,从而根据不同的对象算出不同的结果。这就是C++面向对象技术中最关键的地方,this指针的引入,实际上,在其他面向对象语言中都有相似的机制。


Mathematica 面向对象的实现


那么,我们该如何在Mathematica模拟出面向对象的效果呢?


首先看类的结构是什么样子的。


类,其实就是一组成员变量和成员函数的列表。假设我们有一个 man 类,包含 name 变量和 say 函数。那么,我们可以把man定义成如下的形式
  1. {{“name”, Null},{"say",Null}}
复制代码


我们无需区分变量和函数(它们统称为),直接把它们放在同一个List里。每一个域都对应一个二元List,分别表示它的名字和值;这么做是为了方便我们访问这个域。

这里需要额外插点其他技术知识。面向对象的类里有一种特殊的函数与变量,称为静态函数与静态变量。这些变量是不属于任何对象而是属于这个类的,并且被所有对象所共享。比如“猫”类,我们有一个静态变量maxAge,表示猫的最大寿命,比如我们取30,那么所有的猫对象都可以访问这个数据甚至修改它,但这个数据不属于任何猫对象,而是猫类的一个公共属性。

我们的Mathematica实现里,没有明显的区分静态函数、静态变量;事实上,我们的对象具有和类一模一样的结构,通过类访问域,得到的就是静态域,通过对象访问,得到的就是成员域。我们的这种实现有如下的局限:对象中也保存着静态域的数据;对象可以修改静态域但是不会更新类的静态域(这个缺陷可以通过小修改来克服,暂时没有实现)


正如上面所说,我们的对象具有和类完全相同的结构,因此从一个类构建一个对象只需要复制这个类的结构即可。


接下来的一个问题便是,我们该如何访问这个类的域呢?

我们在前面的擂台帖子(http://forum.simwe.com/thread-997834-1-1.html)中给出了如下的访问方法:
  1. tom = New[man];
  2. tom["year"] = 1989;
  3. tom["name"] = "tom";
  4. tom["say"][]
  5. tom["age"][]
复制代码



相当于用一个下标作为索引访问具体的值。FlyingDuckman 通过定义New来实现,我当时已经指出其中的问题;而且还有一个更深入的问题在于,如此实现,New将不堪重负,每定义一个类就要定义这么一组New函数,完全不符合我们的需求。

对于前一个问题,我们就要仿造C++来引入this指针,这个会在后面介绍;后一个问题,我们要使用一种特殊的机制,称为 Dynamic Dispatch(参见 http://en.wikipedia.org/wiki/Dynamic_dispatch)。

不知道大家是否还记得或者阅读过这篇文章 http://forum.simwe.com/viewthread.php?tid=991845&highlight=%B1%D5%B0%FC,关于Mathematica中的闭包实现。我们这里不需要使用闭包,但是它给我们提供了一种无与伦比的技术:匿名变量

匿名变量?Ok,这是我自己杜撰的名词,但它名副其实。那个例子里,你知道y是什么么?不,你永远不知道,除非你用?去搜索所有以y开头的变量;但是y却是客观存在的,并且每当你调用Add函数时,不仅改变了y的值而且返回它。这就是匿名变量的全部内涵。

那么匿名变量对我们有什么帮助呢?它就是我们实现Dynamic Dispatch的核心。


我来举一个简单的例子。
  1. lily = Module[{me},
  2.   me["name"] = "Lily";
  3.   me["say"] := Function[{}, Print["My name is ", me["name"]]];
  4.   me
  5.   ]
  6. lily["say"][]
复制代码




OK,把这段代码仔细品味品味,我的面向对象思想已经昭然若揭。
我不想过多解释这段代码,只希望你好好考虑如下的问题:lily是什么?lily[“say”][] 是怎么执行的?


这个解决方案的诞生不是灵光一现,它足足折磨了我三天三夜;感谢《计算机程序的构造与解释》,这个解决方案几乎完全是由它给出的。


this指针的实现


那么this指针呢?你肯定立马可以看出来,上面的me就是this指针。对于类的域函数定义,包含两种类型:第一种是静态函数,定义成 Function[{x},y],y仅仅是x的函数,与对象无关;第二种是成员函数,定义成 Function[{this},Function[{x},y]],y是this和x的函数,this代表传入的对象。在对象中dispatch函数时,根据嵌套的Function层数决定是否是静态函数。至此,我们的面向对象体系基本架构完成。


以下是使用我编写的程序包实现的小程序。


  1. << DabaoClass`
  2. man = DefClass["man", {year, name}]
  3. AddField[man, "age"]
  4. man["age"] =
  5.   Function[{this}, Function[{}, First[DateList[]] - this["year"] + 1]];
  6. AddField[man, "say"]
  7. man["say"] =
  8.   Function[{this}, Print["My name is ", this["name"], "\nI'm ",
  9.      this["age"][], " year's old"] &];
  10. tom = New[man];
  11. tom["year"] = 1989;
  12. tom["name"] = "tom";
  13. tom["say"][]
  14. tom["age"][]
复制代码



运行结果
  1. My name is tom
  2. I'm 23 year's old
复制代码



程序包下载
下载后请将扩展名 .txt 删除

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

×
发表于 2011-8-25 14:42:52 | 显示全部楼层 来自 湖南长沙
Simdroid开发平台
楼主终于发解决方案了,感谢感谢!
回复 不支持

使用道具 举报

发表于 2011-9-14 10:51:26 | 显示全部楼层 来自 陕西西安
无比感谢
回复 不支持

使用道具 举报

发表于 2011-10-30 20:27:48 | 显示全部楼层 来自 四川大学
这段代码在豆瓣的Mathematica小组见过,也是出自楼主之手吧。
回复 不支持

使用道具 举报

发表于 2011-11-25 10:46:53 | 显示全部楼层 来自 四川成都
楼主威武,很不错的:)
回复 不支持

使用道具 举报

发表于 2012-1-11 03:36:34 | 显示全部楼层 来自 英国
非常不错的思路,如果mathematica面象对象的编程,且能封装,可以极大的拓展其应用范围.
回复 不支持

使用道具 举报

发表于 2012-3-10 15:05:22 | 显示全部楼层 来自 浙江杭州
很不错的一个应用案例,很有创新意义!
回复 不支持

使用道具 举报

发表于 2012-6-15 13:23:19 | 显示全部楼层 来自 北京
没有实用价值的,玩玩尚可。
Mathematica,Maple等数学工具有着极其丰富的工具包,
这就决定了 再大的项目用它们的语言写起来 也不会很繁杂的。
回复 不支持

使用道具 举报

发表于 2012-11-11 21:52:45 | 显示全部楼层 来自 黑龙江齐齐哈尔
无比感谢
回复 不支持

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

Archiver|小黑屋|联系我们|仿真互动网 ( 京ICP备15048925号-7 )

GMT+8, 2024-3-29 05:39 , Processed in 0.046312 second(s), 13 queries , Gzip On, MemCache On.

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表