Using #deepCopy, I have trouble understanding why it the code fails.

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
4 messages Options
Reply | Threaded
Open this post in threaded view
|

Using #deepCopy, I have trouble understanding why it the code fails.

Intrader Intrader
The code I have trouble with is in my workspace where I execute via 'DoIt'
<code>
|contacts copy|
contacts := MyContact contacts.
copy := contacts deepCopy.
(contacts = copy) ifTrue: [true inspect]
ifFalse: [false inspect]
</copy>
When I use #copy instead of #deepCopy the code correctly enters the ifTrue path. Both contacts and copy show the same under inspection and explore.
all inst vars:
array:     an Array(nil nil a MyContact a MyContact nil nil nil nil nil nil)
firstIndex:     3
lastIndex:     4

Class methods for MyContact:
<code>
contacts
    "Answers the storage for contacts"
    Database isNil ifTrue: [self createSampleDatabase].
    ^ Database
createSampleDatabase
    Database := (OrderedCollection new)
        add: (self name: 'Bob Jones' emailAddress: '[hidden email]');
        add: (self name: 'Steve Smith' emailAddress: '[hidden email]');
        yourself
</code>
Database is an instance variable of MyContact.
The code failing is in the method
<code>
hasEqualElements: otherCollection
    "Answer whether the receiver's size is the same as otherCollection's
    size, and each of the receiver's elements equal the corresponding
    element of otherCollection.
    This should probably replace the current definition of #= ."

    | size |
    (otherCollection isKindOf: SequenceableCollection) ifFalse: [^ false].
    (size := self size) = otherCollection size ifFalse: [^ false].
    1 to: size do:
        [:index |
        (self at: index) = (otherCollection at: index) ifFalse: [^ false]].
    ^ true
= anObject
    "Answer whether the receiver and the argument represent the same
    object. If = is redefined in any subclass, consider also redefining the
    message hash."

    ^self == anObject
</code>
The '#=' code executes a '#==' and fails. Why is it excuting '#==' ? Obviously it would fail for deepCopy.

Thanks



_______________________________________________
vwnc mailing list
[hidden email]
http://lists.cs.uiuc.edu/mailman/listinfo/vwnc
Reply | Threaded
Open this post in threaded view
|

Re: Using #deepCopy, I have trouble understanding why it the code fails.

Ladislav Lenart
Hello.

The code eventually executes #== (inherited from Object) to
compare two MyContact instances, because MyContact does not
define its own version of #=. You can of course implement
it to something like this:

MyContact>>= anObject

     ^self class == anObject class
         and: [name = anObject name
         and: [emailAddress = anObject emailAddress]].


And, as noted in Object>>=, you should define your own
version of #hash as well if you do so:

MyContact>>hash

     ^(self class identityHash bitXor: name hash) bitXor: emailAddress hash.


All objects for which #= evaluates to true must have the
same #hash value. The #hash is used for quick lookup in
Set and Dictionary collections.


HTH,

Ladislav Lenart


On 3.5.2011 06:09, intrader wrote:

> The code I have trouble with is in my workspace where I execute via 'DoIt'
> <code>
> |contacts copy|
> contacts := MyContact contacts.
> copy := contacts deepCopy.
> (contacts = copy) ifTrue: [true inspect]
> ifFalse: [false inspect]
> </copy>
> When I use #copy instead of #deepCopy the code correctly enters the ifTrue path. Both contacts and copy show the same under inspection and explore.
> all inst vars:
> array: an Array(nil nil a MyContact a MyContact nil nil nil nil nil nil)
> firstIndex: 3
> lastIndex: 4
>
> Class methods for MyContact:
> <code>
> contacts
> "Answers the storage for contacts"
> Database isNil ifTrue: [self createSampleDatabase].
> ^ Database
> createSampleDatabase
> Database := (OrderedCollection new)
> add: (self name: 'Bob Jones' emailAddress: '[hidden email]');
> add: (self name: 'Steve Smith' emailAddress: '[hidden email]');
> yourself
> </code>
> Database is an instance variable of MyContact.
> The code failing is in the method
> <code>
> hasEqualElements: otherCollection
> "Answer whether the receiver's size is the same as otherCollection's
> size, and each of the receiver's elements equal the corresponding
> element of otherCollection.
> This should probably replace the current definition of #= ."
>
> | size |
> (otherCollection isKindOf: SequenceableCollection) ifFalse: [^ false].
> (size := self size) = otherCollection size ifFalse: [^ false].
> 1 to: size do:
> [:index |
> (self at: index) = (otherCollection at: index) ifFalse: [^ false]].
> ^ true
> = anObject
> "Answer whether the receiver and the argument represent the same
> object. If = is redefined in any subclass, consider also redefining the
> message hash."
>
> ^self == anObject
> </code>
> The '#=' code executes a '#==' and fails. Why is it excuting '#==' ? Obviously it would fail for deepCopy.
>
> Thanks
>
>
>
>
> _______________________________________________
> vwnc mailing list
> [hidden email]
> http://lists.cs.uiuc.edu/mailman/listinfo/vwnc


