我是Smalltalk(Squeak)的新用户(实际上是在课程中学习)。 我有一个方法来检查一个矩形是否等于给定的矩形,如下所示:
isEqual:givenRec
self a = givenRec a
ifTrue: [
self b = givenRec b
ifTrue: [
^true
].
^false
].
self b = givenRec a
ifTrue: [
self a = givenRec b
ifTrue: [
^true
].
^false
].
^false
我的问题是 - 有没有办法更好地写这个?为了更紧凑?
同样 - 为什么我不能使用self inside方法引用a
哪个是instanceVariableNames?
谢谢你的帮助!
编辑:
这是课程的定义方式:
MyShape subclass: #MyTriangle
instanceVariableNames: 'a b c'
classVariableNames: ''
poolDictionaries: ''
category: 'Ex2'
MyShape
只是源自Object
而没有任何内容。
答案 0 :(得分:4)
你可以把它变得更紧凑,是的。
首先,除了#ifTrue:
之外,还有#ifFalse:
和#ifTrue:ifFalse:
,大致具有if-not-then和if-then-else的效果。
但是,我们已经有了逻辑AND条件,所以为什么不使用它:
isEqual: givenRec
(self a = givenRec a and: [self b = givenRec b])
ifTrue: [^ true].
(self b = givenRec a and: [self a = givenRec b])
ifTrue: [^ true].
^false
使用#ifTrue:ifFalse:
isEqual: givenRec
(self a = givenRec a and: [self b = givenRec b])
ifTrue: [^ true]
ifFalse: [^ (self b = givenRec a and: [self a = givenRec b])]
此外,我们可以围绕整个声明做出回报:
isEqual: givenRec
^ (self a = givenRec a and: [self b = givenRec b])
ifTrue: [true]
ifFalse: [self b = givenRec a and: [self a = givenRec b]]
但是ifTrue: [true]
有点多余,让我们使用#or:
isEqual: givenRec
^ (self a = givenRec a and: [self b = givenRec b]) or:
[self b = givenRec a and: [self a = givenRec b]]
美好而甜蜜,我们也很容易看到逻辑结构。 (请注意,我偏离常见的格式样式,以指出两个逻辑表达式中的相似点和不同点。)
我们现在只有一个返回^
,没有#ifTrue:…
对于实例变量问题:
当您在类中定义一些实例变量时,您可以在代码中使用它们来访问它们:
Object subclass: #Contoso
instanceVariableNames: 'things'
classVariableNames: ''
poolDictionaries: ''
category: 'Examples'
isThingPlusThreeSameAs: anObject
^ thing + 3 = anObject
但通常情况下,最好通过 getters 和 setters 引用实例变量,通常称为访问者。您必须手动编写它们或使用浏览器中类的第二个上下文菜单(通过“more ...”)的“create inst var accessor”菜单项:
这将生成表单
的访问器方法thing
^ thing
thing: anObject
thing := anObject
你可以在其他方法中使用它们
isThingPlusThreeSameAs: anObject
^ self thing + 3 = anObject
答案 1 :(得分:0)
我会把方法写成
equals: aTriangle
a = aTriangle a ifFalse: [^false].
b = aTriangle b ifFalse: [^false].
^c = aTriangle c
请注意我使用选择器#equals:
而不是#isEqual:
的方式,因为后者在英语中读得不好(如果它已经#isEqualTo:
1}},但#equals:
更短。)
我在此添加的另一个评论是,您可以重新定义#=
,而不是添加新的比较选择器。但是,在这种情况下,你必须注意其他一些事情:
= aTriangle
self class = aTriangle class ifFalse: [^false].
a = aTriangle a ifFalse: [^false].
b = aTriangle b ifFalse: [^false].
^c = aTriangle c
课程检查的原因是为了确保#=
是健壮的,即它没有发出MessageNotUnderstood
异常的信号。另一件事是,每当你(重新)定义#=
时,你还应该(重新)定义#hash
,t1 hash = t2 hash
每当t1 = t2
。例如
hash
^(a hash + b hash + c hash) hash
请注意,#hash
的此提案并不是最佳提案,但这不是讨论编写好的#hash
函数问题的地方。
<强>更新强>
当订单无关紧要时,这是我的版本
equals: aTriangle
| sides |
self = aTriangle ifTrue: [^true].
sides := Set with: a with: b with: c.
(sides includes: aTriangle a) ifFalse: [^false].
(sides includes: aTriangle b) ifFalse: [^false].
^(sides includes: aTriangle c)
第一个比较是在订单相同时加快避免Set
的方法。
答案 2 :(得分:0)
你可能不应该这样写,但有些情况下元编程很有用
isEqual: givenRec
#(a b c) do: [ :selector |
(self perform: selector) = (givenRec perform: selector) ifFalse: [
^false]].
^true