0

Is there any way to check if 2 linked lists have the same elements regardless of order.

edit question: I have fixed the code and given some more details:

this is the method that compares 2 lists

 compare: object2
     ^ ((mylist asBag) = ((objetc2 getList) asBag)).

the method belongs to the class myClass that has a field : myLList. myList is a linkedList of type element.

I have compiled it in the workspace:

  a: = element new id:1.
  b:= element new id:2.
  c:=element new id:3.

  d: = element new id:1.
  e:= element new id:2.
  f:=element new id:3.

  elements1 := myClass new. 
  elements addFirst:a.
   elements addFirst:b.
   elements addFirst:c.

   elements2 := myClass new. 
   elements addFirst:d.
   elements addFirst:e.
   elements addFirst:f. 
   Transcript show: (elements1 compare:elements2).

so I am getting false.. seems like it checks for equality by reference rather than equality by value..

So I think the correct question to ask would be: how can I compare 2 Bags by value? I have tried the '=='..but it also returned false.

Ohad
  • 1,563
  • 2
  • 20
  • 44
  • 1
    Are there duplicate elements in the lists? – Tobias Nov 13 '14 at 15:06
  • 1
    (Style comment: you could replace the last 3 lines by `^ bag1 = bag2`) – Tobias Nov 13 '14 at 15:07
  • there could be...that's why I am using Bag and not Set – Ohad Nov 13 '14 at 15:10
  • As for question #3. This is where Smalltalk shines. The implementation of #asBag is available in the same living world as your own code. The best way to learn is to view the implementation directly (perhaps by "browsing implementors" if you aren't sure where it's defined") and answer your own question!! – Sean DeNigris Nov 13 '14 at 15:44
  • ya I have looked at it.. but then it copies it by reference...right ? now the origin linked list and the new bag will point to the same location in memory.. – Ohad Nov 13 '14 at 15:46
  • Did you compile this code in an image, or hand-write it in SO? Since bag1 and bag2 are not temps, if you compiled it, you would've chosen to make them instance variables when the undeclared variable dialog appeared. Is that correct? I'm assuming this would have been in error, but of course one would need to see more code... – Sean DeNigris Nov 13 '14 at 15:51

2 Answers2

4

EDIT: The question changed too much - I think it deserves a new question for itself.

The whole problem here is that (element new id: 1) = (element new id: 1) is giving you false. Unless it's particular class (or superclasses) redefine it, the = message is resolved comparing by identity (==) by default. That's why your code only works with a collection being compared with itself.

Test it with, for example, lists of numbers (which have the = method redefined to reflect what humans understand by numeric equality), and it will work.

You should redefine your element's class' = (and hashCode) methods for this to work.

Smalltalk handles everything by reference: all there exist is an object, which know (reference) other objects.


It would be wrong to say that two lists are equivalent if they are in different order, as the order is part of what a list means. A list without an order is what we call a bag.

The asBag message (as all of the other as<anotherCollectionType> messages) return a new collection of the named type with all the elements of the receiver. So, #(1 2 3 2) is an Array of four elements, and #(1 2 3 2) asBag is a bag containing those four elements. As it's a Bag, it doesn't have any particular order.

When you do bagA := Bag new. you are creating a new Bag instance, and reference it with bagA variable. But then you do bagA := myList asBag, so you lose the reference to the previous bag - the first assignment doesn't do anything useful in your code, as you don't use that bag.

Saying aBool ifTrue: [^true] ifFalse: [^false] has exactly the same meaning as saying ^aBool - so we prefer just to say that. And, as you only create those two new bags to compare them, you could simplify your whole method like this:

compareTo: anotherList
  ^ myList asBag = anotherList asBag

Read it out loud: this object (whatever it is) compares to another list if it's list without considering order is the same than the other list without order.

The name compareTo: is kind of weird for returning a boolean (containsSameElements: would be more descriptive), but you get the point much faster with this code.


Just to be precise about your questions:

1) It doesn't work because you're comparing bag1 and bag2, but just defined bagA and bagB.

2) It's not efficient to create those two extra bags just because, and to send the senseless ifTrue: message, but other way it's OK. You may implement a better way to compare the lists, but it's way better to rely on the implementation of asBag and the Bag's = message being performant.

3) I think you could see the asBag source code, but, yes, you can assume it to be something like:

Collection>>asBag
  |instance|
  instance := Bag new.
  instance addAll: self.
  ^instance

And, of course, the addAll: method could be:

Collection>>addAll: anotherCollection
  anotherCollection do: [ :element | self add: element ]

So, yes - it creates a new Bag with all the receiver's elements.

Stephan Eggermont
  • 15,847
  • 1
  • 38
  • 65
mgarciaisaia
  • 14,521
  • 8
  • 57
  • 81
  • thank you very much.. your answer was very detailed and clear...It does return me a Boolean value, but somehow although my 2 lists have the same elements it returns false.. any idea why? and also ..any idea what is the complexity of the method 'compareTo' I have created? – Ohad Nov 13 '14 at 15:29
  • and just to be sure... when I use asBag..does it copy the elements by value or reference ? – Ohad Nov 13 '14 at 15:31
  • the strange thing is that when my receiver is the same as my argument (means I compare the elements of a linked list with itself) I get true.. but when I send the the method another list with the same elements (same by value only!) I get false..why?! – Ohad Nov 13 '14 at 15:38
  • I've updated my answer to with these new concerns. I really think you should follow Sean's advice and learn Smalltalk's basics - and I recommend you to read the [Pharo By Example](http://www.pharobyexample.org/) book as a start. I would invite you to our POO lessons at university, but I don't think you're near Argentina :) – mgarciaisaia Nov 13 '14 at 17:19
4

mgarciaisaia's answer was good... maybe too good! This may sound harsh, but I want you to succeed if you're serious about learning, so I reiterate my suggestion from another question that you pick up a good Smalltalk fundamentals textbook immediately. Depending on indulgent do-gooders to rework your nonsensical snippets into workable code is a very inefficient way to learn to program ;)

EDIT: The question has changed dramatically. The following spoke to the original three-part question, so I paraphrased the original questions inline.

  1. Q: What is the problem? A: The problem is lack of fundamental Smalltalk understanding.
  2. Q: Is converting to bags an efficient way to make the comparison? A: Although it's probably not efficient, don't worry about that now. In general, and especially at the beginning when you don't have a good intuition about it, avoid premature optimization - "make it work", and then only "make it fast" if justified by real-world profiling.
  3. Q: How does #asBag work? A: The implementation of #asBag is available in the same living world as your own code. The best way to learn is to view the implementation directly (perhaps by "browsing implementors" if you aren't sure where it's defined") and answer your own question!! If you can't understand that implementation, see #1.
Sean DeNigris
  • 6,306
  • 1
  • 31
  • 37
  • please see my edit question... I don't understand what it means compiling it in an image.. i have run it in the workspace of squeak virtual machine.. – Ohad Nov 13 '14 at 16:09