An Object-Oriented Way of Prime Numbers
One way to make the program object-oriented is to turn the sieve function into a method that fills an array of prime numbers passed by reference. The CSieve class does this through the AllPrimes method. The code is very similar to the local sieve function. The problem with this kind of method is that you must calculate all the prime numbers before you can get the first one. But some clients can’t wait. Those quink scientists I mentioned earlier, for example, want a steady stream of numbers fed to them one at a time.
This is the classic trade-off you’ll come to know in COM design. The purpose of any server is to transfer data across boundaries. Often, setting up the transfer is the most expensive part. You can be more efficient by passing larger chunks of data. But the larger the chunk, the longer the client has to wait. In this exercise, you’ll be testing the extremes—all at once versus one at a time.
In real programs, you might want to compromise by transferring medium-sized chunks.
The trick in getting numbers one at a time is to break the sieve function—which seems like an integrated whole—into its parts. Here’s what sieve really does:
-
Keeps its state (number of primes, current number) in local variables
-
Initializes an array to False by redimensioning it
-
Loops through each potential prime number
-
Marks out all multiples of primes
-
Counts the primes as it finds them
You can break these tasks into the following members, methods, and properties:
-
The state is kept in private variables shared by all the methods and properties rather than in local variables. This includes the array of flags for each potential prime (af), the number currently being tested (iCur), the maximum prime (iMaxPrime), and the count of primes (cPrime).
-
A ReInitialize method resets all the internal variables and starts counting again from 0.
-
A MaxPrime property sets the upper bound on the search for primes. From a user’s viewpoint, it would be better not to have a maximum and instead to simply calculate whether each number is a prime without reference to later primes. But you’d have to turn to someone other than Eratosthenes for an algorithm. So let’s stick with the sieve. When you change MaxPrime, you break the algorithm. Therefore, the MaxPrime Property Let procedure must call ReInitialize to start counting from 0.
-
A NextPrime property calculates and returns the next prime.
-
A Primes property (read-only) tells you how many prime numbers have been counted so far.
Here’s the code to implement these methods and properties:
Private af() As Boolean, iCur As Integer
Private iMaxPrime As Integer, cPrime As Integer
Private Sub Class_Initialize()
‘ Default size is largest integer
iMaxPrime = 32766
ReInitialize
End Sub
Sub ReInitialize()
ReDim af(0 To iMaxPrime)
iCur = 1: cPrime = 0
End Sub
Property Get NextPrime() As Integer
‘ Loop until you find a prime or overflow array
iCur = iCur + 1
On Error GoTo OverMaxPrime
Do While af(iCur)
iCur = iCur + 1
Loop
‘ Cancel multiples of this prime
Dim i As Long
For i = iCur + iCur To iMaxPrime Step iCur
af(i) = True
Next
‘ Count and return it
cPrime = cPrime + 1
NextPrime = iCur
OverMaxPrime: ‘ Array overflow comes here
End Property
Property Get MaxPrime() As Integer
MaxPrime = iMaxPrime
End Property
Property Let MaxPrime(iMaxPrimeA As Integer)
iMaxPrime = iMaxPrimeA
ReInitialize
End Property
Property Get Primes() As Integer
Primes = cPrime
End Property
As you can see, the NextPrime property does most of the work. It looks quite different from the Sieve function, but it does essentially the same thing. You might need to study both the Sieve function and the NextPrime property for a minute to see the connection.
The code that calls the class methods looks a little different from the code that calls the function.
Case estBasicLocalClass
‘ Basic local class
Dim sieveLocal As New CSieve
sieveLocal.MaxPrime = txtMaxPrime.Text
If chkAll = vbUnchecked Then
‘ Get one at a time
ms = timeGetTime()
For i = 1 To cIter
sieveLocal.ReInitialize
Do
iPrime = sieveLocal.NextPrime
If fDisplay And iPrime Then
lstOutput.AddItem iPrime
lstOutput.TopIndex = lstOutput.ListCount - 1
lstOutput.Refresh
End If
Loop Until iPrime = 0
Next
txtTime.Text = timeGetTime() - ms
txtPrimes.Text = sieveLocal.Primes
Else
‘ Get all at once
§
The code calling the AllPrimes method to get all the primes at once looks a lot like the functional version, so there’s no need to show it here.