_______________________________________________
vwnc mailing list
[hidden email]
http://lists.cs.uiuc.edu/mailman/listinfo/vwnc
Reply | Threaded
Open this post in threaded view
|

Re: Using #deepCopy, I have trouble understanding why it the code fails.

Holger Guhl
  The answer is simple.
Simple #copy prefers to do #shallowCopy, i.e. make a copy where all inst.vars are identical to the
original. A #deepCopy creates new objects on every level in the depth, until all nodes in the
original object graph have copied counterparts.
When you make a comparison, the default test is for identity. If you have your own implementation of
#=, then you will check equality of various inst.vars. But it is very likely that your object graph
contains instances from "system" classes, i.e. classes that are not from your domain. These classes
have their own understanding of being equal, in particular those that simply inherit #= from Object
with "^self == anObject", then the equality test *must* fail at a certain level.
This means that "x = x deepCopy" should return false *in general*, unless the object graph of x
contains only instances from your domain or from "simple" data types, such as Number, String, Date, etc.
Smalltalk is such a wonderful environment where you can learn how the equality test is done by
simple use of the Debugger. Just run "x = x deepCopy" with your favorite object x, and use "Debug
it" instead of "Do it". Follow the flow by stepping into each comparison and wait for your "aah" effect.

Cheers
Holger

Am 03.05.2011 10:56, schrieb Ladislav Lenart:

> Hello.
>
> The code eventually executes #== (inherited from Object) to
> compare two MyContact instances, because MyContact does not
> define its own version of #=. You can of course implement
> it to something like this:
>
> MyContact>>= anObject
>
>       ^self class == anObject class
>           and: [name = anObject name
>           and: [emailAddress = anObject emailAddress]].
>
>
> And, as noted in Object>>=, you should define your own
> version of #hash as well if you do so:
>
> MyContact>>hash
>
>       ^(self class identityHash bitXor: name hash) bitXor: emailAddress hash.
>
>
> All objects for which #= evaluates to true must have the
> same #hash value. The #hash is used for quick lookup in
> Set and Dictionary collections.
>
>
> HTH,
>
> Ladislav Lenart
>
>
> On 3.5.2011 06:09, intrader wrote:
>> The code I have trouble with is in my workspace where I execute via 'DoIt'
>> <code>
>> |contacts copy|
>> contacts := MyContact contacts.
>> copy := contacts deepCopy.
>> (contacts = copy) ifTrue: [true inspect]
>> ifFalse: [false inspect]
>> </copy>
>> When I use #copy instead of #deepCopy the code correctly enters the ifTrue path. Both contacts and copy show the same under inspection and explore.
>> all inst vars:
>> array: an Array(nil nil a MyContact a MyContact nil nil nil nil nil nil)
>> firstIndex: 3
>> lastIndex: 4
>>
>> Class methods for MyContact:
>> <code>
>> contacts
>> "Answers the storage for contacts"
>> Database isNil ifTrue: [self createSampleDatabase].
>> ^ Database
>> createSampleDatabase
>> Database := (OrderedCollection new)
>> add: (self name: 'Bob Jones' emailAddress: '[hidden email]');
>> add: (self name: 'Steve Smith' emailAddress: '[hidden email]');
>> yourself
>> </code>
>> Database is an instance variable of MyContact.
>> The code failing is in the method
>> <code>
>> hasEqualElements: otherCollection
>> "Answer whether the receiver's size is the same as otherCollection's
>> size, and each of the receiver's elements equal the corresponding
>> element of otherCollection.
>> This should probably replace the current definition of #= ."
>>
>> | size |
>> (otherCollection isKindOf: SequenceableCollection) ifFalse: [^ false].
>> (size := self size) = otherCollection size ifFalse: [^ false].
>> 1 to: size do:
>> [:index |
>> (self at: index) = (otherCollection at: index) ifFalse: [^ false]].
>> ^ true
>> = anObject
>> "Answer whether the receiver and the argument represent the same
>> object. If = is redefined in any subclass, consider also redefining the
>> message hash."
>>
>> ^self == anObject
>> </code>
>> The '#=' code executes a '#==' and fails. Why is it excuting '#==' ? Obviously it would fail for deepCopy.
>>
>> Thanks
>>
>>
>>
>>
>> _______________________________________________
>> vwnc mailing list
>> [hidden email]
>> http://lists.cs.uiuc.edu/mailman/listinfo/vwnc
>
> _______________________________________________
> vwnc mailing list
> [hidden email]
> http://lists.cs.uiuc.edu/mailman/listinfo/vwnc

