C++和Java程序员的Smalltalk入门知识分析
C++和Java程序员的Smalltalk入门知识分析
C++和Java程序员的Smalltalk入门
原文标题:I Can Read C++ and Java But I Can’t Read Smalltalk
原文作者:Wilf LaLonde
原文链接:http://www.eli.sdsu.edu/courses/spring01/cs635/readingSmalltalk.pdf
简介
有很多人告诉我他很熟悉C++或Java,但是却完全读不懂Smalltalk的代码。对于他们来说,Smalltalk简直无法理解!对于这个问题我考虑了很久然后得到的结论是他们是对的。如果我随便挑出一些自己多年以前写的Smalltalk代码,然后假设我只明白Java去阅读的时候,我确信我是无法理解那些代码的。其实要读懂Smalltalk只须了解一些非常简单的概念,当然有些概念也是比较微妙。如果“Johnny读不懂Smalltalk代码”,我有办法。我的办法就是通过实际的例子来帮助新手快速理解Smalltalk的概念。我假设读者了解什么是面向对象变成,对于那些已经掌握Smalltalk的读者也请先假装一会没有学过smalltalk。
如此简单的文法
有些语法是很容易理解的,例如使用双引号来标识注视块;单引号标示字符串,单个字符前面加$(例如:$x标示字符“x”)。除此之外,还有symbol的概念,这是一个特殊的字符串,在整个内存中是唯一的。当源代码被编译的时候,编译器会搜索整个内存,如果发现相同的symbol存在则使用已存在的那个。有理数并不节省内存,但是相对于symbol而言处理速度更快(稍候解释)。
“this is a comment”
‘this is a string’
#’this is a symbol’
#thisIsASymbolToo |
对于赋值操作和等于号,差别不是很大。
:= //Means assignment
= //Means equality comparison
== //Means identity comparison |
如果你给我分别使用’a’和’b’命名的对象的引用,我可以告诉你他们是否是同样的对象(使用a == b, 命名等价)或看上去相同实质上不同的对象(使用 a = b构造等价)。直观的来说,== 比较两个被引用对象的地址是否相同,而 = 则比较两个对象的整个内容是否相同。
Smalltalk代码中很少用到逗号,因为他们不是Smalltalk文法组成的一部分。这就是为什么Smalltalk的数组是没有逗号的。例如:
#(1 2 3 4 5) |
然而,都好是smalltalk的一个操作符。因此你会看到他被用来连接两个字符串,例如:
‘string1’,’string2’ |
无处不在的关键字
Smalltalk中关键字是无处不在的。他们的存在是为了帮助理解代码而不是增加混淆。要明白这到底是怎样一回事,让我们先来看看C++和Java的语法。例如,
t->rotate(a,v); //For C++
t.rotate(a,v); //For Java |
上面的代码向对象t发送消息(注:就是调用类方法)rotate,并指定了参数a和v。为了理解这段代码,读者通常需要继续察看作为参数的变量的申明及其类型。让我们假设其有如下的申明:
Transformation t;
float a;
Vector v; |
在Smalltalk中,变量可以引用任何对象,因此在申明变量的时候没有必要指定变量的类型。例如:
| t a v| |
由于没有变量类型申明,好的Smalltalk程序员都会使用能暗示变量类型的变量名。因此,上面的申明我们通常如下表示:
| aTransformation angle aVector| |
但是后面请允许我继续使用先前的变量名,因为杂志专栏给我可利用的版面很校让我们进一步消除那些不必要的东西来继续“优化”C++和Java的语法。例如,下面的代码应该仍然很好理解:
t.rotate(a,v); //原文
t rotate(a,v); //有谁需要句点吗?(t和rotate中间的圆点)
t rotate a,v; //有谁需要括号吗? |
为了进一步改进上面的语法,我们需要知道参数a 和 v 究竟表示什么。让我们假设整个示例的意思是“围绕端点v进行角度为a的旋转”。那么下一步可能如下:
t.rotate by a around v; //有谁需要逗号吗? |
可是如何才能知道上面这个语句中每个单词的意思呢?我们知道,在这个例子当中,t 是一个变量,rotate 是一个类方法的名称,by 是一个分隔符,a 是一个变量,around 又是一个分隔符,最后的 v 是一个变量。为了消除歧义,我们可以假设如下的变换:在所有的分隔符后面添加一个冒号。那么我们就得到下面的句子:
t.rotate by: a around: v; //有谁需要歧义吗? |
最终,我们要求所有的分隔符都是方法名称的一部分。也就是说,我们需要的方法的名称是“rotate by: around:”,最后让我们去掉空格,就成了“rotateby:around:”。我们最好将中间的单词开头大写,于是“rotateBy:around:”,因此我们的例子就变成了:
t.rotateBy: a around: v; //这就是Smalltalk |
也就是说方法名被分成了好几个部分。幸运的是将这些分开的部分想象成一个整体的名字并不困难。当一个类方法被定义的时候,我们可能会写成下面这样:
self rotateBy: angle around: vector
|result|
result := COMPUTE ANWSER.
^result |
在执行的时候,t和self,a和angle,v和vector有一对一的映射关系。需要注意的是^表示返回,相当于return关键字。变量self则相当于this。如果方法的最后没有显式的返回表达式,则却省为^self。也就是说,不写return语句也不会有什么问题。这同时也意味着无论调用者是否需要,类方法都将返回一个对象的引用。
事实上,Smalltalk的语法要求self不能出现在函数名的最前面,如下所示:
rotateBy: angle around: vector
|result|
result:= COMPUTE ANSWER.
^result |
Smalltalk的这种关键字语法的优点就是,针对不同的方法可以使用不同的关键字。例如,我们可以像下面这样定义第二个函数:
t rotateAround: vector by: angle |
没有必要刻意的去记住参数的顺序,因为关键字暗示了他们的顺序。当然,作为编程者也有可能滥用关键字。例如,如果我们像下面这样定义关键字:
<FONT face |