Smalltalk / Squeak 字符串浅相等
Smalltalk / Squeak string shallow equality
以下代码打印 "false":
a := 'aaa'.
b := a deepCopy.
Transcript show: (a == b).
我确实预料到这种行为,我对此的解释是 deepCopy returns 一个新对象 "b" 与 "a" 完全不同,因为运算符“== " 对比结果为 "false"。对吗?
但是,我不明白为什么下面的代码会产生"true":
a := 'aaa'.
b := 'aaa'.
Transcript show: (a == b).
这里我们对两个不同的对象进行了两次赋值,"a"和"b",它们之间应该没有任何关系,除了它们包含相同的值。但是如果运算符“==”是按引用比较而不是按值比较,为什么这个比较的结果是"true"呢?
两种情况下同样的误解是问题不是"what happens?",而是"what is guaranteed?"。关键是不能保证 'aaa' == 'aaa'
,但编译器和 VM 可以自由地以这种方式做事。复制的情况似乎也是如此。由于字符串是不可变的,我想没有什么可以说复制字符串不能 return 同一个对象!
在你的第一个例子中,像往常一样,最好的老师就是形象。 #deepCopy
委托给 #shallowCopy
,它在某个时候计算 class basicNew: index
,并将字符复制到新对象中。因此,这个特定的实现将始终创建一个新对象。
除了Sean DeNigris所说的,第二种情况比较true
的原因是,当你一起执行所有三个语句时,编译器想要聪明,只once 为 'aaa'
创建对象并为 a
和 b
共享它们。
如果将其放入一个方法中,也会发生同样的情况 *:
Object subclass: #MyClassA
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'MyApp'
!MyClassA methodsFor: 'testing' stamp: nil prior: nil!
testStrings
| a b |
a := 'aaa'
b := 'aaa'
^ a == b
! !
MyClassA testStrings " ==> true"
但如果它们采用不同的方法,不会发生这种情况:
Object subclass: #MyClassB
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'MyApp'
!MyClassB methodsFor: 'testing' stamp: nil prior: nil!
a
| a |
a := 'aaa'
^ a
! !
!MyClassB methodsFor: 'testing' stamp: nil prior: nil!
b
| b |
b := 'aaa'
^ b
! !
!MyClassB methodsFor: 'testing' stamp: nil prior: nil!
testStrings
^ self a == self b
! !
MyClassB testStrings " ==> false"
那是因为在 Squeak 中,像 stings 这样的文字对象存储在它们定义的方法的方法对象中
*:从技术上讲,每个 DoIt
或 PrintIt
,即当您仅通过击键执行代码时,都会在 Squeak 中编译为一个方法。
这是我从散布在网上的一本免费 Smalltalk 书籍中了解到的,但我找不到参考资料:
如您所料,class 的实例是内存中的唯一对象。 deepCopy 故意先创建一个对象,然后将现有实例的副本存储在其中。
然而,数字、字符和字符串被 Smalltalk 视为原始数据类型。当 literal 数据(也称为 literals)分配给变量时,首先会根据用户不可见的本地范围字典检查它们,并且保存文字以检查它们是否已添加到其中。如果没有,它们将被添加到字典中,并且变量将指向字典字段。如果之前已经分配了相同的文字数据,则新变量将仅指向包含相同文字的本地作用域字典字段。这意味着分配相同文字的两个或多个变量指向相同的字典字段,因此是相同的对象。这就是为什么你的问题中的第二个比较返回 true。
以下代码打印 "false":
a := 'aaa'.
b := a deepCopy.
Transcript show: (a == b).
我确实预料到这种行为,我对此的解释是 deepCopy returns 一个新对象 "b" 与 "a" 完全不同,因为运算符“== " 对比结果为 "false"。对吗?
但是,我不明白为什么下面的代码会产生"true":
a := 'aaa'.
b := 'aaa'.
Transcript show: (a == b).
这里我们对两个不同的对象进行了两次赋值,"a"和"b",它们之间应该没有任何关系,除了它们包含相同的值。但是如果运算符“==”是按引用比较而不是按值比较,为什么这个比较的结果是"true"呢?
两种情况下同样的误解是问题不是"what happens?",而是"what is guaranteed?"。关键是不能保证 'aaa' == 'aaa'
,但编译器和 VM 可以自由地以这种方式做事。复制的情况似乎也是如此。由于字符串是不可变的,我想没有什么可以说复制字符串不能 return 同一个对象!
在你的第一个例子中,像往常一样,最好的老师就是形象。 #deepCopy
委托给 #shallowCopy
,它在某个时候计算 class basicNew: index
,并将字符复制到新对象中。因此,这个特定的实现将始终创建一个新对象。
除了Sean DeNigris所说的,第二种情况比较true
的原因是,当你一起执行所有三个语句时,编译器想要聪明,只once 为 'aaa'
创建对象并为 a
和 b
共享它们。
如果将其放入一个方法中,也会发生同样的情况 *:
Object subclass: #MyClassA
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'MyApp'
!MyClassA methodsFor: 'testing' stamp: nil prior: nil!
testStrings
| a b |
a := 'aaa'
b := 'aaa'
^ a == b
! !
MyClassA testStrings " ==> true"
但如果它们采用不同的方法,不会发生这种情况:
Object subclass: #MyClassB
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'MyApp'
!MyClassB methodsFor: 'testing' stamp: nil prior: nil!
a
| a |
a := 'aaa'
^ a
! !
!MyClassB methodsFor: 'testing' stamp: nil prior: nil!
b
| b |
b := 'aaa'
^ b
! !
!MyClassB methodsFor: 'testing' stamp: nil prior: nil!
testStrings
^ self a == self b
! !
MyClassB testStrings " ==> false"
那是因为在 Squeak 中,像 stings 这样的文字对象存储在它们定义的方法的方法对象中
*:从技术上讲,每个 DoIt
或 PrintIt
,即当您仅通过击键执行代码时,都会在 Squeak 中编译为一个方法。
这是我从散布在网上的一本免费 Smalltalk 书籍中了解到的,但我找不到参考资料:
如您所料,class 的实例是内存中的唯一对象。 deepCopy 故意先创建一个对象,然后将现有实例的副本存储在其中。
然而,数字、字符和字符串被 Smalltalk 视为原始数据类型。当 literal 数据(也称为 literals)分配给变量时,首先会根据用户不可见的本地范围字典检查它们,并且保存文字以检查它们是否已添加到其中。如果没有,它们将被添加到字典中,并且变量将指向字典字段。如果之前已经分配了相同的文字数据,则新变量将仅指向包含相同文字的本地作用域字典字段。这意味着分配相同文字的两个或多个变量指向相同的字典字段,因此是相同的对象。这就是为什么你的问题中的第二个比较返回 true。