Monday, February 28, 2011

Rosetta Code: Arrays

Today, let's discuss the work with array (In this post I combined several tasks).
First, lets' show the basic array syntax
There are several ways how to declare the array in ELENA. The simplest way looks like this
#var anArray := (1, 2, 3).
Note that the array is an object containing the references to the integer objects rather than values itself. By default the array object inherits std'basic'Array class.
The syntax to retrieve the element is quite simple:
#var anItem := anArray@1.
Note that @ message returns the indexer proxy rather than the element itself. If you wish to retrieve the object itself the code should be a bit more complex:
#var anItem := (anArray@1) content.
To find out the difference between these variants let's examine the concept of an indexer. The indexer is a special adapter allowing to access the array members. To create indexer it is enough to send indexer message
#var anIndexer := anArray indexer.
The indexer can retrieve or assign the current array element with content'get / content'set methods (note that set method works only for dynamic arrays) and navigate the array (it is similar to the C array pointer). So to retrieve the element we have to create an array, move to the required position and return the content:
anArray indexer write &index:1.
But it is possible to use more simple way with the help of "refer" message (@ operand).
anArray @ 1.
Let's consider the following sample:
ctrl'It::anArray run : anItem =>
[
   'program'Output << anItem << "%n".
]
In this code we print every member of the array. Note that the object is extended with its index.
ctrl'It::anArray run : anItem =>
[
'program'Output << "a[" << anItem index << "]=" << anItem << "%n".
]
We could use an enumerator as well. In this case the object rather then the indexer is used:
ctrl'Scan::anArray run : anItem =>
[
   program'Output << anItem << "%n".
]
If we would like to get the original object from the proxy (for example if it should be assigned to the class field, in this case it is not optimal to store the proxy) we should use content'get message:
anArray@1 content.
Now let's see how could we create a dynamic array:
#var anArray := basic'NewArray::3.
In most cases there is no difference between the constant and the dynamic one except assigning the value:
anArray@0 set &content:2.
I hope it is clear now why we have to do it this way. After a dynamic array is created all its members are nil. Though it is possible to create and fill the array at the same time
anArray := factory'NewArray::{ &factory'array_size:3 &factory'pattern:(Integer::0) }.
And finally let's consider the code to calculate the mean(arithmetic average) of a numeric vector.
#define std'basic'*.
#define std'patterns'*.
#define std'dictionary'*.

// --- Sum ---

#class MeanAction
{
    #field theValue.
    #field theCount.
    
    #role Empty
    {
        #method numeric'get = 0.
        
        #method evaluate : aValue
        [
            theValue := Real::0.
            theCount := Integer::0.
            
            #shift.
            
            self evaluate:aValue.
        ]
    }
    
    #method new
    [
        #shift Empty.
    ]
    
    #method numeric'get = theValue / theCount.
    
    #method evaluate : aValue
    [
        theCount += 1.
        
        theValue += aValue.
    ]
    
    #method start : aPattern
    [
        aPattern run:self.
        
        ^ self numeric.
    ]
}

// --- Program ---

#symbol Program =>
[
    'program'Output << MeanAction start:Scan::(1, 2, 3, 4, 5, 6, 7, 8).
].
In short we execute the special action object for every member of the collection (or an array) and then print the result. The program code is quite simple (once again we use an enumeration, but this type with implicit action object instead of anonymous one), so let's look more precisely at MeanAction. Enumeration code pattern executes the action for every member of the collection by sending "evaluate" message (=> operator) with the collection member. So we have to declare this method and calculate the total sum simultaneously counting the numbers. "numeric'get" method returns the array mean. In the object constructor we initialize the action fields. Till this moment all is quite straightforward. But there is a special case - zero vector (an array with zero members). If we try to return the mean for it the program will crash with divide by zero exception. So we are creating a special set of methods (Empty role) to deal with it and switch the object to it (#shift Role). If evaluate method is called at least once than the vector is not zero and we have to use the standard set of methods (statement #shift).

No comments:

Post a Comment