Smalltalk超~简明教学教材.doc

上传人:小** 文档编号:631611 上传时间:2019-04-22 格式:DOC 页数:16 大小:45.82KB
返回 下载 相关 举报
Smalltalk超~简明教学教材.doc_第1页
第1页 / 共16页
Smalltalk超~简明教学教材.doc_第2页
第2页 / 共16页
点击查看更多>>
资源描述

《Smalltalk超~简明教学教材.doc》由会员分享,可在线阅读,更多相关《Smalltalk超~简明教学教材.doc(16页珍藏版)》请在得力文库 - 分享文档赚钱的网站上搜索。

1、#*Smalltalk 超简明教程超简明教程Simberson 公司的 David Buck 撰写的此文档是为 C/C+/C#/Java 程序员介绍 Smalltalk 的, 而且已经得到他善意的许可而得以发布。如果你对正式的培训有兴趣请联系 Simberson,咨询 Smalltalk 培训。 我听说很多 Java 和 C#程序员说他们看不懂 Smalltalk 代码。Smalltalk 因为只有很少的语法, 所以实际上它非常的简单。Smalltalk 的强大之处是它的环境和类库。 本文意图通过代码对比,使 Java 和 C#程序员迅速理解 Smalltalk。 Copyright Davi

2、d Buck 2005 临时变量 Smalltalk 不需要变量的类型声明。临时变量通过两个竖线来定义JavaSmalltalkint a; char b; float c;| a b c | 赋值 Smalltalk 使用 := 赋值JavaSmalltalka = 5;a := 5 消息 Smalltalk 有三种消息类型形式参数例子一元运算符以小写字母开始的字符和数字0squared二元运算符连接符1+关键字使用多个冒号终结文字和数字字符1 或者多个do:,between: and:传输一个或多个参数时,需要使用关键字消息。每一个参数前面需要添加关键字。 Smalltalk 不使用和;分

3、割参数。例子:JavaSmalltalkmyAccount.getBalance(); myAccount.setBalance (10); myAccount.transfer(20,anotherAccount) myAccount.equals (anotherAccount);myAccount getBalance myAccount setBalance: 10 myAccount transfer: 20 to:anotherAccount myAccount = anotherAccount#* 运算优先级: 一元运算符(首先运算) 二元运算符(其次运算) 关键字(最后运算)

4、在同等优先级下,运算是从左到右JavaSmalltalk3 + 5 * 6 /答案是 333 + 5 * 6 “答案是:48”注意 Smalltalk 版本,这个表达式实际上是两个消息: 消息 1 接受者:3 消息: + 参数:5 结果: 8消息 2 接受者: 8 消息: * 参数: 6 结果:48 语句 Smalltalk 使用句号分割语句。最后一条语句不需句号。JavaSmalltalkmyAccount.deposit(20); myAccount.transfer(20,anotherAccount);myAccount deposit: 20. myAccount transfer:

5、 20 to:anotherAccount 常量 Smalltalk 中整数类型,字符类型,字符串类型,布尔类型,浮点数类型和双精度浮点数类型都是 头等类对象。整数类型是无限精度的,会按需自动增长,而不会溢出(译者注:跟内存大小有关) 。 实际上,Smalltalk 中没有 char 类型,byte 类型,short 类型,int 类型或者 long 类型的相 对应的类型。它们都是整数类型。JavaSmalltalk55012308r12300x7f16r7f3r21012 (你可以选择你想用的基数)200L2e-52e-52e-5d2d-5h$hu03A9Character value: 1

6、6r3A9“hello“hello“cant“cant#* 特定词 Smalltalk 中,nil 引用一个真实的对象,它是 UndefinedObject 类的实例。true 是 True 类 的实例,flase 是 Flase 类的实例JavaSmalltalkthisselfnullniltruetruefalsefalsesuper base (C#)super 方法返回JavaSmalltalkreturn value;value 级联JavaSmalltalkmyAccount deposit: 20; transfer: 20 to: anotherAccount 注释JavaS

