4

I am trying to make a new sorting method in smalltalk. Anyone know how to change this sorting java code to squeak?

public static void SelectionSort ( int [ ] num )
{
     int i, j, first, temp;  
     for ( i = num.length - 1; i > 0; i - - )  
     {
          first = 0;   //initialize to subscript of first element
          for(j = 1; j <= i; j ++)   //locate smallest element between positions 1 and i.
          {
               if( num[j] < num[first] )         
                 first = j;
          }
          temp = num[first];   //swap smallest found with element in position i.
          num[first] = num[ i ];
          num[i] = temp; 
      }           
}
programmingGirl
  • 107
  • 3
  • 11

2 Answers2

7

Short answer:

You don't have to.

To sort an array, just send it the message #asSortedCollection. For instance, inspect this in a workspace:

#(7 2 8 5) asSortedCollection

Long answer:

Since I assume you want to see how you would implement the equivalent of your Java code in Smalltalk if you really had to, here's a relatively "literal translation" you can test in a workspace (tested in Pharo, should work in Squeak as well):

| someNumbers |
someNumbers := #(7 2 8 5) copy. "See comments below for an explanation."
someNumbers size to: 1 by: -1 do: [:eachOuterIndex |
    | indexOfSmallest swapValue |
    indexOfSmallest := 1.
    1 to: eachOuterIndex do: [:eachInnerIndex |
        (someNumbers at: eachInnerIndex) < (someNumbers at: indexOfSmallest)
            ifTrue: [ indexOfSmallest := eachInnerIndex ]
        ].
    swapValue := someNumbers at: indexOfSmallest.
    someNumbers at: indexOfSmallest put: (someNumbers at: eachOuterIndex).
    someNumbers at: eachOuterIndex put: swapValue.
    ].
^someNumbers

Clearly, there are a few changes from your version, such as using explicit naming, which is one of Smalltalk's hallmark conventions (in particular, indexOfSmallest should be clearer than first, which is misleading since it's not necessarily the first index), and decreasing the scope of the variables you called first and temp). See @Leandro's answer for a version that uses your own variable names if you have trouble with the "translation".

If the code were to live in a method, I would probably put it in the SequenceableCollection hierarchy (or maybe you'd want to add yours as a subclass in there if you want to override other behaviour), and the start of it could look something like this:

copySortedDescending
    "Answer a copy of the receiver, sorted in descending order."

    | copy |
    copy := self copy.
    copy size to: 1 by: -1 do: [:eachOuterIndex |
        "... and so on..."
        ].
    ^copy

Again, note that I'm deliberately changing the name, because I don't think selectionSort is a descriptive name of what the method does, and I wouldn't use a collection as an argument to a method living somewhere else - the knowledge of how to do the sorting belongs on the collection itself.

I'm sure you could come up with a better roll-your-own-answer very easily, though. For instance, you could try sending a SequenceableCollection instance the message sort: and pass a sort block as an argument, in which you could specify how you want your elements to be sorted.

Amos M. Carpenter
  • 4,848
  • 4
  • 42
  • 72
  • Your code is mostly correct, but you should not store into a literal array (`#(...)`). Probably change the first line to `someNumbers := #(7 2 8 5) copy.` – Tobias Apr 30 '15 at 06:00
  • @Tobias: Like I said, it's meant to be a "literal translation" of the OP's code. And it works as-is in Pharo... why not change values in it? I'm not iterating over the array, I'm iterating over indices. – Amos M. Carpenter Apr 30 '15 at 06:10
  • 2
    Yes, that is correct. But suppose you are writing this code in a method. you'd expect that every time you call it, that the first line is the same, but this isn't true after the first invocation. This is a bit complex so I made a community wiki entry: https://stackoverflow.com/questions/29964345/why-shouldnt-i-store-into-literal-arrays-in-smalltalk/29964346#29964346 – Tobias Apr 30 '15 at 09:47
  • @Tobias: I appreciate the effort you went to in order to make your point, which is quite a tricky case - I upvoted both your question and your answer there. If this code _were_ to live in a method instead of just being a workspace example, obviously a literal array wouldn't be used in the first place - the array would be the argument, and the method would most likely "answer a sorted copy of the receiver". Happy to update the answer to include the `copy` though, thanks for pointing this out. – Amos M. Carpenter Apr 30 '15 at 11:50
6

Here is a line by line translation. Row numbers are not part of the code.

 1. selectionSort: num
 2.    | first temp |
 3.    num size to: 1 by: -1 do: [:i |
 4.        first := 1. "initialize to subscript of first element"
 5.        1 to: i do: [:j |
 6.            "locate smallest element between positions 1 and i"
 7.            (num at: j) < (num at: first) ifTrue: [first := j]].
 8.        temp := num at: first. "swap smallest with element in position i"
 9.        num at: first put: (num at: i).
10.        num at: i put: temp]

Remarks:

  1. No argument type declaration. No answer type.
  2. Block temporaries i and j declared inside blocks (lines 3 and 5). In Smalltalk, indexed collections are 1 based.
  3. num.length() -> num size. Decreasing for loop translates into to:by:do: message.
  4. Assignment = becomes := and 0 becomes 1 (see line remark 2 above.)
  5. Increasing for loop translates into to:do: message.
  6. Comments are enclosed between double quotes.
  7. [j] translates into the at: j message. if translates into an ifTrue: message.
  8. temp could have been declared in the first block: do: [:i | | temp |....
  9. num[j] = temp also becomes a message sending at:put:.
  10. Idem 9. Note also that you could have used the cascade syntax for lines 9 and 10:

    num
        at: first put: (num at: i);
        at: i put: temp
    
  11. No need to answer num because it's been modified by the method. See, however, the interesting discussion originated in Amos' answer: Why shouldn't I store into literal arrays in Smalltalk?.

Community
  • 1
  • 1
Leandro Caniglia
  • 14,495
  • 4
  • 29
  • 51