#1247 `AssociativeCollection >> =` is incorrect

Closed
opened 4 years ago by herby · 4 comments

These codes produce false.

| d1 d2 |
d1 := Dictionary from: {'a'->1. 'b'->2}.
d2 := Dictionary from: {'b'->2. 'a'->1}.
d1 = d2
| d1 d2 |
d1 := HashedCollection from: {'a'->1. 'b'->2}.
d2 := HashedCollection from: {'b'->2. 'a'->1}.
d1 = d2

It is because = is order-dependent: implemented as self associations = anAssociativeCollection associations.

These codes produce false. ```smalltalk | d1 d2 | d1 := Dictionary from: {'a'->1. 'b'->2}. d2 := Dictionary from: {'b'->2. 'a'->1}. d1 = d2 ``` ```smalltalk | d1 d2 | d1 := HashedCollection from: {'a'->1. 'b'->2}. d2 := HashedCollection from: {'b'->2. 'a'->1}. d1 = d2 ``` It is because `=` is order-dependent: implemented as `self associations = anAssociativeCollection associations`.

Two tests:

Dictionary >> testEqual
    self assert: (Dictionary with: 'a' -> 1 with: 'b' -> 2) equals: (Dictionary with: 'b' -> 2 with: 'a' -> 1)

and

HashedCollectionTest >> testEqual
    self assert: #{'a' -> 1. 'b' -> 2} equals: #{'b' -> 2. 'a' -> 1}

and a method:

AssociativeCollection >> =
    ^ self class = anAssocitativeCollection class and: [
    self size = anAssocitativeCollection size and: [
    self keys asSet = anAssocitativeCollection keys asSet and: [
    self keys allSatisfy: [:key | (self at: key) = (anAssocitativeCollection at: key)]]]]

What do you think?

Two tests: Dictionary >> testEqual self assert: (Dictionary with: 'a' -> 1 with: 'b' -> 2) equals: (Dictionary with: 'b' -> 2 with: 'a' -> 1) and HashedCollectionTest >> testEqual self assert: #{'a' -> 1. 'b' -> 2} equals: #{'b' -> 2. 'a' -> 1} and a method: AssociativeCollection >> = ^ self class = anAssocitativeCollection class and: [ self size = anAssocitativeCollection size and: [ self keys asSet = anAssocitativeCollection keys asSet and: [ self keys allSatisfy: [:key | (self at: key) = (anAssocitativeCollection at: key)]]]] What do you think?
Herby Vojčík commented 4 years ago
Owner

It seems I actually do test it:

https://lolg.it/amber/amber/src/master/lang/src/Kernel-Tests.st#L1005

I am very puzzled about why all Amber tests pass.

When I tried to recreate it in workspace

| c n a |
c := #{ 'b' -> 1. 'a' -> 2. 'c' -> 3. 'd' -> -4 }.
n := #{ 'new' -> 'N' }.
a := #{ 'b' -> 1. 'a' -> 2. 'c' -> 3. 'd' -> -4. 'new' -> 'N' }.

TestCase new assert: n, c equals: a

it failed. But when I run tests in Amber, it's all green. 😢

Issue on test runner: #1248.

It seems I actually do test it: https://lolg.it/amber/amber/src/master/lang/src/Kernel-Tests.st#L1005 I am very puzzled about why all Amber tests pass. When I tried to recreate it in workspace ``` | c n a | c := #{ 'b' -> 1. 'a' -> 2. 'c' -> 3. 'd' -> -4 }. n := #{ 'new' -> 'N' }. a := #{ 'b' -> 1. 'a' -> 2. 'c' -> 3. 'd' -> -4. 'new' -> 'N' }. TestCase new assert: n, c equals: a ``` it failed. But when I run tests in Amber, it's all green. 😢 Issue on test runner: #1248.
Herby Vojčík commented 4 years ago
Owner

I added fixed comma tests in master.

asSet can be expensive operation, even at:. Thinking how to shortcut that...

I added fixed comma tests in master. `asSet` can be expensive operation, even `at:`. Thinking how to shortcut that...
Herby Vojčík commented 4 years ago
Owner

As and: is also a little bit expensive (not inlined yet), as well as non-local return (try-catch handler), sticking to ifFalse:. Added this:

= anAssociativeCollection
    | comparisons |
    self class = anAssociativeCollection class ifFalse: [ ^ false ].
    self size = anAssociativeCollection size ifFalse: [ ^ false ].
    comparisons := OrderedCollection new.
    (self associations allSatisfy: [ :each |
        anAssociativeCollection at: each key
            ifPresent: [ :otherValue | comparisons add: { each value. otherValue }. true ]
            ifAbsent: [ false ] ]) ifFalse: [ ^ false ].
    ^ comparisons allSatisfy: [ :each | each first = each second ]
As `and:` is also a little bit expensive (not inlined yet), as well as non-local return (try-catch handler), sticking to `ifFalse:`. Added this: ```smalltalk = anAssociativeCollection | comparisons | self class = anAssociativeCollection class ifFalse: [ ^ false ]. self size = anAssociativeCollection size ifFalse: [ ^ false ]. comparisons := OrderedCollection new. (self associations allSatisfy: [ :each | anAssociativeCollection at: each key ifPresent: [ :otherValue | comparisons add: { each value. otherValue }. true ] ifAbsent: [ false ] ]) ifFalse: [ ^ false ]. ^ comparisons allSatisfy: [ :each | each first = each second ] ```
Sign in to join this conversation.
No Label
No Milestone
No assignee
2 Participants
Loading...
Cancel
Save
There is no content yet.