7、malltalk/* comment */ / another comment“comment“ 实例创建 Smalltalk 中,类是真实的对象。创建实例,只需向 class 发送 new。针对类的方法称为类方法 (类似于 Java 的 static methods) 。JavaSmalltalknew Reservation();Reservation new 构造函数 Smalltalk 没有构造函数。如果你想执行实例初始化,你可以重定义“new”类方法来初始化实例。JavaSmalltalkReservation() startTime = new GregorianCalendar(

8、).getTime(); endTime = new GregorianCalendar().getTime(); Reservation class methods: new super new initialize Reservation instance method: initialize startTime := Timestamp now. endTime := Timestamp now #* 块 Smalltalk 称对象为块,它是包含可执行代码的对象。Java 中最相似的东西是匿名内部类,C# 2.0中,跟匿名托管相类似。执行“块”无需参数,可以向它发送一个 value 消息

9、 Smalltalk| block | block := 3 + 4. block value “answer is 7“块可以有参数,每一个块参数的声明必须以冒号(:)开头,以竖线(|)表示参数列表的结束和块 代码的开始。 Smalltalk| block | block := :x :y | x * 2 + y. block value: 5 value: 3“answer is 13“ 语法结束 到此为止,我们已经介绍了 Smalltalk 所有的语法。其余的事情属于类库部分。你是否注意到我们 是否忘记了某些事情?if-then-else 或者 while 循环?Smalltalk 只使

10、用块和普通的消息发送。 控制结构 Smalltalk 没有在语言中内置控制结构。Smalltalk 使用向 true 或者 flase 对象发生消息作为 替代。JavaSmalltalkif (tries 5) return “Too many tries“; else return “Trying again“;tries 5 ifTrue: Too many tries ifFalse: Trying again注意表示从方法返回,不只是适用于块。 循环 Smalltalk 使用块做循环。由于块是对象,所有我们可以先他们发生消息JavaSmalltalkint tries = 0; whi

