[Cuis-dev] feedback on simple code
Andres Valloud
ten at smallinteger.com
Wed Jun 5 10:19:09 PDT 2024
Hello, couple things...
Collatz class>>gather: aNumber seq: aSeq
aNumber = 1
ifTrue: [aSeq addLast: 1]
ifFalse: [
| next |
next := Collatz next: aNumber.
aSeq addLast: next.
next = 1 ifFalse: [Collatz gather: next seq: aSeq]
]
Always be on the lookout for when a receiver is just self. Otherwise,
the code is not using the knowledge it has of where it is implemented
(and this leads to all sorts of problems later). So the above really means
Collatz class>>gather: aNumber seq: aSeq
aNumber = 1
ifTrue: [aSeq addLast: 1]
ifFalse: [
| next |
next := Collatz next: aNumber.
aSeq addLast: next.
next = 1 ifFalse: [self gather: next seq: aSeq]
]
Also, you can accomplish the same with much less code. Any time you
have a class whose class methods offer a "service" but do not really
have instances, you should suspect the code should be placed elsewhere.
Side comment: I've been working on the Collatz problem for a *long*
time, so I have quite a few opinions on how to go about writing
specifically this code...
But in this particular case, what you're collecting is the orbit of an
integer (so, anInteger, not aNumber). Here, "orbit" is a mathematical
term from dynamical systems and it refers to the sequence n, f(n),
f(f(n)), etc. Collatz orbits are very interesting by themselves, and
you can ask them lots of very interesting questions. So, rather than an
ordered collection, I would create a new class called CollatzOrbit to
hold the orbit. And I would not make it a subclass of Object, not
OrderedCollection, because the math properties of such orbits do not
really belong in the Collection hierarchy.
Then, the orbit class would have an instance variable, e.g. "orbit",
initialized when the object is created, like this:
CollatzCkOrbit class>>startingAt: anInteger
^self new
startingAt: anInteger;
yourself
CollatzCkOrbit>>startingAt: anInteger
| newOrbit |
newOrbit := self newOrbitStartingAt: anInteger.
self orbit: newOrbit
In addition to all this, strive to accomplish the same goal with the
least possible code (within reason, this is not an obfuscated C
contest). So,
CollatzCkOrbit>>newOrbitStartingAt: anInteger
| answer |
anInteger < 1 ifTrue: [self error: 'Very interesting, but for
simplicity not here please'].
answer := OrderedCollection with: anInteger.
[answer last = 1] whileFalse:
[answer add: answer last ck].
^answer
Integer>>ck
^self even ifTrue: [self // 2] ifFalse: [self * 3 + 1]
Integer>>ckOrbit
^CollatzCkOrbit startingAt: anInteger
That name ck refers to C(k) in the literature. But if you will look at
this further, then use T(k) instead because it has way more interesting
properties.
Integer>>tk
^self even
ifTrue: [self // 2]
ifFalse: [self * 3 + 1 // 2]
Integer>>tkOrbit
^CollatzTkOrbit startingAt: anInteger
And the class hierarchy should look like this (for mathematical reasons):
AbstractCollatzOrbit
CollatzCkOrbit
CollatzTkOrbit
so newOrbitStartingAt: should be in the Abstract superclass etc to
prevent code duplication.
I just wrote all this code in the email, I didn't test it. Finally,
note the effort to have code that references class names the least
possible times.
On 6/5/24 9:56 AM, Mark Volkmann via Cuis-dev wrote:
> I've been learning Smalltalk for the past week or so. I wrote some code
> to compute the Collatz sequence of a given number described here:
> https://en.wikipedia.org/wiki/Collatz_conjecture
> <https://en.wikipedia.org/wiki/Collatz_conjecture>
>
> I'd love it if an experienced Smalltalker could look at my
> implementation and tell me what they would do differently. Here's a
> fileOut of my code. It's not very long.
>
> --
> R. Mark Volkmann
> Object Computing, Inc.
>
More information about the Cuis-dev
mailing list