_______________________________________________
vwnc mailing list
[hidden email]
http://lists.cs.uiuc.edu/mailman/listinfo/vwnc
Reply | Threaded
Open this post in threaded view
|

Re: Using #deepCopy, I have trouble understanding why it the code fails.

Alan Knight-2
I note that there is no #deepCopy method in the base system. There used to be, a very long time ago, but it was removed because of the ambiguity of what it was actually expected to do in different cases. So if you're using it, it's presumably been added by you or by some package you're using.



[hidden email]
May 3, 2011 5:31 AM


The answer is simple.
Simple #copy prefers to do #shallowCopy, i.e. make a copy where all inst.vars are identical to the
original. A #deepCopy creates new objects on every level in the depth, until all nodes in the
original object graph have copied counterparts.
When you make a comparison, the default test is for identity. If you have your own implementation of
#=, then you will check equality of various inst.vars. But it is very likely that your object graph
contains instances from "system" classes, i.e. classes that are not from your domain. These classes
have their own understanding of being equal, in particular those that simply inherit #= from Object
with "^self == anObject", then the equality test *must* fail at a certain level.
This means that "x = x deepCopy" should return false *in general*, unless the object graph of x
contains only instances from your domain or from "simple" data types, such as Number, String, Date, etc.
Smalltalk is such a wonderful environment where you can learn how the equality test is done by
simple use of the Debugger. Just run "x = x deepCopy" with your favorite object x, and use "Debug
it" instead of "Do it". Follow the flow by stepping into each comparison and wait for your "aah" effect.

Cheers
Holger

_______________________________________________
vwnc mailing list
[hidden email]
http://lists.cs.uiuc.edu/mailman/listinfo/vwnc


[hidden email]
May 3, 2011 4:56 AM


Hello.

The code eventually executes #== (inherited from Object) to
compare two MyContact instances, because MyContact does not
define its own version of #=. You can of course implement
it to something like this:

MyContact>>= anObject

^self class == anObject class
and: [name = anObject name
and: [emailAddress = anObject emailAddress]].


And, as noted in Object>>=, you should define your own
version of #hash as well if you do so:

MyContact>>hash

^(self class identityHash bitXor: name hash) bitXor: emailAddress hash.


All objects for which #= evaluates to true must have the
same #hash value. The #hash is used for quick lookup in
Set and Dictionary collections.


HTH,

Ladislav Lenart



_______________________________________________
vwnc mailing list
[hidden email]
http://lists.cs.uiuc.edu/mailman/listinfo/vwnc

--
Alan Knight [|], Engineering Manager, Cincom Smalltalk
[hidden email]
[hidden email]
http://www.cincomsmalltalk.com

_______________________________________________
vwnc mailing list
[hidden email]
http://lists.cs.uiuc.edu/mailman/listinfo/vwnc