11、le (tries 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 程序员都会使用能暗示变量类型的变量名。因此,上面的

12、 申明我们通常如下表示: | aTransformation angle aVector| 但是后面请允许我继续使用先前的变量名,因为杂志专栏给我可利用的版面很小。让我们进一步消 除那些不必要的东西来继续“优化”C+和 Java 的语法。例如,下面的代码应该仍然很好理解: t.rotate(a,v); /原文 t rotate(a,v); /有谁需要句点吗?(t 和 rotate 中间的圆点) t rotate a,v; /有谁需要括号吗? 为了进一步改进上面的语法,我们需要知道参数 a 和 v 究竟表示什么。让我们假设整个示例的意思 是“围绕端点 v 进行角度为 a 的旋转” 。那么下一步可

13、能如下: t.rotate by a around v; /有谁需要逗号吗? 可是如何才能知道上面这个语句中每个单词的意思呢?我们知道,在这个例子当中,t 是一个变量, rotate 是一个类方法的名称,by 是一个分隔符,a 是一个变量,around 又是一个分隔符,最后的 v 是一个变量。为了消除歧义,我们可以假设如下的变换:在所有的分隔符后面添加一个冒号。那么我们 就得到下面的句子: t.rotate by: a around: v; /有谁需要歧义吗? 最终,我们要求所有的分隔符都是方法名称的一部分。也就是说,我们需要的方法的名称是“rotate by: around:” ,最后让我们

14、去掉空格,就成了“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 和 vecto

15、r 有一对一的映射关系。需要注意的是表示返回, 相当于 return 关键字。变量 self 则相当于 this。如果方法的最后没有显式的返回表达式,则却省为 self。也就是说,不写 return 语句也不会有什么问题。这同时也意味着无论调用者是否需要,类方法 都将返回一个对象的引用。 事实上,Smalltalk 的语法要求 self 不能出现在函数名的最前面,如下所示: rotateBy: angle around: vector|result|result:= COMPUTE ANSWER.result Smalltalk 的这种关键字语法的优点就是,针对不同的方法可以使用不同的关键字。

16、例如,我们可 以像下面这样定义第二个函数: t rotateAround: vector by: angle 没有必要刻意的去记住参数的顺序,因为关键字暗示了他们的顺序。当然,作为编程者也有可能滥 用关键字。例如,如果我们像下面这样定义关键字: t rotate: angle and: vector 很明显,使用这个函数的人无法通过关键字确定参数的顺序。这是很不好的编程风格。如果只有一 个参数的话,则无所谓。但是我们仍然需要只有一个关键字的方法名: #* t rotateAroundXBy: angle t rotateAroundYBy: angle我更倾向于将关键字理解为一种说明性的参数。

17、但是如果没有参数的时候该怎么办呢? t makeIdentity: /Can a colon at the end make sense? 如果关键字是说明性参数,而实际的参数却没有的话,那么我们就不是用关键字。因此一个没有关 键字的消息(类方法)将写成下面这样: t makeIdentity /This is Smalltalk 当然,二元操作符(也是类方法,类似 C+语言的 operator)的定义中也可以使用关键字,但是一 元操作符不用(上面的 makeIdentity 是一元消息,不是一元操作符) 。当很多消息同时使用的时候,我 们可能得到下面这样一个表达式: a negative |

18、 (b between: c and: d)ifTrue: a := c negated 作为读者,我相信你现在知道消息 negative 被发送到对象 a(无参数),然后 true 或者 false 将被 返回;between: c and: d 被发送到对象 b,并返回 true 或者 false。然后对这两个返回结果进行或 运算,得到的结果对象被作为 ifTrue:a:= c negated消息的接受者。这是一个 if-then 条件表达式, 但是并不像 C+或者 Java 那样需要使用特别的语法。 在 Smalltalk 中,这跟其他的类方法调用的语法没有任何差别,只不过消息的接收者是

19、一个布尔对 象,而消息的关键字是 ifTrue,参数是a:= c negated(这实际上是一个 block 对象) 。 在 Smalltalk 中你将不会看到 a:= -c 这样的表达式,因为没有-这样的一元操作符。但是可以 写成 5 这样的表达式,但是这里的负号被作为常量定义的组成部分。因此,当你看到诸如“-3.5 negated truncated factorial”这样的表达式时,你应该立即认识到这里没有关键字。因此-3.5 必 须作为 negated 消息的接收者;而得到的结果 3.5 则被作为 truncated 消息的接收者;进一步得到的 结果 3 将作为 factorial

20、消息的接收者,最终得到结果 6。 当然,这里也存在优先级的问题,通常是从左到右,一次元消息优先级最高,多参数消息的优先级 最低。对于编程的人来说这很重要,但是对于读者来说这并不影响其阅读和理解。哦,对了从左到右的 优先级实际上意味着: 1+2*3 等于 9 操作符之间没有优先级之分。当然你也可能会看到下面这样的表达式,那是因为编写者知道不这样 写可能会给不太熟悉 Smalltalk 的阅读者造成困扰: (1+2)*3 尽管这里的括号并不必要。分号与句号的不同分号与句号的不同 大多数的非 Smalltalk 读者可能会认为分号是表达式的终结符,但在 Smalltalk 中使用的是句号。 因此我们

21、不会写下面这样的表达式: account deposit: 100 dollars; collection add: transformation; 正确的写法应该是下面这样: account deposit: 100 dollars. collection add: transformation. 哦,你可能会很奇怪这里怎么可能有一个 dollars 消息,其实这没什么特别的。为了使这个表达式 合法,Integer 类中必须有一个 dollars 方法的定义(100 将被当作 Integer 对象处理)。虽然在标准的 Smalltalk 环境中并没有这样的定义,但是我们可以自己添加。Smal

22、ltalk 的基类可以简单的通过定义 新的继承类来扩展。#*因此,句号是表达式的终结符,但是在最后一行表达式后面也可以省略(因此你也可以把句号当成 表达式之间的分隔符) 。但是分号也是合法的,他用在层跌消息语法中(往一个对象中一次发送多个对象 的语法) 。#*例如: | p | p := Client new. p name: Jack. p age: 32. p address: Earth. 对于上面这样一个表达式我们应该如下编写: | p | p := Client new. p name: Jack; age: 32; address: Earth. 或者干脆写成: | p | p :

23、= Client new name: Jack; age: 32; address: Earth. 上面的例子中格式并没太大关系,只要愿意我们甚至可以把所有的句子都写成一行。很关键的分号 指出了前一条消息发送到接受对象并使其状态发生变化之后,下一条消息将被继续发到这个接受对象。 (而不是发到上一条消息的返回值对象,上条消息的返回值将被抛弃或者忽视) 。 在最后一个例子当中,new 被发送到类来生成一个实例(作为返回值) 。然后“name:Jack ”被发 送到这个实例。第一个分号表示“name:Jack ”消息返回的结果将被忽略,紧接着“age:32”被发送 到先前的实例。第二个分号表示“ag

24、e:32”返回的结果被忽略,紧接着“address:Earth ”被发送 到先前的实例。最后, “address:Earth ”返回的结果被保存到变量 p。修改接收者属性的类方法通 常都返回接收者自身。因此变量 p 就最终被绑定到了被修改了好几次的新生成的类实例对象上。 我们可以将上面的分号替换成“AND ALSO”就会感觉很容易理解这段代码的意思了。在 Smalltalk 中类似这样向同一个对象连续发送消息的语法称作层跌消息。分号也可以在子表达式中使用,例如“p := (Client new name: Jack; age: 32; address: Earth)”注意这里的括号。Get/S

25、et 方法使用与实例变量相同的名称方法使用与实例变量相同的名称 类似 name,age,address 这样的类实例变量在 Smalltalk 中全都属于私有变量。但是类方法可以提 供对这些变量的访问。在 C+中(Java 也类似) ,例如,我们可以定义下面这样的访问类实例变量的方法:long getAge () return age; void setAge (long newAge) age = newAge; 如果你有好几打的类,并且你使用上面这样的编码约定,你将会发现最终你的代码中有一大堆的以 get 和 set 打头的类方法。当然,如果你碰巧决定通过去掉这些重复的前缀以便让类方法名短

26、些,你会 发现 C+的编译器将会无法编译,因为他无法从名称区分变量和函数。但是 Java 的编译器对于下面这样 的代码则没有什么问题。 long age () return age; void age (long newAge) age = newAge; 你能区分变量 age 和消息 age 吗?你应该可以。当你使用这条消息的时候,你需要加上括号,就像“age()或者 age(22)” ;当你引用这些变量的时候,则不需要使用括号。同样的类方法在 Smalltalk 中 可以写成下面这样: #* age age age: newAge age := newAge不过,我们通常通过写成两行来让代

27、码更可读: age age age: newAge age := newAge 在 Smalltalk 中没有括号可以帮助你区分变量和消息,但是要区分也不是很难。如果你行的话,应 该可以看出下面这句话中哪些是变量,哪些是方法: age age age: age age + age age 好吧,答案是 3;第一个 age 必然是一个变量,第四个也是(每个关键字后面都必须是一个子表达 式;所有的表达式都必须以变量开头) ,还有第七个(在一个二元操作符后面,也必须是一个子表达式) 。 用更简单典型的例子应该更容易理解,如下: name size /name 必然是一个变量 self name /n

28、ame 必然是一个类方法 Collection 的广泛使用的广泛使用The two most heavily used types of collections in Smalltalk are ordered collection and dictionaries. Arrays are conceptually ordered collections that cant change size. | a b | a := OrderedCollection new add: #red; add: #green; yourself. b := Dictionary new at: #red

29、put: #rouge; at: #green put: #vert; yourself. In each of the above assignments, the variable is bound to the result of the last message executed; i.e., the result of “yourself” which is, hopefully, the newly created collection. Message “yourself” is designed to return the receiver (almost like a no-

30、op) but “add:” and “at:put:” do not (they return their last parameter). So without “yourself”, we would bind “a” to #green” and “b” to #vert. I deliberately used cascading above in order to explain why “yourself” is used at all since it appears in isolated methods in the built-in classes. The big ad

