Неглубокое равенство строки Smalltalk/Squeak

Следующий код печатает «false»:

a := 'aaa'.
b := a deepCopy.
Transcript show: (a == b).

Я действительно ожидаю такого поведения, и мое объяснение этому будет заключаться в том, что deepCopy возвращает новый объект «b», который является совершенно другим объектом, чем «a», и поскольку оператор «==» сравнивает по ссылке, результат «ложь». Это правильно?

Однако я не понимаю, почему следующий код выдает "true":

a := 'aaa'.
b := 'aaa'.
Transcript show: (a == b).

Здесь мы сделали два присваивания двум разным объектам, "a" и "b", и между ними не должно быть никакой связи, кроме того факта, что они содержат одно и то же значение. Но если оператор "==" сравнивает по ссылке, а не по значению, то почему результат этого сравнения "истинный"?


person Louis    schedule 12.07.2015    source источник
comment
В первом случае разные экземпляры объекта, поэтому false. Во втором случае компилятор/интерпретатор, очевидно, решил, что a и b будут ссылаться на один и тот же строковый объект, так как значения одинаковы и это экономит память, поэтому true.   -  person lurker    schedule 14.07.2015


Ответы (3)


Одно и то же заблуждение в обоих случаях состоит в том, что вопрос не в том, «что произойдет?», а в том, «что гарантировано?». Суть в том, что нет никакой гарантии, что 'aaa' == 'aaa', но компилятор и виртуальная машина могут так делать. То же самое верно и для случая копирования; поскольку строки неизменяемы, я думаю, нельзя сказать, что копирование строки не может вернуть тот же объект!

В вашем первом примере, как обычно, лучший учитель — изображение. #deepCopy делегирует #shallowCopy, который в какой-то момент оценивает class basicNew: index и копирует символы в новый объект. Итак, эта конкретная реализация всегда будет создавать новый объект.

person Sean DeNigris    schedule 12.07.2015
comment
Хороший ответ. Как правило, на уровне виртуальной машины происходит немногое: виртуальная машина просто выполняет то, что мы ей приказываем, и создает объект, о котором мы говорим. Что-то происходит на стороне изображения в компиляторе. Однако он немного скрыт в переменной экземпляра listSet Encoder... Он содержит PluggableDictionary, использующий #literalEqual: для проверки равенства ключей. Итак, если два литерала считаются равными, то они не дублируются. Действительно, компилятор предполагает, что литералы не будут изменены, хотя технически они изменяемы. - person aka.nice; 13.07.2015

В дополнение к тому, что сказал Шон ДеНигрис, причина, по которой сравнение true во втором случае, заключается в том, что когда вы выполняете все три оператора вместе, компилятор хочет быть умным и только один раз создает объект для '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 литеральные объекты, такие как укусы, хранятся в объекте метода того метода, в котором они определены.

*: Технически каждый DoIt или PrintIt, то есть когда вы просто выполняете код нажатием клавиши, компилируется в один метод в Squeak.

person Tobias    schedule 13.07.2015

Вот что я знаю из одной из бесплатных книг по Smalltalk, разбросанных по сети, но не могу найти ссылку:

Как и следовало ожидать, экземпляр класса является уникальным объектом в памяти. deepCopy намеренно сначала создает объект, а затем сохраняет в нем копию существующего экземпляра.

Однако числа, символы и строки рассматриваются Smalltalk как примитивные типы данных. Когда литеральные данные, также называемые литералами, назначаются переменным, они сначала проверяются по словарю локальной области видимости, который невидим для пользователя и содержит литералы для проверки их соответствия. к нему уже добавлено. Если нет, они будут добавлены в словарь, и переменная будет указывать на поле словаря. Если ранее были назначены идентичные литеральные данные, новая переменная будет указывать только на поле словаря локальной области, которое содержит идентичный литерал. Это означает, что две или более переменных, которым присвоены одинаковые литералы, указывают на одно и то же поле словаря и, следовательно, являются идентичными объектами. Вот почему второе сравнение в вашем вопросе возвращает true.

person walid    schedule 22.07.2015