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 =
{
evaluate &m:anM &n:anN
[
#if (anM == 0)?
[ ^ anN + 1. ].
#if (anM > 0)?
[
#if (anN == 0)?
[ ^ self evaluate &m:(anM - 1) &n:1. ].
#if (anN > 0)?
[ ^ self evaluate &m:(anM - 1) &n:(self evaluate &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).
We may declare the new ones or reuse existing ones from std'dictionary'math module.
#subject std'dictionary'math'*.
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 evaluate &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:
#hint(signature:(ackermann, m, n))
#class AckermanValue
{
#hint(arg:m)
#field theM.
#hint(arg:n)
#field theN.
#method m'get = theM.
#method n'get = theN.
#method int'get
[
^ Ackermann evaluate:self.
]
}
Because a signature should be in the same module as the leading subject we introduce a new one - "ackermann":
#subject ackermann.
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
{
...
#hint(disp)
#method save : aWriter
[
aWriter << self int.
]
#method __textwriter'save : aWriter
[
aWriter << "A(" << theM << "," << theN << ")=" << self int.
]
}
Let's examine these methods. It is presumed that a generic "write" method calls back its parameter. So we have to implement a new method "save". The problem is that actually any writer could call this method. Therefore there should be some conditional statement to check if the parameter is a text writer (subject - __textwriter). It could be achieved with the help of multi-dispatching by marking a generic "save" method with "dispatchable" (or "disp") hint. Now if a parameter is a text writer, "textwriter'save" method will be called instead.
The expression output will be the following:
A(0,3)=4
0 comments:
Post a Comment