31、vantage to Smalltalk collections is that you can put any objects whatsoever in the collections. Even the keys in dictionaries can be any type of object. Also, the objects in a specific collection dont have to be the same type; they can all be different types. We dont need to invent new kinds of coll

32、ections just because we want to accumulate instances of a brand new class. Ordered collections can be accessed like arrays; e.g., “a at: 1” indexed starting at 1. Dictionaries are accessed the same way too; e.g., “b at: #red”. But there are many applications where we dont need to deal with the keys.

33、 For these, there are very simply looping facilities that iterate through the items; e.g.,#* a do: :item | USE item FOR SOMETHING. b do: :item | USE item FOR SOMETHING. Loop variable “item” gets the elements of the collection one-by-one even if they are all different types. If necessary, we can easi

34、ly find out what something is at execution time; e.g., “item isKindOf: Boat” returns true or false. There are also many special-case query messages like “item isCollection” or “item isNumber”. There are also many looping constructs that create and return new collections such as c := clients select:

35、:client | client netWorth 500000. d := clients collect: :client | client name. In the first case, we get a subcollection of those clients that are pretty rich. In the second, we get a collection of names (the original collection was a collection of clients).序列化抽象无须创建新的序列化抽象无须创建新的 class Every now and

36、 then, a reader will find code that looks like stuff value: x value: y value: z where the keywords are all “value:”. This clearly looks useless and bewildering to a non-Smalltalk reader. What is happening is that the programmer has (typically) created a new abstraction facility. Let me explain this

