Friday, January 28, 2011

Rosseta code tutorials:Ackermann function

Let's implement Ackermann function - Rosettacode.

Because ELENA is a pure object-oriented language we have to create the object which will calculate the function. This object will only evaluates the parameter so we have to declare symbol with the single method "evaluate" (note that a new class can be declared both as implicit symbol - #class MyClass {} and as an explicit one -
#symbol MyClass = { ..}, the only difference that the symbol cannot have fields and be inherited).

#symbol Ackermann =
{
    a_function &m:anM &n:anN
    [
        #if anM
            ifequal:0 [ ^ anN + 1. ]
            | greater:0 ?
            [
                #if anN
                    ifequal:0 [ ^ self a_function &m:(anM - 1) &n:1. ]
                    | greater:0 ? 
                    [ 
                        ^ self a_function 
                             &m:(anM - 1) 
                             &n:(self a_function &m:anM &n:(anN - 1)). 
                    ].
            ].
        
        $self fail.
    ]
}.

The implementation is quite straight-forward but there are several things I would like to explain. Any ELENA method has only one parameter but it is still possible to pass several ones using the argument list (which is in fact the single object which returns the expected arguments). The argument names have to be declared beforehand
(see - Method calling syntax).

Let's declare the required subjects.

#subject a_function, m, n.

Secondly Ackermann function is defined for n and m bigger or equal to zero. So we have to decide what to do if one of the parameters is negative. ELENA does not support exceptions so we have to break the program flow. It is done by sending the special non-existing message - fail - to itself.

self fail.

Now we could calculate our function:

'program'Output << Ackermann a_function &n:1 &m:1.

Let's modify our code using the appropriate signature (note that << and write are synonyms):

'program'Output write &ackermann &m:0 &n:3.

To use this signature we have to declare a class with a "signature" hint:

#class(signature:(a_function, m, n)) AckermanValue
{
    #field(arg:m)theM.
    
    #field(arg:n)theN.
    
    #method m'get = Int64Value::theM.
    
    #method n'get = Int64Value::theN.
    
    #method save : aWriter = aWriter write &:(Ackermann a_function:self).
    
    #method literal_writer'read : aWriter
    [
        aWriter << "A(" << theM << "," << theN << ")=" << Int64Value::self.

        ^ aWriter.
    ]
}

Note that declaring AckermanValue class we do not specify its type (ELENA is actually a type-less language but we could assign a subject to it which can be used as its "type " by multi-dispatching mechanism), so 'program'output will not know how to print it. We have to add a new method to implement a custom print action.

#class AckermanValue
{
...   
    #method save : aWriter = aWriter write &:(Ackermann a_function:self).
    
    #method literal_writer'read : aWriter
    [
        aWriter << "A(" << theM << "," << theN << ")=" << Int64Value::self.

        ^ aWriter.
    ]
}

The expression output will be the following:

A(0,3)=4

No comments:

Post a Comment