37、fundamental capability that only Smalltalk supports with a challenge. Given that we have already introduced the Client class earlier, suppose that we want a simple facility for looping through an individual clients parts; i.e., we want the loop to first give us the name, then the next time around, w

38、e want the age, and finally we want the address. The conventional solution in C+ and Java is to create a special stream class or enumerator, say called ClientIterator, with facilities to initialize it, to ask it if it is at the end, and, if not, to ask for the next object in the iterator. Then we ca

39、n have a loop that sets up the iterator and, while not at the end, gets the next object and processes it. The advantage of the iterator is that it can provide its own variables for keeping track of where its at in the sequencing process; i.e., its not necessary to extend the Client class with “tempo

40、rary” instance variables needed for iterating. An example piece of code that might make use of the intended abstraction is shown below: aClient := CODE TO GET ONE. aClient partsDo: :object | object printOn: Transcript Notice that partsDo: looks like a looping construct where object is the loop varia

41、ble. The first iteration, we get the name and print it on the transcript (a special workspace in the Smalltalk programming environment). Then we get the age in the second iteration and finally the address in the third. Also note that “partsDo:” is a keyword message where “aClient” is the receiver an

42、d “:object | object printOn:Transcript” (a block) is the parameter.#* Before we go too far, let me just show you the Smalltalk solution. Then Ill explain how it works and provide more sophisticated examples. All we do is add one method to class Client as follows: partsDo: aBlock aBlock value: self n

43、ame. aBlock value: self age. aBlock value: self address. The clue to understanding this is the realization that blocks are nameless functions. To better understand what I mean, suppose that we want to assign a function to a variable BUT WE DONT WANT TO CALL IT. Let me make up the syntax for a C-like

44、 language as follows (I do know the correct syntax for doing this in C but it doesnt help to elucidate the important idea; so Im not using it): a = f (x) return x + 1; /C-Like syntax a := :x | x + 1 /Smalltalk syntax Now variable “a” is the function object itself. Since f is a function, we might exp

45、ect to be able to call it with the notation “f (10)” to get back 11. But we should also be able to call it by executing “a (10)” because as value is the function. Executing the function through variable “a” doesnt require any knowledge of its original name. So in Smalltalk, we dont even bother with

46、a name for the function. We simply assign it to any variable and use it through that variable. To “call” the function, we unfortunately dont have the nice notation used above. We have to settle for “a value: 10” which returns 11. During execution, parameter x is bound to 10, x + 1 is computed, and t

47、he end of the block is reached. When that happens, the last value computed is returned. In general, we rarely execute blocks directly. Instead, we write abstractions like “partsDo:” above and hide the cumbersome “call” to the block in the method providing the abstraction. Consider some more examples

48、. Suppose we have an Airplane class that maintains a list of passengers. Lets just suppose that we want to sequence through those passengers that are kids (suppose we define kids to be 12 years old or younger). Then a piece of code for doing that might look like anAirplane passengers do: :person | person age 4 消息的运算优先级 无参数消息 符号消息 带参数消息 用()可以修改运算顺序,同优先级消息从左到右运算。 文法 数字 十进制整数 43 -34 二进制整数 2r101111 16 进制整数 16rffff 浮点数 123.432 1.3e3 字符 $a $b $1 字符串以单引号标示

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 教育专区 > 教案示例

本站为文档C TO C交易模式,本站只提供存储空间、用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。本站仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知得利文库网,我们立即给予删除!客服QQ:136780468 微信:18945177775 电话:18904686070

工信部备案号:黑ICP备15003705号-8 |  经营许可证:黑B2-20190332号 |   黑公网安备:91230400333293403D

© 2020-2023 www.deliwenku.com 得利文库. All Rights Reserved 黑龙江转换宝科技有限公司 

黑龙江省互联网违法和不良信息举报
举报电话:0468-3380021 邮箱:hgswwxb@163.com